Completed
Branch master (16f63d)
by
unknown
05:48
created
core/domain/entities/routing/handlers/admin/EspressoLegacyAdmin.php 1 patch
Indentation   +144 added lines, -144 removed lines patch added patch discarded remove patch
@@ -18,154 +18,154 @@
 block discarded – undo
18 18
  */
19 19
 class EspressoLegacyAdmin extends AdminRoute
20 20
 {
21
-    /**
22
-     * @var LegacyAccountingAssetManager $asset_manager
23
-     */
24
-    protected $asset_manager;
21
+	/**
22
+	 * @var LegacyAccountingAssetManager $asset_manager
23
+	 */
24
+	protected $asset_manager;
25 25
 
26 26
 
27
-    /**
28
-     * returns true if the current request matches this route
29
-     *
30
-     * @return bool
31
-     * @since   5.0.0.p
32
-     */
33
-    public function matchesCurrentRequest(): bool
34
-    {
35
-        global $pagenow;
36
-        $page = $this->request->getRequestParam('page');
37
-        return ($pagenow === 'admin.php' || $pagenow === 'admin-ajax.php')
38
-               && (
39
-                   $page === 'pricing'
40
-                   || strpos($page, 'espresso') !== false
41
-                   || apply_filters(
42
-                       'FHEE__EspressoLegacyAdmin__matchesCurrentRequest__page',
43
-                       false,
44
-                       $page
45
-                   )
46
-               )
47
-               && parent::matchesCurrentRequest();
48
-    }
27
+	/**
28
+	 * returns true if the current request matches this route
29
+	 *
30
+	 * @return bool
31
+	 * @since   5.0.0.p
32
+	 */
33
+	public function matchesCurrentRequest(): bool
34
+	{
35
+		global $pagenow;
36
+		$page = $this->request->getRequestParam('page');
37
+		return ($pagenow === 'admin.php' || $pagenow === 'admin-ajax.php')
38
+			   && (
39
+				   $page === 'pricing'
40
+				   || strpos($page, 'espresso') !== false
41
+				   || apply_filters(
42
+					   'FHEE__EspressoLegacyAdmin__matchesCurrentRequest__page',
43
+					   false,
44
+					   $page
45
+				   )
46
+			   )
47
+			   && parent::matchesCurrentRequest();
48
+	}
49 49
 
50 50
 
51
-    /**
52
-     * @since 5.0.0.p
53
-     */
54
-    protected function registerDependencies()
55
-    {
56
-        $asset_manger_dependencies = [
57
-            'EventEspresso\core\domain\Domain'                   => EE_Dependency_Map::load_from_cache,
58
-            'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_new_object,
59
-            'EventEspresso\core\services\assets\Registry'        => EE_Dependency_Map::load_from_cache,
60
-        ];
61
-        $this->dependency_map->registerDependencies(JqueryAssetManager::class, $asset_manger_dependencies);
62
-        $this->dependency_map->registerDependencies(EspressoLegacyAdminAssetManager::class, $asset_manger_dependencies);
63
-        $this->dependency_map->registerDependencies(
64
-            LegacyAccountingAssetManager::class,
65
-            ['EE_Currency_Config' => EE_Dependency_Map::load_from_cache] + $asset_manger_dependencies
66
-        );
67
-        $this->dependency_map->registerDependencies(
68
-            'EE_Admin_Transactions_List_Table',
69
-            [
70
-                null,
71
-                'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
72
-            ]
73
-        );
74
-        $this->dependency_map->registerDependencies(
75
-            'EventEspresso\caffeinated\modules\recaptcha_invisible\RecaptchaAdminSettings',
76
-            ['EE_Registration_Config' => EE_Dependency_Map::load_from_cache]
77
-        );
78
-        $this->dependency_map->registerDependencies(
79
-            'EventEspresso\admin_pages\general_settings\OrganizationSettings',
80
-            [
81
-                'EE_Registry'                                                  => EE_Dependency_Map::load_from_cache,
82
-                'EE_Organization_Config'                                       => EE_Dependency_Map::load_from_cache,
83
-                'EE_Core_Config'                                               => EE_Dependency_Map::load_from_cache,
84
-                'EE_Network_Core_Config'                                       => EE_Dependency_Map::load_from_cache,
85
-                'EventEspresso\core\services\address\CountrySubRegionDao'      => EE_Dependency_Map::load_from_cache,
86
-                'EventEspresso\core\domain\services\capabilities\FeatureFlags' => EE_Dependency_Map::load_from_cache,
87
-            ]
88
-        );
89
-        $this->dependency_map->registerDependencies(
90
-            'EventEspresso\core\services\address\CountrySubRegionDao',
91
-            [
92
-                'EEM_State'                                            => EE_Dependency_Map::load_from_cache,
93
-                'EventEspresso\core\services\validators\JsonValidator' => EE_Dependency_Map::load_from_cache,
94
-            ]
95
-        );
96
-        $this->dependency_map->registerDependencies(
97
-            'EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder',
98
-            [
99
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
100
-                'EEM_Registration'                            => EE_Dependency_Map::load_from_cache,
101
-                null,
102
-            ]
103
-        );
104
-        $this->dependency_map->registerDependencies(
105
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\AttendeeFilterHeader',
106
-            [
107
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
108
-                'EEM_Attendee'                                => EE_Dependency_Map::load_from_cache,
109
-            ]
110
-        );
111
-        $this->dependency_map->registerDependencies(
112
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\DateFilterHeader',
113
-            [
114
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
115
-                'EEM_Datetime'                                => EE_Dependency_Map::load_from_cache,
116
-            ]
117
-        );
118
-        $this->dependency_map->registerDependencies(
119
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\EventFilterHeader',
120
-            [
121
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
122
-                'EEM_Event'                                   => EE_Dependency_Map::load_from_cache,
123
-            ]
124
-        );
125
-        $this->dependency_map->registerDependencies(
126
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\TicketFilterHeader',
127
-            [
128
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
129
-                'EEM_Ticket'                                  => EE_Dependency_Map::load_from_cache,
130
-            ]
131
-        );
132
-        $this->dependency_map->registerDependencies(
133
-            'EventEspresso\core\services\admin\AdminListTableFilters',
134
-            [
135
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
136
-            ]
137
-        );
138
-        $this->dependency_map->registerDependencies(
139
-            ServiceChangesManager::class,
140
-            [
141
-                'EE_Dependency_Map'                                   => EE_Dependency_Map::load_from_cache,
142
-                'EventEspresso\core\services\loaders\LoaderInterface' => EE_Dependency_Map::load_from_cache,
143
-            ]
144
-        );
145
-    }
51
+	/**
52
+	 * @since 5.0.0.p
53
+	 */
54
+	protected function registerDependencies()
55
+	{
56
+		$asset_manger_dependencies = [
57
+			'EventEspresso\core\domain\Domain'                   => EE_Dependency_Map::load_from_cache,
58
+			'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_new_object,
59
+			'EventEspresso\core\services\assets\Registry'        => EE_Dependency_Map::load_from_cache,
60
+		];
61
+		$this->dependency_map->registerDependencies(JqueryAssetManager::class, $asset_manger_dependencies);
62
+		$this->dependency_map->registerDependencies(EspressoLegacyAdminAssetManager::class, $asset_manger_dependencies);
63
+		$this->dependency_map->registerDependencies(
64
+			LegacyAccountingAssetManager::class,
65
+			['EE_Currency_Config' => EE_Dependency_Map::load_from_cache] + $asset_manger_dependencies
66
+		);
67
+		$this->dependency_map->registerDependencies(
68
+			'EE_Admin_Transactions_List_Table',
69
+			[
70
+				null,
71
+				'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
72
+			]
73
+		);
74
+		$this->dependency_map->registerDependencies(
75
+			'EventEspresso\caffeinated\modules\recaptcha_invisible\RecaptchaAdminSettings',
76
+			['EE_Registration_Config' => EE_Dependency_Map::load_from_cache]
77
+		);
78
+		$this->dependency_map->registerDependencies(
79
+			'EventEspresso\admin_pages\general_settings\OrganizationSettings',
80
+			[
81
+				'EE_Registry'                                                  => EE_Dependency_Map::load_from_cache,
82
+				'EE_Organization_Config'                                       => EE_Dependency_Map::load_from_cache,
83
+				'EE_Core_Config'                                               => EE_Dependency_Map::load_from_cache,
84
+				'EE_Network_Core_Config'                                       => EE_Dependency_Map::load_from_cache,
85
+				'EventEspresso\core\services\address\CountrySubRegionDao'      => EE_Dependency_Map::load_from_cache,
86
+				'EventEspresso\core\domain\services\capabilities\FeatureFlags' => EE_Dependency_Map::load_from_cache,
87
+			]
88
+		);
89
+		$this->dependency_map->registerDependencies(
90
+			'EventEspresso\core\services\address\CountrySubRegionDao',
91
+			[
92
+				'EEM_State'                                            => EE_Dependency_Map::load_from_cache,
93
+				'EventEspresso\core\services\validators\JsonValidator' => EE_Dependency_Map::load_from_cache,
94
+			]
95
+		);
96
+		$this->dependency_map->registerDependencies(
97
+			'EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder',
98
+			[
99
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
100
+				'EEM_Registration'                            => EE_Dependency_Map::load_from_cache,
101
+				null,
102
+			]
103
+		);
104
+		$this->dependency_map->registerDependencies(
105
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\AttendeeFilterHeader',
106
+			[
107
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
108
+				'EEM_Attendee'                                => EE_Dependency_Map::load_from_cache,
109
+			]
110
+		);
111
+		$this->dependency_map->registerDependencies(
112
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\DateFilterHeader',
113
+			[
114
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
115
+				'EEM_Datetime'                                => EE_Dependency_Map::load_from_cache,
116
+			]
117
+		);
118
+		$this->dependency_map->registerDependencies(
119
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\EventFilterHeader',
120
+			[
121
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
122
+				'EEM_Event'                                   => EE_Dependency_Map::load_from_cache,
123
+			]
124
+		);
125
+		$this->dependency_map->registerDependencies(
126
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\TicketFilterHeader',
127
+			[
128
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
129
+				'EEM_Ticket'                                  => EE_Dependency_Map::load_from_cache,
130
+			]
131
+		);
132
+		$this->dependency_map->registerDependencies(
133
+			'EventEspresso\core\services\admin\AdminListTableFilters',
134
+			[
135
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
136
+			]
137
+		);
138
+		$this->dependency_map->registerDependencies(
139
+			ServiceChangesManager::class,
140
+			[
141
+				'EE_Dependency_Map'                                   => EE_Dependency_Map::load_from_cache,
142
+				'EventEspresso\core\services\loaders\LoaderInterface' => EE_Dependency_Map::load_from_cache,
143
+			]
144
+		);
145
+	}
146 146
 
147 147
 
148
-    /**
149
-     * implements logic required to run during request
150
-     *
151
-     * @return bool
152
-     * @since   5.0.0.p
153
-     */
154
-    protected function requestHandler(): bool
155
-    {
156
-        add_filter(
157
-            'admin_body_class',
158
-            function ($classes) {
159
-                if (strpos($classes, 'espresso-admin') === false) {
160
-                    $classes .= ' espresso-admin';
161
-                }
162
-                return $classes;
163
-            }
164
-        );
165
-        $this->loader->getShared(JqueryAssetManager::class);
166
-        $this->loader->getShared(EspressoLegacyAdminAssetManager::class);
167
-        $this->loader->getShared(LegacyAccountingAssetManager::class);
168
-        $this->loader->getShared(ServiceChangesManager::class);
169
-        return true;
170
-    }
148
+	/**
149
+	 * implements logic required to run during request
150
+	 *
151
+	 * @return bool
152
+	 * @since   5.0.0.p
153
+	 */
154
+	protected function requestHandler(): bool
155
+	{
156
+		add_filter(
157
+			'admin_body_class',
158
+			function ($classes) {
159
+				if (strpos($classes, 'espresso-admin') === false) {
160
+					$classes .= ' espresso-admin';
161
+				}
162
+				return $classes;
163
+			}
164
+		);
165
+		$this->loader->getShared(JqueryAssetManager::class);
166
+		$this->loader->getShared(EspressoLegacyAdminAssetManager::class);
167
+		$this->loader->getShared(LegacyAccountingAssetManager::class);
168
+		$this->loader->getShared(ServiceChangesManager::class);
169
+		return true;
170
+	}
171 171
 }
Please login to merge, or discard this patch.
core/domain/entities/config/legacy/EE_Ticket_Selector_Config.php 1 patch
Indentation   +210 added lines, -210 removed lines patch added patch discarded remove patch
@@ -7,214 +7,214 @@
 block discarded – undo
7 7
  */
8 8
 class EE_Ticket_Selector_Config extends EE_Config_Base
9 9
 {
10
-    /**
11
-     * constant to indicate that a datetime selector should NEVER be shown for ticket selectors
12
-     */
13
-    const DO_NOT_SHOW_DATETIME_SELECTOR = 'no_datetime_selector';
14
-
15
-    /**
16
-     * constant to indicate that a datetime selector should only be shown for ticket selectors
17
-     * when the number of datetimes for the event matches the value set for $datetime_selector_threshold
18
-     */
19
-    const MAYBE_SHOW_DATETIME_SELECTOR = 'maybe_datetime_selector';
20
-
21
-    /**
22
-     * @var bool $show_ticket_sale_columns
23
-     */
24
-    public $show_ticket_sale_columns = true;
25
-
26
-    /**
27
-     * @var bool $show_ticket_details
28
-     */
29
-    public $show_ticket_details = true;
30
-
31
-    /**
32
-     * @var bool $show_expired_tickets
33
-     */
34
-    public $show_expired_tickets = true;
35
-
36
-    /**
37
-     * whether to display a dropdown box populated with event datetimes
38
-     * that toggles which tickets are displayed for a ticket selector.
39
-     * uses one of the *_DATETIME_SELECTOR constants defined above
40
-     *
41
-     * @var string $show_datetime_selector
42
-     */
43
-    protected $show_datetime_selector = EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR;
44
-
45
-    /**
46
-     * the number of datetimes an event has to have before conditionally displaying a datetime selector
47
-     *
48
-     * @var int $datetime_selector_threshold
49
-     */
50
-    protected $datetime_selector_threshold = 3;
51
-
52
-    /**
53
-     * determines the maximum number of "checked" dates in the date and time filter
54
-     *
55
-     * @var int $datetime_selector_checked
56
-     */
57
-    protected $datetime_selector_max_checked = 10;
58
-
59
-    private bool $use_new_checkbox_selector = false;
60
-
61
-    private bool $use_new_form_styles = false;
62
-
63
-    private array $accent_color = [210, 100, 50];
64
-
65
-
66
-    public function __construct()
67
-    {
68
-    }
69
-
70
-
71
-    /**
72
-     * returns true if a datetime selector should be displayed
73
-     *
74
-     * @param array $datetimes
75
-     * @return bool
76
-     */
77
-    public function showDatetimeSelector(array $datetimes)
78
-    {
79
-        // if the settings are NOT: don't show OR below threshold, THEN active = true
80
-        return ! (
81
-            $this->getShowDatetimeSelector() === EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR
82
-            || (
83
-                $this->getShowDatetimeSelector() === EE_Ticket_Selector_Config::MAYBE_SHOW_DATETIME_SELECTOR
84
-                && count($datetimes) < $this->getDatetimeSelectorThreshold()
85
-            )
86
-        );
87
-    }
88
-
89
-
90
-    /**
91
-     * @return string
92
-     */
93
-    public function getShowDatetimeSelector()
94
-    {
95
-        return $this->show_datetime_selector;
96
-    }
97
-
98
-
99
-    /**
100
-     * @param bool $keys_only
101
-     * @return array
102
-     */
103
-    public function getShowDatetimeSelectorOptions($keys_only = true)
104
-    {
105
-        return $keys_only
106
-            ? array(
107
-                EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR,
108
-                EE_Ticket_Selector_Config::MAYBE_SHOW_DATETIME_SELECTOR,
109
-            )
110
-            : array(
111
-                EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR => esc_html__(
112
-                    'Do not show date & time filter',
113
-                    'event_espresso'
114
-                ),
115
-                EE_Ticket_Selector_Config::MAYBE_SHOW_DATETIME_SELECTOR  => esc_html__(
116
-                    'Maybe show date & time filter',
117
-                    'event_espresso'
118
-                ),
119
-            );
120
-    }
121
-
122
-
123
-    /**
124
-     * @param string $show_datetime_selector
125
-     */
126
-    public function setShowDatetimeSelector($show_datetime_selector)
127
-    {
128
-        $this->show_datetime_selector = in_array(
129
-            $show_datetime_selector,
130
-            $this->getShowDatetimeSelectorOptions(),
131
-            true
132
-        )
133
-            ? $show_datetime_selector
134
-            : EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR;
135
-    }
136
-
137
-
138
-    /**
139
-     * @return int
140
-     */
141
-    public function getDatetimeSelectorThreshold()
142
-    {
143
-        return $this->datetime_selector_threshold;
144
-    }
145
-
146
-
147
-    /**
148
-     * @param int $datetime_selector_threshold
149
-     */
150
-    public function setDatetimeSelectorThreshold($datetime_selector_threshold)
151
-    {
152
-        $datetime_selector_threshold = absint($datetime_selector_threshold);
153
-        $this->datetime_selector_threshold = $datetime_selector_threshold ?: 3;
154
-    }
155
-
156
-
157
-    /**
158
-     * @return int
159
-     */
160
-    public function getDatetimeSelectorMaxChecked()
161
-    {
162
-        return $this->datetime_selector_max_checked;
163
-    }
164
-
165
-
166
-    /**
167
-     * @param int $datetime_selector_max_checked
168
-     */
169
-    public function setDatetimeSelectorMaxChecked($datetime_selector_max_checked)
170
-    {
171
-        $this->datetime_selector_max_checked = absint($datetime_selector_max_checked);
172
-    }
173
-
174
-
175
-    public function useNewCheckboxSelector(): bool
176
-    {
177
-        return $this->use_new_checkbox_selector;
178
-    }
179
-
180
-
181
-    public function setUseNewCheckboxSelector(bool $use_new_checkbox_selector): void
182
-    {
183
-        $this->use_new_checkbox_selector = $use_new_checkbox_selector;
184
-    }
185
-
186
-
187
-    public function useNewFormStyles(): bool
188
-    {
189
-        return $this->use_new_form_styles;
190
-    }
191
-
192
-
193
-    public function setUseNewFormStyles(bool $use_new_form_styles): void
194
-    {
195
-        $this->use_new_form_styles = $use_new_form_styles;
196
-    }
197
-
198
-
199
-    public function accentColor(): array
200
-    {
201
-        return $this->accent_color;
202
-    }
203
-
204
-
205
-    public function accentColorAsHex(): string
206
-    {
207
-        return ColorConverter::hsl2Hex($this->accent_color);
208
-    }
209
-
210
-
211
-    public function setAccentColor(array $accent_color): void
212
-    {
213
-        $this->accent_color = $accent_color;
214
-    }
215
-
216
-    public function setAccentColorHex(string $accent_color): void
217
-    {
218
-        $this->accent_color = ColorConverter::rgb2Hsl(ColorConverter::hex2RGB($accent_color));
219
-    }
10
+	/**
11
+	 * constant to indicate that a datetime selector should NEVER be shown for ticket selectors
12
+	 */
13
+	const DO_NOT_SHOW_DATETIME_SELECTOR = 'no_datetime_selector';
14
+
15
+	/**
16
+	 * constant to indicate that a datetime selector should only be shown for ticket selectors
17
+	 * when the number of datetimes for the event matches the value set for $datetime_selector_threshold
18
+	 */
19
+	const MAYBE_SHOW_DATETIME_SELECTOR = 'maybe_datetime_selector';
20
+
21
+	/**
22
+	 * @var bool $show_ticket_sale_columns
23
+	 */
24
+	public $show_ticket_sale_columns = true;
25
+
26
+	/**
27
+	 * @var bool $show_ticket_details
28
+	 */
29
+	public $show_ticket_details = true;
30
+
31
+	/**
32
+	 * @var bool $show_expired_tickets
33
+	 */
34
+	public $show_expired_tickets = true;
35
+
36
+	/**
37
+	 * whether to display a dropdown box populated with event datetimes
38
+	 * that toggles which tickets are displayed for a ticket selector.
39
+	 * uses one of the *_DATETIME_SELECTOR constants defined above
40
+	 *
41
+	 * @var string $show_datetime_selector
42
+	 */
43
+	protected $show_datetime_selector = EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR;
44
+
45
+	/**
46
+	 * the number of datetimes an event has to have before conditionally displaying a datetime selector
47
+	 *
48
+	 * @var int $datetime_selector_threshold
49
+	 */
50
+	protected $datetime_selector_threshold = 3;
51
+
52
+	/**
53
+	 * determines the maximum number of "checked" dates in the date and time filter
54
+	 *
55
+	 * @var int $datetime_selector_checked
56
+	 */
57
+	protected $datetime_selector_max_checked = 10;
58
+
59
+	private bool $use_new_checkbox_selector = false;
60
+
61
+	private bool $use_new_form_styles = false;
62
+
63
+	private array $accent_color = [210, 100, 50];
64
+
65
+
66
+	public function __construct()
67
+	{
68
+	}
69
+
70
+
71
+	/**
72
+	 * returns true if a datetime selector should be displayed
73
+	 *
74
+	 * @param array $datetimes
75
+	 * @return bool
76
+	 */
77
+	public function showDatetimeSelector(array $datetimes)
78
+	{
79
+		// if the settings are NOT: don't show OR below threshold, THEN active = true
80
+		return ! (
81
+			$this->getShowDatetimeSelector() === EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR
82
+			|| (
83
+				$this->getShowDatetimeSelector() === EE_Ticket_Selector_Config::MAYBE_SHOW_DATETIME_SELECTOR
84
+				&& count($datetimes) < $this->getDatetimeSelectorThreshold()
85
+			)
86
+		);
87
+	}
88
+
89
+
90
+	/**
91
+	 * @return string
92
+	 */
93
+	public function getShowDatetimeSelector()
94
+	{
95
+		return $this->show_datetime_selector;
96
+	}
97
+
98
+
99
+	/**
100
+	 * @param bool $keys_only
101
+	 * @return array
102
+	 */
103
+	public function getShowDatetimeSelectorOptions($keys_only = true)
104
+	{
105
+		return $keys_only
106
+			? array(
107
+				EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR,
108
+				EE_Ticket_Selector_Config::MAYBE_SHOW_DATETIME_SELECTOR,
109
+			)
110
+			: array(
111
+				EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR => esc_html__(
112
+					'Do not show date & time filter',
113
+					'event_espresso'
114
+				),
115
+				EE_Ticket_Selector_Config::MAYBE_SHOW_DATETIME_SELECTOR  => esc_html__(
116
+					'Maybe show date & time filter',
117
+					'event_espresso'
118
+				),
119
+			);
120
+	}
121
+
122
+
123
+	/**
124
+	 * @param string $show_datetime_selector
125
+	 */
126
+	public function setShowDatetimeSelector($show_datetime_selector)
127
+	{
128
+		$this->show_datetime_selector = in_array(
129
+			$show_datetime_selector,
130
+			$this->getShowDatetimeSelectorOptions(),
131
+			true
132
+		)
133
+			? $show_datetime_selector
134
+			: EE_Ticket_Selector_Config::DO_NOT_SHOW_DATETIME_SELECTOR;
135
+	}
136
+
137
+
138
+	/**
139
+	 * @return int
140
+	 */
141
+	public function getDatetimeSelectorThreshold()
142
+	{
143
+		return $this->datetime_selector_threshold;
144
+	}
145
+
146
+
147
+	/**
148
+	 * @param int $datetime_selector_threshold
149
+	 */
150
+	public function setDatetimeSelectorThreshold($datetime_selector_threshold)
151
+	{
152
+		$datetime_selector_threshold = absint($datetime_selector_threshold);
153
+		$this->datetime_selector_threshold = $datetime_selector_threshold ?: 3;
154
+	}
155
+
156
+
157
+	/**
158
+	 * @return int
159
+	 */
160
+	public function getDatetimeSelectorMaxChecked()
161
+	{
162
+		return $this->datetime_selector_max_checked;
163
+	}
164
+
165
+
166
+	/**
167
+	 * @param int $datetime_selector_max_checked
168
+	 */
169
+	public function setDatetimeSelectorMaxChecked($datetime_selector_max_checked)
170
+	{
171
+		$this->datetime_selector_max_checked = absint($datetime_selector_max_checked);
172
+	}
173
+
174
+
175
+	public function useNewCheckboxSelector(): bool
176
+	{
177
+		return $this->use_new_checkbox_selector;
178
+	}
179
+
180
+
181
+	public function setUseNewCheckboxSelector(bool $use_new_checkbox_selector): void
182
+	{
183
+		$this->use_new_checkbox_selector = $use_new_checkbox_selector;
184
+	}
185
+
186
+
187
+	public function useNewFormStyles(): bool
188
+	{
189
+		return $this->use_new_form_styles;
190
+	}
191
+
192
+
193
+	public function setUseNewFormStyles(bool $use_new_form_styles): void
194
+	{
195
+		$this->use_new_form_styles = $use_new_form_styles;
196
+	}
197
+
198
+
199
+	public function accentColor(): array
200
+	{
201
+		return $this->accent_color;
202
+	}
203
+
204
+
205
+	public function accentColorAsHex(): string
206
+	{
207
+		return ColorConverter::hsl2Hex($this->accent_color);
208
+	}
209
+
210
+
211
+	public function setAccentColor(array $accent_color): void
212
+	{
213
+		$this->accent_color = $accent_color;
214
+	}
215
+
216
+	public function setAccentColorHex(string $accent_color): void
217
+	{
218
+		$this->accent_color = ColorConverter::rgb2Hsl(ColorConverter::hex2RGB($accent_color));
219
+	}
220 220
 }
Please login to merge, or discard this patch.
core/domain/entities/notifications/PersistentAdminNotice.php 2 patches
Indentation   +315 added lines, -315 removed lines patch added patch discarded remove patch
@@ -24,319 +24,319 @@
 block discarded – undo
24 24
  */
25 25
 class PersistentAdminNotice implements RequiresCapCheckInterface
26 26
 {
27
-    protected string $name = '';
28
-
29
-    protected string $message = '';
30
-
31
-    protected string $extra_css = '';
32
-
33
-    protected string $type = 'info';
34
-
35
-    private array $allowed_types = ['info', 'success', 'attention', 'warning', 'error'];
36
-
37
-    protected bool $force_update = false;
38
-
39
-    protected string $capability = 'manage_options';
40
-
41
-    protected string $cap_context = 'view persistent admin notice';
42
-
43
-    protected bool $dismissed = false;
44
-
45
-    protected ?CapCheckInterface $cap_check = null;
46
-
47
-    /**
48
-     * if true, then this notice will be deleted from the database
49
-     *
50
-     * @var boolean $purge
51
-     */
52
-    protected bool $purge = false;
53
-
54
-    /**
55
-     * gets set to true if notice is successfully registered with the PersistentAdminNoticeManager
56
-     * if false, and WP_DEBUG is on, then an exception will be thrown in the admin footer
57
-     *
58
-     * @var boolean $registered
59
-     */
60
-    private bool $registered = false;
61
-
62
-
63
-    /**
64
-     * PersistentAdminNotice constructor
65
-     *
66
-     * @param string $name         [required] the name, or key of the Persistent Admin Notice to be stored
67
-     * @param string $message      [required] the message to be stored persistently until dismissed
68
-     * @param bool   $force_update enforce the reappearance of a persistent message
69
-     * @param string $capability   user capability required to view this notice
70
-     * @param string $cap_context  description for why the cap check is being performed
71
-     * @param bool   $dismissed    whether the user has already dismissed/viewed this notice
72
-     * @param string $type         the type of notice (info, success, attention, warning, error)
73
-     * @param string $extra_css    additional CSS classes to be applied to the main notice container
74
-     */
75
-    public function __construct(
76
-        string $name,
77
-        string $message,
78
-        bool $force_update = false,
79
-        string $capability = 'manage_options',
80
-        string $cap_context = 'view persistent admin notice',
81
-        bool $dismissed = false,
82
-        string $type = 'info',
83
-        string $extra_css = ''
84
-    ) {
85
-        $this->setName($name);
86
-        $this->setMessage($message);
87
-        $this->setForceUpdate($force_update);
88
-        $this->setCapability($capability);
89
-        $this->setCapContext($cap_context);
90
-        $this->setDismissed($dismissed);
91
-        $this->setType($type);
92
-        $this->setExtraCss($extra_css);
93
-
94
-        add_action(
95
-            'AHEE__EventEspresso_core_services_notifications_PersistentAdminNoticeManager__registerNotices',
96
-            [$this, 'registerPersistentAdminNotice']
97
-        );
98
-        add_action('shutdown', [$this, 'confirmRegistered'], 999);
99
-    }
100
-
101
-
102
-    /**
103
-     * @return string
104
-     */
105
-    public function getName(): string
106
-    {
107
-        return $this->name;
108
-    }
109
-
110
-
111
-    /**
112
-     * @param string $name
113
-     */
114
-    private function setName(string $name)
115
-    {
116
-        $this->name = sanitize_key($name);
117
-    }
118
-
119
-
120
-    /**
121
-     * @return string
122
-     */
123
-    public function getMessage(): string
124
-    {
125
-        return $this->message;
126
-    }
127
-
128
-
129
-    /**
130
-     * @param string $message
131
-     */
132
-    private function setMessage(string $message)
133
-    {
134
-        $allowedtags   = AllowedTags::getAllowedTags();
135
-        $this->message = wp_kses($message, $allowedtags);
136
-    }
137
-
138
-
139
-    public function getType(): string
140
-    {
141
-        return $this->type ?: 'info';
142
-    }
143
-
144
-
145
-    public function setType(string $type): void
146
-    {
147
-        if (! in_array($type, $this->allowed_types, true)) {
148
-            throw new DomainException(
149
-                sprintf(
150
-                    esc_html__(
151
-                        'The "%1$s" type is not allowed for PersistentAdminNotice. Allowed types are: %2$s',
152
-                        'event_espresso'
153
-                    ),
154
-                    $type,
155
-                    implode(', ', $this->allowed_types)
156
-                )
157
-            );
158
-        }
159
-        $this->type = $type;
160
-    }
161
-
162
-
163
-    public function extraCss(): string
164
-    {
165
-        return $this->extra_css;
166
-    }
167
-
168
-
169
-    public function setExtraCss(string $extra_css): void
170
-    {
171
-        $this->extra_css = $extra_css;
172
-    }
173
-
174
-
175
-    /**
176
-     * @return bool
177
-     */
178
-    public function getForceUpdate(): bool
179
-    {
180
-        return $this->force_update;
181
-    }
182
-
183
-
184
-    /**
185
-     * @param bool|int|string $force_update
186
-     */
187
-    private function setForceUpdate($force_update)
188
-    {
189
-        $this->force_update = filter_var($force_update, FILTER_VALIDATE_BOOLEAN);
190
-    }
191
-
192
-
193
-    /**
194
-     * @return string
195
-     */
196
-    public function getCapability(): string
197
-    {
198
-        return $this->capability;
199
-    }
200
-
201
-
202
-    /**
203
-     * @param string $capability
204
-     */
205
-    private function setCapability(string $capability)
206
-    {
207
-        $this->capability = ! empty($capability) ? $capability : 'manage_options';
208
-    }
209
-
210
-
211
-    /**
212
-     * @return string
213
-     */
214
-    public function getCapContext(): string
215
-    {
216
-        return $this->cap_context;
217
-    }
218
-
219
-
220
-    /**
221
-     * @param string $cap_context
222
-     */
223
-    private function setCapContext(string $cap_context)
224
-    {
225
-        $this->cap_context = ! empty($cap_context) ? $cap_context : 'view persistent admin notice';
226
-    }
227
-
228
-
229
-    /**
230
-     * @return bool
231
-     */
232
-    public function getDismissed(): bool
233
-    {
234
-        return $this->dismissed;
235
-    }
236
-
237
-
238
-    /**
239
-     * @param bool|int|string $dismissed
240
-     */
241
-    public function setDismissed($dismissed)
242
-    {
243
-        $this->dismissed = filter_var($dismissed, FILTER_VALIDATE_BOOLEAN);
244
-    }
245
-
246
-
247
-    /**
248
-     * @return CapCheckInterface
249
-     */
250
-    public function getCapCheck(): ?CapCheckInterface
251
-    {
252
-        if (! $this->cap_check instanceof CapCheckInterface) {
253
-            $this->setCapCheck(new CapCheck($this->capability, $this->cap_context));
254
-        }
255
-        return $this->cap_check;
256
-    }
257
-
258
-
259
-    /**
260
-     * @param CapCheckInterface $cap_check
261
-     */
262
-    private function setCapCheck(CapCheckInterface $cap_check)
263
-    {
264
-        $this->cap_check = $cap_check;
265
-    }
266
-
267
-
268
-    /**
269
-     * @return bool
270
-     */
271
-    public function getPurge(): bool
272
-    {
273
-        return $this->purge;
274
-    }
275
-
276
-
277
-    /**
278
-     * @param bool|int|string $purge
279
-     */
280
-    public function setPurge($purge)
281
-    {
282
-        $this->purge = filter_var($purge, FILTER_VALIDATE_BOOLEAN);
283
-    }
284
-
285
-
286
-    /**
287
-     * given a valid PersistentAdminNotice Collection,
288
-     * this notice will be added if it is not already found in the collection (using its name as the identifier)
289
-     * if an existing notice is found that has already been dismissed,
290
-     * but we are overriding with a forced update, then we will toggle its dismissed state,
291
-     * so that the notice is displayed again
292
-     *
293
-     * @param Collection $persistent_admin_notice_collection
294
-     * @throws InvalidEntityException
295
-     * @throws DuplicateCollectionIdentifierException
296
-     */
297
-    public function registerPersistentAdminNotice(Collection $persistent_admin_notice_collection)
298
-    {
299
-        if ($this->registered) {
300
-            return;
301
-        }
302
-        // first check if this notice has already been added to the collection
303
-        if ($persistent_admin_notice_collection->has($this->name)) {
304
-            /** @var PersistentAdminNotice $existing */
305
-            $existing = $persistent_admin_notice_collection->get($this->name);
306
-            // we don't need to add it again (we can't actually)
307
-            // but if it has already been dismissed, and we are overriding with a forced update
308
-            if ($existing->getDismissed() && $this->getForceUpdate()) {
309
-                // then toggle the notice's dismissed state to true
310
-                // so that it gets displayed again
311
-                $existing->setDismissed(false);
312
-                // and make sure the message is set
313
-                $existing->setMessage($this->message);
314
-            }
315
-        } else {
316
-            $persistent_admin_notice_collection->add($this, $this->name);
317
-        }
318
-        $this->registered = true;
319
-    }
320
-
321
-
322
-    /**
323
-     * @throws Exception
324
-     */
325
-    public function confirmRegistered()
326
-    {
327
-        if (! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) {
328
-            PersistentAdminNoticeManager::loadRegisterAndSaveNotices();
329
-        }
330
-        if (! $this->registered && WP_DEBUG) {
331
-            throw new DomainException(
332
-                sprintf(
333
-                    esc_html__(
334
-                        'The "%1$s" PersistentAdminNotice was not successfully registered. Please ensure that it is being created prior to either the "admin_notices" or "network_admin_notices" hooks being triggered.',
335
-                        'event_espresso'
336
-                    ),
337
-                    $this->name
338
-                )
339
-            );
340
-        }
341
-    }
27
+	protected string $name = '';
28
+
29
+	protected string $message = '';
30
+
31
+	protected string $extra_css = '';
32
+
33
+	protected string $type = 'info';
34
+
35
+	private array $allowed_types = ['info', 'success', 'attention', 'warning', 'error'];
36
+
37
+	protected bool $force_update = false;
38
+
39
+	protected string $capability = 'manage_options';
40
+
41
+	protected string $cap_context = 'view persistent admin notice';
42
+
43
+	protected bool $dismissed = false;
44
+
45
+	protected ?CapCheckInterface $cap_check = null;
46
+
47
+	/**
48
+	 * if true, then this notice will be deleted from the database
49
+	 *
50
+	 * @var boolean $purge
51
+	 */
52
+	protected bool $purge = false;
53
+
54
+	/**
55
+	 * gets set to true if notice is successfully registered with the PersistentAdminNoticeManager
56
+	 * if false, and WP_DEBUG is on, then an exception will be thrown in the admin footer
57
+	 *
58
+	 * @var boolean $registered
59
+	 */
60
+	private bool $registered = false;
61
+
62
+
63
+	/**
64
+	 * PersistentAdminNotice constructor
65
+	 *
66
+	 * @param string $name         [required] the name, or key of the Persistent Admin Notice to be stored
67
+	 * @param string $message      [required] the message to be stored persistently until dismissed
68
+	 * @param bool   $force_update enforce the reappearance of a persistent message
69
+	 * @param string $capability   user capability required to view this notice
70
+	 * @param string $cap_context  description for why the cap check is being performed
71
+	 * @param bool   $dismissed    whether the user has already dismissed/viewed this notice
72
+	 * @param string $type         the type of notice (info, success, attention, warning, error)
73
+	 * @param string $extra_css    additional CSS classes to be applied to the main notice container
74
+	 */
75
+	public function __construct(
76
+		string $name,
77
+		string $message,
78
+		bool $force_update = false,
79
+		string $capability = 'manage_options',
80
+		string $cap_context = 'view persistent admin notice',
81
+		bool $dismissed = false,
82
+		string $type = 'info',
83
+		string $extra_css = ''
84
+	) {
85
+		$this->setName($name);
86
+		$this->setMessage($message);
87
+		$this->setForceUpdate($force_update);
88
+		$this->setCapability($capability);
89
+		$this->setCapContext($cap_context);
90
+		$this->setDismissed($dismissed);
91
+		$this->setType($type);
92
+		$this->setExtraCss($extra_css);
93
+
94
+		add_action(
95
+			'AHEE__EventEspresso_core_services_notifications_PersistentAdminNoticeManager__registerNotices',
96
+			[$this, 'registerPersistentAdminNotice']
97
+		);
98
+		add_action('shutdown', [$this, 'confirmRegistered'], 999);
99
+	}
100
+
101
+
102
+	/**
103
+	 * @return string
104
+	 */
105
+	public function getName(): string
106
+	{
107
+		return $this->name;
108
+	}
109
+
110
+
111
+	/**
112
+	 * @param string $name
113
+	 */
114
+	private function setName(string $name)
115
+	{
116
+		$this->name = sanitize_key($name);
117
+	}
118
+
119
+
120
+	/**
121
+	 * @return string
122
+	 */
123
+	public function getMessage(): string
124
+	{
125
+		return $this->message;
126
+	}
127
+
128
+
129
+	/**
130
+	 * @param string $message
131
+	 */
132
+	private function setMessage(string $message)
133
+	{
134
+		$allowedtags   = AllowedTags::getAllowedTags();
135
+		$this->message = wp_kses($message, $allowedtags);
136
+	}
137
+
138
+
139
+	public function getType(): string
140
+	{
141
+		return $this->type ?: 'info';
142
+	}
143
+
144
+
145
+	public function setType(string $type): void
146
+	{
147
+		if (! in_array($type, $this->allowed_types, true)) {
148
+			throw new DomainException(
149
+				sprintf(
150
+					esc_html__(
151
+						'The "%1$s" type is not allowed for PersistentAdminNotice. Allowed types are: %2$s',
152
+						'event_espresso'
153
+					),
154
+					$type,
155
+					implode(', ', $this->allowed_types)
156
+				)
157
+			);
158
+		}
159
+		$this->type = $type;
160
+	}
161
+
162
+
163
+	public function extraCss(): string
164
+	{
165
+		return $this->extra_css;
166
+	}
167
+
168
+
169
+	public function setExtraCss(string $extra_css): void
170
+	{
171
+		$this->extra_css = $extra_css;
172
+	}
173
+
174
+
175
+	/**
176
+	 * @return bool
177
+	 */
178
+	public function getForceUpdate(): bool
179
+	{
180
+		return $this->force_update;
181
+	}
182
+
183
+
184
+	/**
185
+	 * @param bool|int|string $force_update
186
+	 */
187
+	private function setForceUpdate($force_update)
188
+	{
189
+		$this->force_update = filter_var($force_update, FILTER_VALIDATE_BOOLEAN);
190
+	}
191
+
192
+
193
+	/**
194
+	 * @return string
195
+	 */
196
+	public function getCapability(): string
197
+	{
198
+		return $this->capability;
199
+	}
200
+
201
+
202
+	/**
203
+	 * @param string $capability
204
+	 */
205
+	private function setCapability(string $capability)
206
+	{
207
+		$this->capability = ! empty($capability) ? $capability : 'manage_options';
208
+	}
209
+
210
+
211
+	/**
212
+	 * @return string
213
+	 */
214
+	public function getCapContext(): string
215
+	{
216
+		return $this->cap_context;
217
+	}
218
+
219
+
220
+	/**
221
+	 * @param string $cap_context
222
+	 */
223
+	private function setCapContext(string $cap_context)
224
+	{
225
+		$this->cap_context = ! empty($cap_context) ? $cap_context : 'view persistent admin notice';
226
+	}
227
+
228
+
229
+	/**
230
+	 * @return bool
231
+	 */
232
+	public function getDismissed(): bool
233
+	{
234
+		return $this->dismissed;
235
+	}
236
+
237
+
238
+	/**
239
+	 * @param bool|int|string $dismissed
240
+	 */
241
+	public function setDismissed($dismissed)
242
+	{
243
+		$this->dismissed = filter_var($dismissed, FILTER_VALIDATE_BOOLEAN);
244
+	}
245
+
246
+
247
+	/**
248
+	 * @return CapCheckInterface
249
+	 */
250
+	public function getCapCheck(): ?CapCheckInterface
251
+	{
252
+		if (! $this->cap_check instanceof CapCheckInterface) {
253
+			$this->setCapCheck(new CapCheck($this->capability, $this->cap_context));
254
+		}
255
+		return $this->cap_check;
256
+	}
257
+
258
+
259
+	/**
260
+	 * @param CapCheckInterface $cap_check
261
+	 */
262
+	private function setCapCheck(CapCheckInterface $cap_check)
263
+	{
264
+		$this->cap_check = $cap_check;
265
+	}
266
+
267
+
268
+	/**
269
+	 * @return bool
270
+	 */
271
+	public function getPurge(): bool
272
+	{
273
+		return $this->purge;
274
+	}
275
+
276
+
277
+	/**
278
+	 * @param bool|int|string $purge
279
+	 */
280
+	public function setPurge($purge)
281
+	{
282
+		$this->purge = filter_var($purge, FILTER_VALIDATE_BOOLEAN);
283
+	}
284
+
285
+
286
+	/**
287
+	 * given a valid PersistentAdminNotice Collection,
288
+	 * this notice will be added if it is not already found in the collection (using its name as the identifier)
289
+	 * if an existing notice is found that has already been dismissed,
290
+	 * but we are overriding with a forced update, then we will toggle its dismissed state,
291
+	 * so that the notice is displayed again
292
+	 *
293
+	 * @param Collection $persistent_admin_notice_collection
294
+	 * @throws InvalidEntityException
295
+	 * @throws DuplicateCollectionIdentifierException
296
+	 */
297
+	public function registerPersistentAdminNotice(Collection $persistent_admin_notice_collection)
298
+	{
299
+		if ($this->registered) {
300
+			return;
301
+		}
302
+		// first check if this notice has already been added to the collection
303
+		if ($persistent_admin_notice_collection->has($this->name)) {
304
+			/** @var PersistentAdminNotice $existing */
305
+			$existing = $persistent_admin_notice_collection->get($this->name);
306
+			// we don't need to add it again (we can't actually)
307
+			// but if it has already been dismissed, and we are overriding with a forced update
308
+			if ($existing->getDismissed() && $this->getForceUpdate()) {
309
+				// then toggle the notice's dismissed state to true
310
+				// so that it gets displayed again
311
+				$existing->setDismissed(false);
312
+				// and make sure the message is set
313
+				$existing->setMessage($this->message);
314
+			}
315
+		} else {
316
+			$persistent_admin_notice_collection->add($this, $this->name);
317
+		}
318
+		$this->registered = true;
319
+	}
320
+
321
+
322
+	/**
323
+	 * @throws Exception
324
+	 */
325
+	public function confirmRegistered()
326
+	{
327
+		if (! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) {
328
+			PersistentAdminNoticeManager::loadRegisterAndSaveNotices();
329
+		}
330
+		if (! $this->registered && WP_DEBUG) {
331
+			throw new DomainException(
332
+				sprintf(
333
+					esc_html__(
334
+						'The "%1$s" PersistentAdminNotice was not successfully registered. Please ensure that it is being created prior to either the "admin_notices" or "network_admin_notices" hooks being triggered.',
335
+						'event_espresso'
336
+					),
337
+					$this->name
338
+				)
339
+			);
340
+		}
341
+	}
342 342
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -144,7 +144,7 @@  discard block
 block discarded – undo
144 144
 
145 145
     public function setType(string $type): void
146 146
     {
147
-        if (! in_array($type, $this->allowed_types, true)) {
147
+        if ( ! in_array($type, $this->allowed_types, true)) {
148 148
             throw new DomainException(
149 149
                 sprintf(
150 150
                     esc_html__(
@@ -249,7 +249,7 @@  discard block
 block discarded – undo
249 249
      */
250 250
     public function getCapCheck(): ?CapCheckInterface
251 251
     {
252
-        if (! $this->cap_check instanceof CapCheckInterface) {
252
+        if ( ! $this->cap_check instanceof CapCheckInterface) {
253 253
             $this->setCapCheck(new CapCheck($this->capability, $this->cap_context));
254 254
         }
255 255
         return $this->cap_check;
@@ -324,10 +324,10 @@  discard block
 block discarded – undo
324 324
      */
325 325
     public function confirmRegistered()
326 326
     {
327
-        if (! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) {
327
+        if ( ! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) {
328 328
             PersistentAdminNoticeManager::loadRegisterAndSaveNotices();
329 329
         }
330
-        if (! $this->registered && WP_DEBUG) {
330
+        if ( ! $this->registered && WP_DEBUG) {
331 331
             throw new DomainException(
332 332
                 sprintf(
333 333
                     esc_html__(
Please login to merge, or discard this patch.
core/admin/EE_Admin_List_Table.core.php 2 patches
Indentation   +969 added lines, -969 removed lines patch added patch discarded remove patch
@@ -6,7 +6,7 @@  discard block
 block discarded – undo
6 6
 use EventEspresso\core\services\request\sanitizers\AllowedTags;
7 7
 
8 8
 if (! class_exists('WP_List_Table')) {
9
-    require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
9
+	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
10 10
 }
11 11
 
12 12
 
@@ -24,465 +24,465 @@  discard block
 block discarded – undo
24 24
  */
25 25
 abstract class EE_Admin_List_Table extends WP_List_Table
26 26
 {
27
-    const ACTION_COPY    = 'duplicate';
28
-
29
-    const ACTION_DELETE  = 'delete';
30
-
31
-    const ACTION_EDIT    = 'edit';
32
-
33
-    const ACTION_RESTORE = 'restore';
34
-
35
-    const ACTION_TRASH   = 'trash';
36
-
37
-    protected static array $actions = [
38
-        self::ACTION_COPY,
39
-        self::ACTION_DELETE,
40
-        self::ACTION_EDIT,
41
-        self::ACTION_RESTORE,
42
-        self::ACTION_TRASH,
43
-    ];
44
-
45
-    /**
46
-     * holds the data that will be processed for the table
47
-     *
48
-     * @var array $_data
49
-     */
50
-    protected array $_data;
51
-
52
-
53
-    /**
54
-     * This holds the value of all the data available for the given view (for all pages).
55
-     *
56
-     * @var int $_all_data_count
57
-     */
58
-    protected int $_all_data_count;
59
-
60
-
61
-    /**
62
-     * Will contain the count of trashed items for the view label.
63
-     *
64
-     * @var int $_trashed_count
65
-     */
66
-    protected int $_trashed_count;
67
-
68
-
69
-    /**
70
-     * This is what will be referenced as the slug for the current screen
71
-     *
72
-     * @var string $_screen
73
-     */
74
-    protected string $_screen;
75
-
76
-
77
-    /**
78
-     * this is the EE_Admin_Page object
79
-     *
80
-     * @var EE_Admin_Page $_admin_page
81
-     */
82
-    protected EE_Admin_Page $_admin_page;
83
-
84
-
85
-    /**
86
-     * The current view
87
-     *
88
-     * @var string $_view
89
-     */
90
-    protected string $_view;
91
-
92
-
93
-    /**
94
-     * array of possible views for this table
95
-     *
96
-     * @var array $_views
97
-     */
98
-    protected array $_views;
99
-
100
-
101
-    /**
102
-     * An array of key => value pairs containing information about the current table
103
-     * array(
104
-     *        'plural' => 'plural label',
105
-     *        'singular' => 'singular label',
106
-     *        'ajax' => false, //whether to use ajax or not
107
-     *        'screen' => null, //string used to reference what screen this is
108
-     *        (WP_List_table converts to screen object)
109
-     * )
110
-     *
111
-     * @var array $_wp_list_args
112
-     */
113
-    protected array $_wp_list_args;
114
-
115
-    /**
116
-     * an array of column names
117
-     * array(
118
-     *    'internal-name' => 'Title'
119
-     * )
120
-     *
121
-     * @var array $_columns
122
-     */
123
-    protected array $_columns;
124
-
125
-    /**
126
-     * An array of sortable columns
127
-     * array(
128
-     *    'internal-name' => 'orderby' //or
129
-     *    'internal-name' => array( 'orderby', true )
130
-     * )
131
-     *
132
-     * @var array $_sortable_columns
133
-     */
134
-    protected array $_sortable_columns;
135
-
136
-    /**
137
-     * callback method used to perform AJAX row reordering
138
-     *
139
-     * @var string|null $_ajax_sorting_callback
140
-     */
141
-    protected ?string $_ajax_sorting_callback = null;
142
-
143
-    /**
144
-     * An array of hidden columns (if needed)
145
-     * array('internal-name', 'internal-name')
146
-     *
147
-     * @var array $_hidden_columns
148
-     */
149
-    protected array $_hidden_columns;
150
-
151
-    /**
152
-     * holds the per_page value
153
-     *
154
-     * @var int $_per_page
155
-     */
156
-    protected int $_per_page;
157
-
158
-    /**
159
-     * holds what page number is currently being viewed
160
-     *
161
-     * @var int $_current_page
162
-     */
163
-    protected int $_current_page;
164
-
165
-    /**
166
-     * the reference string for the nonce_action
167
-     *
168
-     * @var string $_nonce_action_ref
169
-     */
170
-    protected string $_nonce_action_ref;
171
-
172
-    /**
173
-     * property to hold incoming request data (as set by the admin_page_core)
174
-     *
175
-     * @var array $_req_data
176
-     */
177
-    protected array $_req_data;
178
-
179
-
180
-    /**
181
-     * yes / no array for admin form fields
182
-     *
183
-     * @var array $_yes_no
184
-     */
185
-    protected array $_yes_no = [];
186
-
187
-    /**
188
-     * Array describing buttons that should appear at the bottom of the page
189
-     * Keys are strings that represent the button's function (specifically a key in _labels['buttons']),
190
-     * and the values are another array with the following keys
191
-     * array(
192
-     *    'route' => 'page_route',
193
-     *    'extra_request' => array('evt_id' => 1 ); //extra request vars that need to be included in the button.
194
-     * )
195
-     *
196
-     * @var array $_bottom_buttons
197
-     */
198
-    protected array $_bottom_buttons = [];
199
-
200
-
201
-    /**
202
-     * Used to indicate what should be the primary column for the list table.
203
-     * If not present then falls back to what WP calculates
204
-     * as the primary column.
205
-     *
206
-     * @type string $_primary_column
207
-     */
208
-    protected string $_primary_column = '';
209
-
210
-
211
-    /**
212
-     * Used to indicate whether the table has a checkbox column or not.
213
-     *
214
-     * @type bool $_has_checkbox_column
215
-     */
216
-    protected bool $_has_checkbox_column = false;
217
-
218
-    /**
219
-     * @var AdminListTableFilters|null
220
-     */
221
-    protected ?AdminListTableFilters $admin_list_table_filters = null;
222
-
223
-    protected ?RequestInterface $request = null;
224
-
225
-
226
-    /**
227
-     * @param EE_Admin_Page              $admin_page we use this for obtaining everything we need in the list table
228
-     * @param AdminListTableFilters|null $filters    to display list table filters
229
-     */
230
-    public function __construct(EE_Admin_Page $admin_page, ?AdminListTableFilters $filters = null)
231
-    {
232
-        $this->request     = $this->request ?? LoaderFactory::getShared(RequestInterface::class);
233
-        $this->_admin_page = $admin_page;
234
-        // kept for back compat
235
-        $this->_req_data     = $this->_admin_page->get_request_data();
236
-        $this->_view         = $this->_admin_page->get_view();
237
-        $this->_views        = $this->_admin_page->get_list_table_view_RLs();
238
-        $this->_current_page = $this->get_pagenum();
239
-        $this->_screen       = $this->_admin_page->get_current_page() . '_' . $this->_admin_page->get_current_view();
240
-        $this->_yes_no       = [
241
-            esc_html__('No', 'event_espresso'),
242
-            esc_html__('Yes', 'event_espresso'),
243
-        ];
244
-
245
-        $this->_per_page = $this->get_items_per_page($this->_screen . '_per_page');
246
-
247
-        $this->admin_list_table_filters = $filters instanceof AdminListTableFilters
248
-            ? $filters
249
-            : LoaderFactory::getShared(AdminListTableFilters::class);
250
-
251
-        $this->_setup_data();
252
-        $this->_add_view_counts();
253
-        // ensure changes made to views in child classes get ported back to the admin page
254
-        $this->_admin_page->updateViews($this->_views);
255
-
256
-        $this->_nonce_action_ref = $this->_view;
257
-
258
-        $this->_set_properties();
259
-
260
-        // set primary column
261
-        add_filter('list_table_primary_column', [$this, 'set_primary_column']);
262
-
263
-        // set parent defaults
264
-        parent::__construct($this->_wp_list_args);
265
-
266
-        $this->prepare_items();
267
-    }
268
-
269
-
270
-    /**
271
-     * _setup_data
272
-     * this method is used to setup the $_data, $_all_data_count, and _per_page properties
273
-     *
274
-     * @return void
275
-     * @uses $this->_admin_page
276
-     */
277
-    abstract protected function _setup_data();
278
-
279
-
280
-    /**
281
-     * set the properties that this class needs to be able to execute wp_list_table properly
282
-     * properties set:
283
-     * _wp_list_args = what the arguments required for the parent _wp_list_table.
284
-     * _columns = set the columns in an array.
285
-     * _sortable_columns = columns that are sortable (array).
286
-     * _hidden_columns = columns that are hidden (array)
287
-     * _default_orderby = the default orderby for sorting.
288
-     *
289
-     * @abstract
290
-     * @access protected
291
-     * @return void
292
-     */
293
-    abstract protected function _set_properties();
294
-
295
-
296
-    /**
297
-     * _get_table_filters
298
-     * We use this to assemble and return any filters that are associated with this table that help further refine what
299
-     * gets shown in the table.
300
-     *
301
-     * @abstract
302
-     * @access protected
303
-     * @return string[]
304
-     */
305
-    abstract protected function _get_table_filters();
306
-
307
-
308
-    /**
309
-     * this is a method that child class will do to add counts to the views array so when views are displayed the
310
-     * counts of the views is accurate.
311
-     *
312
-     * @abstract
313
-     * @access protected
314
-     * @return void
315
-     */
316
-    abstract protected function _add_view_counts();
317
-
318
-
319
-    /**
320
-     * _get_hidden_fields
321
-     * returns a html string of hidden fields so if any table filters are used the current view will be respected.
322
-     *
323
-     * @return string
324
-     */
325
-    protected function _get_hidden_fields()
326
-    {
327
-        $page   = $this->request->getRequestParam('page', '');
328
-        $action = $this->request->getRequestParam('route', '');
329
-        $action = $this->request->getRequestParam('action', $action);
330
-        // if action is STILL empty, then we set it to default
331
-        $action = empty($action) || $action === '-1' ? 'default' : $action;
332
-        $field  = '<input type="hidden" name="page" value="' . esc_attr($page) . '" />' . "\n";
333
-        $field  .= '<input type="hidden" name="route" value="' . esc_attr($action) . '" />' . "\n";
334
-        $field  .= '<input type="hidden" name="perpage" value="' . esc_attr($this->_per_page) . '" />' . "\n";
335
-
336
-        $bulk_actions = $this->_get_bulk_actions();
337
-        foreach ($bulk_actions as $bulk_action => $label) {
338
-            $field .= '<input type="hidden" name="' . $bulk_action . '_nonce"'
339
-                . ' value="' . wp_create_nonce($bulk_action . '_nonce') . '" />' . "\n";
340
-        }
341
-
342
-        return $field;
343
-    }
344
-
345
-
346
-    /**
347
-     * _set_column_info
348
-     * we're using this to set the column headers property.
349
-     *
350
-     * @access protected
351
-     * @return void
352
-     */
353
-    protected function _set_column_info()
354
-    {
355
-        $columns   = $this->get_columns();
356
-        $hidden    = $this->get_hidden_columns();
357
-        $_sortable = $this->get_sortable_columns();
358
-
359
-        /**
360
-         * Dynamic hook allowing for adding sortable columns in this list table.
361
-         * Note that $this->screen->id is in the format
362
-         * {sanitize_title($top_level_menu_label)}_page_{$espresso_admin_page_slug}.  So for the messages list
363
-         * table it is: event-espresso_page_espresso_messages.
364
-         * However, take note that if the top level menu label has been translated (i.e. "Event Espresso"). then the
365
-         * hook prefix ("event-espresso") will be different.
366
-         *
367
-         * @var array $_sortable
368
-         */
369
-        $_sortable = apply_filters(
370
-            "FHEE_manage_{$this->screen->id}_sortable_columns",
371
-            $_sortable,
372
-            $this->_screen,
373
-            $this
374
-        );
375
-
376
-        $sortable = [];
377
-        foreach ($_sortable as $id => $data) {
378
-            if (empty($data)) {
379
-                continue;
380
-            }
381
-            // fix for offset errors with WP_List_Table default get_columninfo()
382
-            if (is_array($data)) {
383
-                $_data[0] = key($data);
384
-                $_data[1] = $data[1] ?? false;
385
-            } else {
386
-                $_data[0] = $data;
387
-            }
388
-
389
-            $data = (array) $data;
390
-
391
-            if (! isset($data[1])) {
392
-                $_data[1] = false;
393
-            }
394
-
395
-            $sortable[ $id ] = $_data;
396
-        }
397
-        $primary               = $this->get_primary_column_name();
398
-        $this->_column_headers = [$columns, $hidden, $sortable, $primary];
399
-    }
400
-
401
-
402
-    /**
403
-     * Added for WP4.1 backward compat (@see https://events.codebasehq.com/projects/event-espresso/tickets/8814)
404
-     *
405
-     * @return string
406
-     */
407
-    protected function get_primary_column_name()
408
-    {
409
-        foreach (class_parents($this) as $parent) {
410
-            if ($parent === 'WP_List_Table' && method_exists($parent, 'get_primary_column_name')) {
411
-                return parent::get_primary_column_name();
412
-            }
413
-        }
414
-        return $this->_primary_column;
415
-    }
416
-
417
-
418
-    /**
419
-     * Added for WP4.1 backward compat (@see https://events.codebasehq.com/projects/event-espresso/tickets/8814)
420
-     *
421
-     * @param EE_Base_Class $item
422
-     * @param string        $column_name
423
-     * @param string        $primary
424
-     * @return string
425
-     */
426
-    protected function handle_row_actions($item, $column_name, $primary)
427
-    {
428
-        foreach (class_parents($this) as $parent) {
429
-            if ($parent === 'WP_List_Table' && method_exists($parent, 'handle_row_actions')) {
430
-                return parent::handle_row_actions($item, $column_name, $primary);
431
-            }
432
-        }
433
-        return '';
434
-    }
435
-
436
-
437
-    /**
438
-     * _get_bulk_actions
439
-     * This is a wrapper called by WP_List_Table::get_bulk_actions()
440
-     *
441
-     * @access protected
442
-     * @return array bulk_actions
443
-     */
444
-    protected function _get_bulk_actions(): array
445
-    {
446
-        $actions = [];
447
-        // the _views property should have the bulk_actions, so let's go through and extract them into a properly
448
-        // formatted array for the wp_list_table();
449
-        foreach ($this->get_views() as $view => $args) {
450
-            if ($this->_view === $view && isset($args['bulk_action']) && is_array($args['bulk_action'])) {
451
-                // each bulk action will correspond with an admin page route, so we can check whatever the capability is
452
-                // for that page route and skip adding the bulk action if no access for the current logged-in user.
453
-                foreach ($args['bulk_action'] as $route => $label) {
454
-                    if ($this->_admin_page->check_user_access($route, true)) {
455
-                        $actions[ $route ] = $label;
456
-                    }
457
-                }
458
-            }
459
-        }
460
-        return $actions;
461
-    }
462
-
463
-
464
-    /**
465
-     * Generate the table navigation above or below the table.
466
-     * Overrides the parent table nav in WP_List_Table so we can hide the bulk action div if there are no bulk actions.
467
-     *
468
-     * @throws EE_Error
469
-     * @since 4.9.44.rc.001
470
-     */
471
-    public function display_tablenav($which)
472
-    {
473
-        if ('top' === $which) {
474
-            wp_nonce_field('bulk-' . $this->_args['plural']);
475
-        }
476
-
477
-        ob_start();
478
-        $this->extra_tablenav($which);
479
-        $ee_tablenav = ob_get_clean();
480
-
481
-        ob_start();
482
-        $this->bulk_actions();
483
-        $bulkactions = ob_get_clean();
484
-
485
-        ?>
27
+	const ACTION_COPY    = 'duplicate';
28
+
29
+	const ACTION_DELETE  = 'delete';
30
+
31
+	const ACTION_EDIT    = 'edit';
32
+
33
+	const ACTION_RESTORE = 'restore';
34
+
35
+	const ACTION_TRASH   = 'trash';
36
+
37
+	protected static array $actions = [
38
+		self::ACTION_COPY,
39
+		self::ACTION_DELETE,
40
+		self::ACTION_EDIT,
41
+		self::ACTION_RESTORE,
42
+		self::ACTION_TRASH,
43
+	];
44
+
45
+	/**
46
+	 * holds the data that will be processed for the table
47
+	 *
48
+	 * @var array $_data
49
+	 */
50
+	protected array $_data;
51
+
52
+
53
+	/**
54
+	 * This holds the value of all the data available for the given view (for all pages).
55
+	 *
56
+	 * @var int $_all_data_count
57
+	 */
58
+	protected int $_all_data_count;
59
+
60
+
61
+	/**
62
+	 * Will contain the count of trashed items for the view label.
63
+	 *
64
+	 * @var int $_trashed_count
65
+	 */
66
+	protected int $_trashed_count;
67
+
68
+
69
+	/**
70
+	 * This is what will be referenced as the slug for the current screen
71
+	 *
72
+	 * @var string $_screen
73
+	 */
74
+	protected string $_screen;
75
+
76
+
77
+	/**
78
+	 * this is the EE_Admin_Page object
79
+	 *
80
+	 * @var EE_Admin_Page $_admin_page
81
+	 */
82
+	protected EE_Admin_Page $_admin_page;
83
+
84
+
85
+	/**
86
+	 * The current view
87
+	 *
88
+	 * @var string $_view
89
+	 */
90
+	protected string $_view;
91
+
92
+
93
+	/**
94
+	 * array of possible views for this table
95
+	 *
96
+	 * @var array $_views
97
+	 */
98
+	protected array $_views;
99
+
100
+
101
+	/**
102
+	 * An array of key => value pairs containing information about the current table
103
+	 * array(
104
+	 *        'plural' => 'plural label',
105
+	 *        'singular' => 'singular label',
106
+	 *        'ajax' => false, //whether to use ajax or not
107
+	 *        'screen' => null, //string used to reference what screen this is
108
+	 *        (WP_List_table converts to screen object)
109
+	 * )
110
+	 *
111
+	 * @var array $_wp_list_args
112
+	 */
113
+	protected array $_wp_list_args;
114
+
115
+	/**
116
+	 * an array of column names
117
+	 * array(
118
+	 *    'internal-name' => 'Title'
119
+	 * )
120
+	 *
121
+	 * @var array $_columns
122
+	 */
123
+	protected array $_columns;
124
+
125
+	/**
126
+	 * An array of sortable columns
127
+	 * array(
128
+	 *    'internal-name' => 'orderby' //or
129
+	 *    'internal-name' => array( 'orderby', true )
130
+	 * )
131
+	 *
132
+	 * @var array $_sortable_columns
133
+	 */
134
+	protected array $_sortable_columns;
135
+
136
+	/**
137
+	 * callback method used to perform AJAX row reordering
138
+	 *
139
+	 * @var string|null $_ajax_sorting_callback
140
+	 */
141
+	protected ?string $_ajax_sorting_callback = null;
142
+
143
+	/**
144
+	 * An array of hidden columns (if needed)
145
+	 * array('internal-name', 'internal-name')
146
+	 *
147
+	 * @var array $_hidden_columns
148
+	 */
149
+	protected array $_hidden_columns;
150
+
151
+	/**
152
+	 * holds the per_page value
153
+	 *
154
+	 * @var int $_per_page
155
+	 */
156
+	protected int $_per_page;
157
+
158
+	/**
159
+	 * holds what page number is currently being viewed
160
+	 *
161
+	 * @var int $_current_page
162
+	 */
163
+	protected int $_current_page;
164
+
165
+	/**
166
+	 * the reference string for the nonce_action
167
+	 *
168
+	 * @var string $_nonce_action_ref
169
+	 */
170
+	protected string $_nonce_action_ref;
171
+
172
+	/**
173
+	 * property to hold incoming request data (as set by the admin_page_core)
174
+	 *
175
+	 * @var array $_req_data
176
+	 */
177
+	protected array $_req_data;
178
+
179
+
180
+	/**
181
+	 * yes / no array for admin form fields
182
+	 *
183
+	 * @var array $_yes_no
184
+	 */
185
+	protected array $_yes_no = [];
186
+
187
+	/**
188
+	 * Array describing buttons that should appear at the bottom of the page
189
+	 * Keys are strings that represent the button's function (specifically a key in _labels['buttons']),
190
+	 * and the values are another array with the following keys
191
+	 * array(
192
+	 *    'route' => 'page_route',
193
+	 *    'extra_request' => array('evt_id' => 1 ); //extra request vars that need to be included in the button.
194
+	 * )
195
+	 *
196
+	 * @var array $_bottom_buttons
197
+	 */
198
+	protected array $_bottom_buttons = [];
199
+
200
+
201
+	/**
202
+	 * Used to indicate what should be the primary column for the list table.
203
+	 * If not present then falls back to what WP calculates
204
+	 * as the primary column.
205
+	 *
206
+	 * @type string $_primary_column
207
+	 */
208
+	protected string $_primary_column = '';
209
+
210
+
211
+	/**
212
+	 * Used to indicate whether the table has a checkbox column or not.
213
+	 *
214
+	 * @type bool $_has_checkbox_column
215
+	 */
216
+	protected bool $_has_checkbox_column = false;
217
+
218
+	/**
219
+	 * @var AdminListTableFilters|null
220
+	 */
221
+	protected ?AdminListTableFilters $admin_list_table_filters = null;
222
+
223
+	protected ?RequestInterface $request = null;
224
+
225
+
226
+	/**
227
+	 * @param EE_Admin_Page              $admin_page we use this for obtaining everything we need in the list table
228
+	 * @param AdminListTableFilters|null $filters    to display list table filters
229
+	 */
230
+	public function __construct(EE_Admin_Page $admin_page, ?AdminListTableFilters $filters = null)
231
+	{
232
+		$this->request     = $this->request ?? LoaderFactory::getShared(RequestInterface::class);
233
+		$this->_admin_page = $admin_page;
234
+		// kept for back compat
235
+		$this->_req_data     = $this->_admin_page->get_request_data();
236
+		$this->_view         = $this->_admin_page->get_view();
237
+		$this->_views        = $this->_admin_page->get_list_table_view_RLs();
238
+		$this->_current_page = $this->get_pagenum();
239
+		$this->_screen       = $this->_admin_page->get_current_page() . '_' . $this->_admin_page->get_current_view();
240
+		$this->_yes_no       = [
241
+			esc_html__('No', 'event_espresso'),
242
+			esc_html__('Yes', 'event_espresso'),
243
+		];
244
+
245
+		$this->_per_page = $this->get_items_per_page($this->_screen . '_per_page');
246
+
247
+		$this->admin_list_table_filters = $filters instanceof AdminListTableFilters
248
+			? $filters
249
+			: LoaderFactory::getShared(AdminListTableFilters::class);
250
+
251
+		$this->_setup_data();
252
+		$this->_add_view_counts();
253
+		// ensure changes made to views in child classes get ported back to the admin page
254
+		$this->_admin_page->updateViews($this->_views);
255
+
256
+		$this->_nonce_action_ref = $this->_view;
257
+
258
+		$this->_set_properties();
259
+
260
+		// set primary column
261
+		add_filter('list_table_primary_column', [$this, 'set_primary_column']);
262
+
263
+		// set parent defaults
264
+		parent::__construct($this->_wp_list_args);
265
+
266
+		$this->prepare_items();
267
+	}
268
+
269
+
270
+	/**
271
+	 * _setup_data
272
+	 * this method is used to setup the $_data, $_all_data_count, and _per_page properties
273
+	 *
274
+	 * @return void
275
+	 * @uses $this->_admin_page
276
+	 */
277
+	abstract protected function _setup_data();
278
+
279
+
280
+	/**
281
+	 * set the properties that this class needs to be able to execute wp_list_table properly
282
+	 * properties set:
283
+	 * _wp_list_args = what the arguments required for the parent _wp_list_table.
284
+	 * _columns = set the columns in an array.
285
+	 * _sortable_columns = columns that are sortable (array).
286
+	 * _hidden_columns = columns that are hidden (array)
287
+	 * _default_orderby = the default orderby for sorting.
288
+	 *
289
+	 * @abstract
290
+	 * @access protected
291
+	 * @return void
292
+	 */
293
+	abstract protected function _set_properties();
294
+
295
+
296
+	/**
297
+	 * _get_table_filters
298
+	 * We use this to assemble and return any filters that are associated with this table that help further refine what
299
+	 * gets shown in the table.
300
+	 *
301
+	 * @abstract
302
+	 * @access protected
303
+	 * @return string[]
304
+	 */
305
+	abstract protected function _get_table_filters();
306
+
307
+
308
+	/**
309
+	 * this is a method that child class will do to add counts to the views array so when views are displayed the
310
+	 * counts of the views is accurate.
311
+	 *
312
+	 * @abstract
313
+	 * @access protected
314
+	 * @return void
315
+	 */
316
+	abstract protected function _add_view_counts();
317
+
318
+
319
+	/**
320
+	 * _get_hidden_fields
321
+	 * returns a html string of hidden fields so if any table filters are used the current view will be respected.
322
+	 *
323
+	 * @return string
324
+	 */
325
+	protected function _get_hidden_fields()
326
+	{
327
+		$page   = $this->request->getRequestParam('page', '');
328
+		$action = $this->request->getRequestParam('route', '');
329
+		$action = $this->request->getRequestParam('action', $action);
330
+		// if action is STILL empty, then we set it to default
331
+		$action = empty($action) || $action === '-1' ? 'default' : $action;
332
+		$field  = '<input type="hidden" name="page" value="' . esc_attr($page) . '" />' . "\n";
333
+		$field  .= '<input type="hidden" name="route" value="' . esc_attr($action) . '" />' . "\n";
334
+		$field  .= '<input type="hidden" name="perpage" value="' . esc_attr($this->_per_page) . '" />' . "\n";
335
+
336
+		$bulk_actions = $this->_get_bulk_actions();
337
+		foreach ($bulk_actions as $bulk_action => $label) {
338
+			$field .= '<input type="hidden" name="' . $bulk_action . '_nonce"'
339
+				. ' value="' . wp_create_nonce($bulk_action . '_nonce') . '" />' . "\n";
340
+		}
341
+
342
+		return $field;
343
+	}
344
+
345
+
346
+	/**
347
+	 * _set_column_info
348
+	 * we're using this to set the column headers property.
349
+	 *
350
+	 * @access protected
351
+	 * @return void
352
+	 */
353
+	protected function _set_column_info()
354
+	{
355
+		$columns   = $this->get_columns();
356
+		$hidden    = $this->get_hidden_columns();
357
+		$_sortable = $this->get_sortable_columns();
358
+
359
+		/**
360
+		 * Dynamic hook allowing for adding sortable columns in this list table.
361
+		 * Note that $this->screen->id is in the format
362
+		 * {sanitize_title($top_level_menu_label)}_page_{$espresso_admin_page_slug}.  So for the messages list
363
+		 * table it is: event-espresso_page_espresso_messages.
364
+		 * However, take note that if the top level menu label has been translated (i.e. "Event Espresso"). then the
365
+		 * hook prefix ("event-espresso") will be different.
366
+		 *
367
+		 * @var array $_sortable
368
+		 */
369
+		$_sortable = apply_filters(
370
+			"FHEE_manage_{$this->screen->id}_sortable_columns",
371
+			$_sortable,
372
+			$this->_screen,
373
+			$this
374
+		);
375
+
376
+		$sortable = [];
377
+		foreach ($_sortable as $id => $data) {
378
+			if (empty($data)) {
379
+				continue;
380
+			}
381
+			// fix for offset errors with WP_List_Table default get_columninfo()
382
+			if (is_array($data)) {
383
+				$_data[0] = key($data);
384
+				$_data[1] = $data[1] ?? false;
385
+			} else {
386
+				$_data[0] = $data;
387
+			}
388
+
389
+			$data = (array) $data;
390
+
391
+			if (! isset($data[1])) {
392
+				$_data[1] = false;
393
+			}
394
+
395
+			$sortable[ $id ] = $_data;
396
+		}
397
+		$primary               = $this->get_primary_column_name();
398
+		$this->_column_headers = [$columns, $hidden, $sortable, $primary];
399
+	}
400
+
401
+
402
+	/**
403
+	 * Added for WP4.1 backward compat (@see https://events.codebasehq.com/projects/event-espresso/tickets/8814)
404
+	 *
405
+	 * @return string
406
+	 */
407
+	protected function get_primary_column_name()
408
+	{
409
+		foreach (class_parents($this) as $parent) {
410
+			if ($parent === 'WP_List_Table' && method_exists($parent, 'get_primary_column_name')) {
411
+				return parent::get_primary_column_name();
412
+			}
413
+		}
414
+		return $this->_primary_column;
415
+	}
416
+
417
+
418
+	/**
419
+	 * Added for WP4.1 backward compat (@see https://events.codebasehq.com/projects/event-espresso/tickets/8814)
420
+	 *
421
+	 * @param EE_Base_Class $item
422
+	 * @param string        $column_name
423
+	 * @param string        $primary
424
+	 * @return string
425
+	 */
426
+	protected function handle_row_actions($item, $column_name, $primary)
427
+	{
428
+		foreach (class_parents($this) as $parent) {
429
+			if ($parent === 'WP_List_Table' && method_exists($parent, 'handle_row_actions')) {
430
+				return parent::handle_row_actions($item, $column_name, $primary);
431
+			}
432
+		}
433
+		return '';
434
+	}
435
+
436
+
437
+	/**
438
+	 * _get_bulk_actions
439
+	 * This is a wrapper called by WP_List_Table::get_bulk_actions()
440
+	 *
441
+	 * @access protected
442
+	 * @return array bulk_actions
443
+	 */
444
+	protected function _get_bulk_actions(): array
445
+	{
446
+		$actions = [];
447
+		// the _views property should have the bulk_actions, so let's go through and extract them into a properly
448
+		// formatted array for the wp_list_table();
449
+		foreach ($this->get_views() as $view => $args) {
450
+			if ($this->_view === $view && isset($args['bulk_action']) && is_array($args['bulk_action'])) {
451
+				// each bulk action will correspond with an admin page route, so we can check whatever the capability is
452
+				// for that page route and skip adding the bulk action if no access for the current logged-in user.
453
+				foreach ($args['bulk_action'] as $route => $label) {
454
+					if ($this->_admin_page->check_user_access($route, true)) {
455
+						$actions[ $route ] = $label;
456
+					}
457
+				}
458
+			}
459
+		}
460
+		return $actions;
461
+	}
462
+
463
+
464
+	/**
465
+	 * Generate the table navigation above or below the table.
466
+	 * Overrides the parent table nav in WP_List_Table so we can hide the bulk action div if there are no bulk actions.
467
+	 *
468
+	 * @throws EE_Error
469
+	 * @since 4.9.44.rc.001
470
+	 */
471
+	public function display_tablenav($which)
472
+	{
473
+		if ('top' === $which) {
474
+			wp_nonce_field('bulk-' . $this->_args['plural']);
475
+		}
476
+
477
+		ob_start();
478
+		$this->extra_tablenav($which);
479
+		$ee_tablenav = ob_get_clean();
480
+
481
+		ob_start();
482
+		$this->bulk_actions();
483
+		$bulkactions = ob_get_clean();
484
+
485
+		?>
486 486
     <?php if ($ee_tablenav && $which === 'top') : ?>
487 487
     <div class="tablenav <?php echo esc_attr($which); ?> ee-tablenav">
488 488
         <?php echo wp_kses($ee_tablenav, AllowedTags::getWithFormTags()); ?>
@@ -502,484 +502,484 @@  discard block
 block discarded – undo
502 502
     </div>
503 503
     <?php endif; ?>
504 504
         <?php
505
-        if ($which === 'top') {
506
-            echo wp_kses($this->_get_hidden_fields(), AllowedTags::getWithFormTags());
507
-        }
508
-    }
509
-
510
-
511
-    /**
512
-     * _filters
513
-     * This receives the filters array from children _get_table_filters() and assembles the string including the filter
514
-     * button.
515
-     *
516
-     * @access private
517
-     * @return void  echos html showing filters
518
-     */
519
-    private function _filters(): void
520
-    {
521
-        $classname = get_class($this);
522
-        $filters   = apply_filters(
523
-            "FHEE__{$classname}__filters",
524
-            $this->_get_table_filters(),
525
-            $this,
526
-            $this->_screen
527
-        );
528
-
529
-        if (empty($filters)) {
530
-            return;
531
-        }
532
-
533
-        $this->admin_list_table_filters->filters(
534
-            $filters,
535
-            $this->get_admin_page()->get_current_page_view_url()
536
-        );
537
-    }
538
-
539
-
540
-    /**
541
-     * Callback for 'list_table_primary_column' WordPress filter
542
-     * If child EE_Admin_List_Table classes set the _primary_column property then that will be set as the primary
543
-     * column when class is instantiated.
544
-     *
545
-     * @param string $column_name
546
-     * @return string
547
-     * @see WP_List_Table::get_primary_column_name
548
-     */
549
-    public function set_primary_column($column_name)
550
-    {
551
-        return ! empty($this->_primary_column) ? $this->_primary_column : $column_name;
552
-    }
553
-
554
-
555
-    /**
556
-     *
557
-     */
558
-    public function prepare_items()
559
-    {
560
-        $this->_set_column_info();
561
-        $this->process_bulk_action();
562
-
563
-        $this->items = $this->_data;
564
-        $this->set_pagination_args(
565
-            [
566
-                'total_items' => $this->_all_data_count,
567
-                'per_page'    => $this->_per_page,
568
-                'total_pages' => (int) ceil($this->_all_data_count / $this->_per_page),
569
-            ]
570
-        );
571
-    }
572
-
573
-
574
-    /**
575
-     * @param object|array $item
576
-     * @return string html content for the column
577
-     */
578
-    protected function column_cb($item)
579
-    {
580
-        return '';
581
-    }
582
-
583
-
584
-    /**
585
-     * This column is the default for when there is no defined column method for a registered column.
586
-     * This can be overridden by child classes, but allows for hooking in for custom columns.
587
-     *
588
-     * @param EE_Base_Class $item
589
-     * @param string        $column_name The column being called.
590
-     * @return string html content for the column
591
-     */
592
-    public function column_default($item, $column_name)
593
-    {
594
-        /**
595
-         * Dynamic hook allowing for adding additional column content in this list table.
596
-         * Note that $this->screen->id is in the format
597
-         * {sanitize_title($top_level_menu_label)}_page_{$espresso_admin_page_slug}.  So for the messages list
598
-         * table it is: event-espresso_page_espresso_messages.
599
-         * However, take note that if the top level menu label has been translated (i.e. "Event Espresso"). then the
600
-         * hook prefix ("event-espresso") will be different.
601
-         */
602
-        ob_start();
603
-        do_action(
604
-            'AHEE__EE_Admin_List_Table__column_' . $column_name . '__' . $this->screen->id,
605
-            $item,
606
-            $this->_screen
607
-        );
608
-        $content = ob_get_clean();
609
-        return $column_name === 'actions' ? $this->actionsModalMenu($content) : $content;
610
-    }
611
-
612
-
613
-    /**
614
-     * Get a list of columns. The format is:
615
-     * 'internal-name' => 'Title'
616
-     *
617
-     * @return array
618
-     * @since  3.1.0
619
-     * @access public
620
-     * @abstract
621
-     */
622
-    public function get_columns()
623
-    {
624
-        /**
625
-         * Dynamic hook allowing for adding additional columns in this list table.
626
-         * Note that $this->screen->id is in the format
627
-         * {sanitize_title($top_level_menu_label)}_page_{$espresso_admin_page_slug}.  So for the messages list
628
-         * table it is: event-espresso_page_espresso_messages.
629
-         * However, take note that if the top level menu label has been translated (i.e. "Event Espresso"). then the
630
-         * hook prefix ("event-espresso") will be different.
631
-         *
632
-         * @var array
633
-         */
634
-        return apply_filters("FHEE_manage_{$this->screen->id}_columns", $this->_columns, $this->_screen, $this);
635
-    }
636
-
637
-
638
-    /**
639
-     * Get an associative array ( id => link ) with the list
640
-     * of views available on this table.
641
-     *
642
-     * @return array
643
-     * @since  3.1.0
644
-     * @access protected
645
-     */
646
-    public function get_views()
647
-    {
648
-        return $this->_admin_page->get_views();
649
-    }
650
-
651
-
652
-    /**
653
-     * Generate the views html.
654
-     */
655
-    public function display_views()
656
-    {
657
-        $views = $this->get_views();
658
-
659
-        if (empty($views)) {
660
-            return;
661
-        }
662
-        $assembled_views = [];
663
-        foreach ($views as $view) {
664
-            $count = ! empty($view['count']) ? absint($view['count']) : 0;
665
-            if (isset($view['slug'], $view['class'], $view['url'], $view['label'])) {
666
-                $filter                           = "<li";
667
-                $filter                           .= $view['class'] ? " class='" . esc_attr($view['class']) . "'" : '';
668
-                $filter                           .= ">";
669
-                $filter                           .= '<a href="' . esc_url_raw($view['url']) . '">' . esc_html(
670
-                    $view['label']
671
-                ) . '</a>';
672
-                $filter                           .= '<span class="count">(' . $count . ')</span>';
673
-                $filter                           .= '</li>';
674
-                $assembled_views[ $view['slug'] ] = $filter;
675
-            }
676
-        }
677
-        $assembled_views = ! empty($assembled_views)
678
-            ? implode("<li style='margin:0 .5rem;'>|</li>", $assembled_views)
679
-            : '';
680
-        echo "
505
+		if ($which === 'top') {
506
+			echo wp_kses($this->_get_hidden_fields(), AllowedTags::getWithFormTags());
507
+		}
508
+	}
509
+
510
+
511
+	/**
512
+	 * _filters
513
+	 * This receives the filters array from children _get_table_filters() and assembles the string including the filter
514
+	 * button.
515
+	 *
516
+	 * @access private
517
+	 * @return void  echos html showing filters
518
+	 */
519
+	private function _filters(): void
520
+	{
521
+		$classname = get_class($this);
522
+		$filters   = apply_filters(
523
+			"FHEE__{$classname}__filters",
524
+			$this->_get_table_filters(),
525
+			$this,
526
+			$this->_screen
527
+		);
528
+
529
+		if (empty($filters)) {
530
+			return;
531
+		}
532
+
533
+		$this->admin_list_table_filters->filters(
534
+			$filters,
535
+			$this->get_admin_page()->get_current_page_view_url()
536
+		);
537
+	}
538
+
539
+
540
+	/**
541
+	 * Callback for 'list_table_primary_column' WordPress filter
542
+	 * If child EE_Admin_List_Table classes set the _primary_column property then that will be set as the primary
543
+	 * column when class is instantiated.
544
+	 *
545
+	 * @param string $column_name
546
+	 * @return string
547
+	 * @see WP_List_Table::get_primary_column_name
548
+	 */
549
+	public function set_primary_column($column_name)
550
+	{
551
+		return ! empty($this->_primary_column) ? $this->_primary_column : $column_name;
552
+	}
553
+
554
+
555
+	/**
556
+	 *
557
+	 */
558
+	public function prepare_items()
559
+	{
560
+		$this->_set_column_info();
561
+		$this->process_bulk_action();
562
+
563
+		$this->items = $this->_data;
564
+		$this->set_pagination_args(
565
+			[
566
+				'total_items' => $this->_all_data_count,
567
+				'per_page'    => $this->_per_page,
568
+				'total_pages' => (int) ceil($this->_all_data_count / $this->_per_page),
569
+			]
570
+		);
571
+	}
572
+
573
+
574
+	/**
575
+	 * @param object|array $item
576
+	 * @return string html content for the column
577
+	 */
578
+	protected function column_cb($item)
579
+	{
580
+		return '';
581
+	}
582
+
583
+
584
+	/**
585
+	 * This column is the default for when there is no defined column method for a registered column.
586
+	 * This can be overridden by child classes, but allows for hooking in for custom columns.
587
+	 *
588
+	 * @param EE_Base_Class $item
589
+	 * @param string        $column_name The column being called.
590
+	 * @return string html content for the column
591
+	 */
592
+	public function column_default($item, $column_name)
593
+	{
594
+		/**
595
+		 * Dynamic hook allowing for adding additional column content in this list table.
596
+		 * Note that $this->screen->id is in the format
597
+		 * {sanitize_title($top_level_menu_label)}_page_{$espresso_admin_page_slug}.  So for the messages list
598
+		 * table it is: event-espresso_page_espresso_messages.
599
+		 * However, take note that if the top level menu label has been translated (i.e. "Event Espresso"). then the
600
+		 * hook prefix ("event-espresso") will be different.
601
+		 */
602
+		ob_start();
603
+		do_action(
604
+			'AHEE__EE_Admin_List_Table__column_' . $column_name . '__' . $this->screen->id,
605
+			$item,
606
+			$this->_screen
607
+		);
608
+		$content = ob_get_clean();
609
+		return $column_name === 'actions' ? $this->actionsModalMenu($content) : $content;
610
+	}
611
+
612
+
613
+	/**
614
+	 * Get a list of columns. The format is:
615
+	 * 'internal-name' => 'Title'
616
+	 *
617
+	 * @return array
618
+	 * @since  3.1.0
619
+	 * @access public
620
+	 * @abstract
621
+	 */
622
+	public function get_columns()
623
+	{
624
+		/**
625
+		 * Dynamic hook allowing for adding additional columns in this list table.
626
+		 * Note that $this->screen->id is in the format
627
+		 * {sanitize_title($top_level_menu_label)}_page_{$espresso_admin_page_slug}.  So for the messages list
628
+		 * table it is: event-espresso_page_espresso_messages.
629
+		 * However, take note that if the top level menu label has been translated (i.e. "Event Espresso"). then the
630
+		 * hook prefix ("event-espresso") will be different.
631
+		 *
632
+		 * @var array
633
+		 */
634
+		return apply_filters("FHEE_manage_{$this->screen->id}_columns", $this->_columns, $this->_screen, $this);
635
+	}
636
+
637
+
638
+	/**
639
+	 * Get an associative array ( id => link ) with the list
640
+	 * of views available on this table.
641
+	 *
642
+	 * @return array
643
+	 * @since  3.1.0
644
+	 * @access protected
645
+	 */
646
+	public function get_views()
647
+	{
648
+		return $this->_admin_page->get_views();
649
+	}
650
+
651
+
652
+	/**
653
+	 * Generate the views html.
654
+	 */
655
+	public function display_views()
656
+	{
657
+		$views = $this->get_views();
658
+
659
+		if (empty($views)) {
660
+			return;
661
+		}
662
+		$assembled_views = [];
663
+		foreach ($views as $view) {
664
+			$count = ! empty($view['count']) ? absint($view['count']) : 0;
665
+			if (isset($view['slug'], $view['class'], $view['url'], $view['label'])) {
666
+				$filter                           = "<li";
667
+				$filter                           .= $view['class'] ? " class='" . esc_attr($view['class']) . "'" : '';
668
+				$filter                           .= ">";
669
+				$filter                           .= '<a href="' . esc_url_raw($view['url']) . '">' . esc_html(
670
+					$view['label']
671
+				) . '</a>';
672
+				$filter                           .= '<span class="count">(' . $count . ')</span>';
673
+				$filter                           .= '</li>';
674
+				$assembled_views[ $view['slug'] ] = $filter;
675
+			}
676
+		}
677
+		$assembled_views = ! empty($assembled_views)
678
+			? implode("<li style='margin:0 .5rem;'>|</li>", $assembled_views)
679
+			: '';
680
+		echo "
681 681
         <ul class='subsubsub'>
682 682
             $assembled_views
683 683
         </ul>";
684
-    }
685
-
686
-
687
-    /**
688
-     * Generates content for a single row of the table
689
-     *
690
-     * @param EE_Base_Class $item The current item
691
-     * @since  4.1
692
-     * @access public
693
-     */
694
-    public function single_row($item)
695
-    {
696
-        echo '<tr class="' . esc_attr($this->_get_row_class($item)) . '">';
697
-        $this->single_row_columns($item); // already escaped
698
-        echo '</tr>';
699
-    }
700
-
701
-
702
-    /**
703
-     * This simply sets up the row class for the table rows.
704
-     * Allows for easier overriding of child methods for setting up sorting.
705
-     *
706
-     * @param EE_Base_Class $item the current item
707
-     * @return string
708
-     */
709
-    protected function _get_row_class($item)
710
-    {
711
-        static $row_class = '';
712
-        $row_class = ($row_class === '' ? 'alternate' : '');
713
-
714
-        $new_row_class = $row_class;
715
-
716
-        if (! empty($this->_ajax_sorting_callback)) {
717
-            $new_row_class .= ' rowsortable';
718
-        }
719
-
720
-        return $new_row_class;
721
-    }
722
-
723
-
724
-    /**
725
-     * @return array
726
-     */
727
-    public function get_sortable_columns()
728
-    {
729
-        return (array) $this->_sortable_columns;
730
-    }
731
-
732
-
733
-    /**
734
-     * @return string
735
-     */
736
-    public function get_ajax_sorting_callback()
737
-    {
738
-        return $this->_ajax_sorting_callback;
739
-    }
740
-
741
-
742
-    /**
743
-     * @return array
744
-     */
745
-    public function get_hidden_columns(): array
746
-    {
747
-        $user_id     = get_current_user_id();
748
-        $has_default = get_user_option('default' . $this->screen->id . 'columnshidden', $user_id);
749
-        if (empty($has_default) && ! empty($this->_hidden_columns)) {
750
-            update_user_option($user_id, 'default' . $this->screen->id . 'columnshidden', true);
751
-            update_user_option($user_id, 'manage' . $this->screen->id . 'columnshidden', $this->_hidden_columns, true);
752
-        }
753
-        $ref = 'manage' . $this->screen->id . 'columnshidden';
754
-        return (array) get_user_option($ref, $user_id);
755
-    }
756
-
757
-
758
-    /**
759
-     * Generates the columns for a single row of the table.
760
-     * Overridden from wp_list_table so as to allow us to filter the column content for a given
761
-     * column.
762
-     *
763
-     * @param EE_Base_Class $item The current item
764
-     * @since 3.1.0
765
-     */
766
-    public function single_row_columns($item)
767
-    {
768
-        [$columns, $hidden, $sortable, $primary] = $this->get_column_info();
769
-
770
-        foreach ($columns as $column_name => $column_display_name) {
771
-            /**
772
-             * With WordPress version 4.3.RC+ WordPress started using the hidden css class to control whether columns
773
-             * are hidden or not instead of using "display:none;".  This bit of code provides backward compat.
774
-             */
775
-            $hidden_class = in_array($column_name, $hidden) ? ' hidden' : '';
776
-
777
-            $classes = $column_name . ' column-' . $column_name . $hidden_class;
778
-            if ($primary === $column_name) {
779
-                $classes .= ' has-row-actions column-primary';
780
-            }
781
-
782
-            $data = ' data-colname="' . wp_strip_all_tags($column_display_name) . '"';
783
-
784
-            $class = 'class="' . esc_attr($classes) . '"';
785
-
786
-            $attributes = "$class$data";
787
-
788
-            if ($column_name === 'cb') {
789
-                echo '<th scope="row" class="check-column">';
790
-                echo apply_filters(
791
-                    'FHEE__EE_Admin_List_Table__single_row_columns__column_cb_content',
792
-                    $this->column_cb($item), // already escaped
793
-                    $item,
794
-                    $this
795
-                );
796
-                echo '</th>';
797
-            } elseif (method_exists($this, "column_$column_name")) {
798
-                echo "<td $attributes>"; // already escaped
799
-                echo apply_filters(
800
-                    'FHEE__EE_Admin_List_Table__single_row_columns__column_' . $column_name . '__column_content',
801
-                    call_user_func([$this, "column_$column_name"], $item),
802
-                    $item,
803
-                    $this
804
-                );
805
-                echo wp_kses($this->handle_row_actions($item, $column_name, $primary), AllowedTags::getWithFormTags());
806
-                echo "</td>";
807
-            } else {
808
-                echo "<td $attributes>"; // already escaped
809
-                echo apply_filters(
810
-                    'FHEE__EE_Admin_List_Table__single_row_columns__column_default__column_content',
811
-                    $this->column_default($item, $column_name),
812
-                    $item,
813
-                    $column_name,
814
-                    $this
815
-                );
816
-                echo wp_kses($this->handle_row_actions($item, $column_name, $primary), AllowedTags::getWithFormTags());
817
-                echo "</td>";
818
-            }
819
-        }
820
-    }
821
-
822
-
823
-    /**
824
-     * Extra controls to be displayed between bulk actions and pagination
825
-     *
826
-     * @access public
827
-     * @param string $which
828
-     * @throws EE_Error
829
-     */
830
-    public function extra_tablenav($which)
831
-    {
832
-        if ($which === 'top') {
833
-            $this->_filters();
834
-        } elseif ($this->_bottom_buttons) {
835
-            echo '<div class="list-table-bottom-buttons alignleft actions">';
836
-            foreach ($this->_bottom_buttons as $type => $action) {
837
-                $route         = $action['route'] ?? '';
838
-                $extra_request = $action['extra_request'] ?? [];
839
-                $btn_class     = $action['btn_class'] ?? 'button button--secondary';
840
-                // already escaped
841
-                echo wp_kses(
842
-                    $this->_admin_page->get_action_link_or_button(
843
-                        $route,
844
-                        $type,
845
-                        $extra_request,
846
-                        $btn_class
847
-                    ),
848
-                    AllowedTags::getWithFormTags()
849
-                );
850
-            }
851
-            do_action('AHEE__EE_Admin_List_Table__extra_tablenav__after_bottom_buttons', $this, $this->_screen);
852
-            echo '</div>';
853
-        }
854
-    }
855
-
856
-
857
-    /**
858
-     * Get an associative array ( option_name => option_title ) with the list
859
-     * of bulk actions available on this table.
860
-     *
861
-     * @return array
862
-     * @since  3.1.0
863
-     * @access protected
864
-     */
865
-    public function get_bulk_actions()
866
-    {
867
-        return (array) $this->_get_bulk_actions();
868
-    }
869
-
870
-
871
-    /**
872
-     * Processing bulk actions.
873
-     */
874
-    public function process_bulk_action()
875
-    {
876
-        // this is not used it is handled by the child EE_Admin_Page class (routes).  However, including here for
877
-        // reference in case there is a case where it gets used.
878
-    }
879
-
880
-
881
-    /**
882
-     * returns the EE admin page this list table is associated with
883
-     *
884
-     * @return EE_Admin_Page
885
-     */
886
-    public function get_admin_page()
887
-    {
888
-        return $this->_admin_page;
889
-    }
890
-
891
-
892
-    /**
893
-     * A "helper" function for all children to provide an html string of
894
-     * actions to output in their content.  It is preferable for child classes
895
-     * to use this method for generating their actions content so that it's
896
-     * filterable by plugins
897
-     *
898
-     * @param string        $action_container           what are the html container
899
-     *                                                  elements for this actions string?
900
-     * @param string        $action_class               What class is for the container
901
-     *                                                  element.
902
-     * @param string        $action_items               The contents for the action items
903
-     *                                                  container.  This is filtered before
904
-     *                                                  returned.
905
-     * @param string        $action_id                  What id (optional) is used for the
906
-     *                                                  container element.
907
-     * @param EE_Base_Class $item                       The object for the column displaying
908
-     *                                                  the actions.
909
-     * @return string The assembled action elements container.
910
-     */
911
-    protected function _action_string(
912
-        $action_items,
913
-        $item,
914
-        $action_container = 'ul',
915
-        $action_class = '',
916
-        $action_id = ''
917
-    ) {
918
-        $action_class = ! empty($action_class) ? ' class="' . esc_attr($action_class) . '"' : '';
919
-        $action_id    = ! empty($action_id) ? ' id="' . esc_attr($action_id) . '"' : '';
920
-        $open_tag     = ! empty($action_container) ? '<' . $action_container . $action_class . $action_id . '>' : '';
921
-        $close_tag    = ! empty($action_container) ? '</' . $action_container . '>' : '';
922
-        try {
923
-            $content = apply_filters(
924
-                'FHEE__EE_Admin_List_Table___action_string__action_items',
925
-                $action_items,
926
-                $item,
927
-                $this
928
-            );
929
-        } catch (Exception $e) {
930
-            if (WP_DEBUG) {
931
-                EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
932
-            }
933
-            $content = $action_items;
934
-        }
935
-        return "$open_tag$content$close_tag";
936
-    }
937
-
938
-
939
-    /**
940
-     * @return string
941
-     */
942
-    protected function getReturnUrl()
943
-    {
944
-        $host = $this->_admin_page->get_request()->getServerParam('HTTP_HOST');
945
-        $uri  = $this->_admin_page->get_request()->getServerParam('REQUEST_URI');
946
-        return urlencode(esc_url_raw("//$host$uri"));
947
-    }
948
-
949
-
950
-    /**
951
-     * @param string $id
952
-     * @param string $content
953
-     * @param string $align  start (default), center, end
954
-     * @param string $layout row (default) or stack
955
-     * @return string
956
-     * @since   5.0.0.p
957
-     */
958
-    protected function columnContent(
959
-        string $id,
960
-        string $content,
961
-        string $align = 'start',
962
-        string $layout = 'row'
963
-    ): string {
964
-        if (! isset($this->_columns[ $id ])) {
965
-            throw new DomainException('missing column id');
966
-        }
967
-        $heading = $id !== 'cb' ? $this->_columns[ $id ] : '';
968
-        $align   = in_array($align, ['start', 'center', 'end']) ? $align : 'start';
969
-        $align   = "ee-responsive-table-cell--$align";
970
-        $layout  = $layout === 'row' ? 'ee-layout-row' : 'ee-layout-stack';
971
-
972
-        $html = "<div class='ee-responsive-table-cell ee-responsive-table-cell--column-$id $align $layout'>";
973
-        $html .= "<div class='ee-responsive-table-cell__heading'>$heading</div>";
974
-        $html .= "<div class='ee-responsive-table-cell__content $layout'>$content</div>";
975
-        $html .= "</div>";
976
-        return $html;
977
-    }
978
-
979
-
980
-    protected function actionsModalMenu($actions): string
981
-    {
982
-        return '
684
+	}
685
+
686
+
687
+	/**
688
+	 * Generates content for a single row of the table
689
+	 *
690
+	 * @param EE_Base_Class $item The current item
691
+	 * @since  4.1
692
+	 * @access public
693
+	 */
694
+	public function single_row($item)
695
+	{
696
+		echo '<tr class="' . esc_attr($this->_get_row_class($item)) . '">';
697
+		$this->single_row_columns($item); // already escaped
698
+		echo '</tr>';
699
+	}
700
+
701
+
702
+	/**
703
+	 * This simply sets up the row class for the table rows.
704
+	 * Allows for easier overriding of child methods for setting up sorting.
705
+	 *
706
+	 * @param EE_Base_Class $item the current item
707
+	 * @return string
708
+	 */
709
+	protected function _get_row_class($item)
710
+	{
711
+		static $row_class = '';
712
+		$row_class = ($row_class === '' ? 'alternate' : '');
713
+
714
+		$new_row_class = $row_class;
715
+
716
+		if (! empty($this->_ajax_sorting_callback)) {
717
+			$new_row_class .= ' rowsortable';
718
+		}
719
+
720
+		return $new_row_class;
721
+	}
722
+
723
+
724
+	/**
725
+	 * @return array
726
+	 */
727
+	public function get_sortable_columns()
728
+	{
729
+		return (array) $this->_sortable_columns;
730
+	}
731
+
732
+
733
+	/**
734
+	 * @return string
735
+	 */
736
+	public function get_ajax_sorting_callback()
737
+	{
738
+		return $this->_ajax_sorting_callback;
739
+	}
740
+
741
+
742
+	/**
743
+	 * @return array
744
+	 */
745
+	public function get_hidden_columns(): array
746
+	{
747
+		$user_id     = get_current_user_id();
748
+		$has_default = get_user_option('default' . $this->screen->id . 'columnshidden', $user_id);
749
+		if (empty($has_default) && ! empty($this->_hidden_columns)) {
750
+			update_user_option($user_id, 'default' . $this->screen->id . 'columnshidden', true);
751
+			update_user_option($user_id, 'manage' . $this->screen->id . 'columnshidden', $this->_hidden_columns, true);
752
+		}
753
+		$ref = 'manage' . $this->screen->id . 'columnshidden';
754
+		return (array) get_user_option($ref, $user_id);
755
+	}
756
+
757
+
758
+	/**
759
+	 * Generates the columns for a single row of the table.
760
+	 * Overridden from wp_list_table so as to allow us to filter the column content for a given
761
+	 * column.
762
+	 *
763
+	 * @param EE_Base_Class $item The current item
764
+	 * @since 3.1.0
765
+	 */
766
+	public function single_row_columns($item)
767
+	{
768
+		[$columns, $hidden, $sortable, $primary] = $this->get_column_info();
769
+
770
+		foreach ($columns as $column_name => $column_display_name) {
771
+			/**
772
+			 * With WordPress version 4.3.RC+ WordPress started using the hidden css class to control whether columns
773
+			 * are hidden or not instead of using "display:none;".  This bit of code provides backward compat.
774
+			 */
775
+			$hidden_class = in_array($column_name, $hidden) ? ' hidden' : '';
776
+
777
+			$classes = $column_name . ' column-' . $column_name . $hidden_class;
778
+			if ($primary === $column_name) {
779
+				$classes .= ' has-row-actions column-primary';
780
+			}
781
+
782
+			$data = ' data-colname="' . wp_strip_all_tags($column_display_name) . '"';
783
+
784
+			$class = 'class="' . esc_attr($classes) . '"';
785
+
786
+			$attributes = "$class$data";
787
+
788
+			if ($column_name === 'cb') {
789
+				echo '<th scope="row" class="check-column">';
790
+				echo apply_filters(
791
+					'FHEE__EE_Admin_List_Table__single_row_columns__column_cb_content',
792
+					$this->column_cb($item), // already escaped
793
+					$item,
794
+					$this
795
+				);
796
+				echo '</th>';
797
+			} elseif (method_exists($this, "column_$column_name")) {
798
+				echo "<td $attributes>"; // already escaped
799
+				echo apply_filters(
800
+					'FHEE__EE_Admin_List_Table__single_row_columns__column_' . $column_name . '__column_content',
801
+					call_user_func([$this, "column_$column_name"], $item),
802
+					$item,
803
+					$this
804
+				);
805
+				echo wp_kses($this->handle_row_actions($item, $column_name, $primary), AllowedTags::getWithFormTags());
806
+				echo "</td>";
807
+			} else {
808
+				echo "<td $attributes>"; // already escaped
809
+				echo apply_filters(
810
+					'FHEE__EE_Admin_List_Table__single_row_columns__column_default__column_content',
811
+					$this->column_default($item, $column_name),
812
+					$item,
813
+					$column_name,
814
+					$this
815
+				);
816
+				echo wp_kses($this->handle_row_actions($item, $column_name, $primary), AllowedTags::getWithFormTags());
817
+				echo "</td>";
818
+			}
819
+		}
820
+	}
821
+
822
+
823
+	/**
824
+	 * Extra controls to be displayed between bulk actions and pagination
825
+	 *
826
+	 * @access public
827
+	 * @param string $which
828
+	 * @throws EE_Error
829
+	 */
830
+	public function extra_tablenav($which)
831
+	{
832
+		if ($which === 'top') {
833
+			$this->_filters();
834
+		} elseif ($this->_bottom_buttons) {
835
+			echo '<div class="list-table-bottom-buttons alignleft actions">';
836
+			foreach ($this->_bottom_buttons as $type => $action) {
837
+				$route         = $action['route'] ?? '';
838
+				$extra_request = $action['extra_request'] ?? [];
839
+				$btn_class     = $action['btn_class'] ?? 'button button--secondary';
840
+				// already escaped
841
+				echo wp_kses(
842
+					$this->_admin_page->get_action_link_or_button(
843
+						$route,
844
+						$type,
845
+						$extra_request,
846
+						$btn_class
847
+					),
848
+					AllowedTags::getWithFormTags()
849
+				);
850
+			}
851
+			do_action('AHEE__EE_Admin_List_Table__extra_tablenav__after_bottom_buttons', $this, $this->_screen);
852
+			echo '</div>';
853
+		}
854
+	}
855
+
856
+
857
+	/**
858
+	 * Get an associative array ( option_name => option_title ) with the list
859
+	 * of bulk actions available on this table.
860
+	 *
861
+	 * @return array
862
+	 * @since  3.1.0
863
+	 * @access protected
864
+	 */
865
+	public function get_bulk_actions()
866
+	{
867
+		return (array) $this->_get_bulk_actions();
868
+	}
869
+
870
+
871
+	/**
872
+	 * Processing bulk actions.
873
+	 */
874
+	public function process_bulk_action()
875
+	{
876
+		// this is not used it is handled by the child EE_Admin_Page class (routes).  However, including here for
877
+		// reference in case there is a case where it gets used.
878
+	}
879
+
880
+
881
+	/**
882
+	 * returns the EE admin page this list table is associated with
883
+	 *
884
+	 * @return EE_Admin_Page
885
+	 */
886
+	public function get_admin_page()
887
+	{
888
+		return $this->_admin_page;
889
+	}
890
+
891
+
892
+	/**
893
+	 * A "helper" function for all children to provide an html string of
894
+	 * actions to output in their content.  It is preferable for child classes
895
+	 * to use this method for generating their actions content so that it's
896
+	 * filterable by plugins
897
+	 *
898
+	 * @param string        $action_container           what are the html container
899
+	 *                                                  elements for this actions string?
900
+	 * @param string        $action_class               What class is for the container
901
+	 *                                                  element.
902
+	 * @param string        $action_items               The contents for the action items
903
+	 *                                                  container.  This is filtered before
904
+	 *                                                  returned.
905
+	 * @param string        $action_id                  What id (optional) is used for the
906
+	 *                                                  container element.
907
+	 * @param EE_Base_Class $item                       The object for the column displaying
908
+	 *                                                  the actions.
909
+	 * @return string The assembled action elements container.
910
+	 */
911
+	protected function _action_string(
912
+		$action_items,
913
+		$item,
914
+		$action_container = 'ul',
915
+		$action_class = '',
916
+		$action_id = ''
917
+	) {
918
+		$action_class = ! empty($action_class) ? ' class="' . esc_attr($action_class) . '"' : '';
919
+		$action_id    = ! empty($action_id) ? ' id="' . esc_attr($action_id) . '"' : '';
920
+		$open_tag     = ! empty($action_container) ? '<' . $action_container . $action_class . $action_id . '>' : '';
921
+		$close_tag    = ! empty($action_container) ? '</' . $action_container . '>' : '';
922
+		try {
923
+			$content = apply_filters(
924
+				'FHEE__EE_Admin_List_Table___action_string__action_items',
925
+				$action_items,
926
+				$item,
927
+				$this
928
+			);
929
+		} catch (Exception $e) {
930
+			if (WP_DEBUG) {
931
+				EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
932
+			}
933
+			$content = $action_items;
934
+		}
935
+		return "$open_tag$content$close_tag";
936
+	}
937
+
938
+
939
+	/**
940
+	 * @return string
941
+	 */
942
+	protected function getReturnUrl()
943
+	{
944
+		$host = $this->_admin_page->get_request()->getServerParam('HTTP_HOST');
945
+		$uri  = $this->_admin_page->get_request()->getServerParam('REQUEST_URI');
946
+		return urlencode(esc_url_raw("//$host$uri"));
947
+	}
948
+
949
+
950
+	/**
951
+	 * @param string $id
952
+	 * @param string $content
953
+	 * @param string $align  start (default), center, end
954
+	 * @param string $layout row (default) or stack
955
+	 * @return string
956
+	 * @since   5.0.0.p
957
+	 */
958
+	protected function columnContent(
959
+		string $id,
960
+		string $content,
961
+		string $align = 'start',
962
+		string $layout = 'row'
963
+	): string {
964
+		if (! isset($this->_columns[ $id ])) {
965
+			throw new DomainException('missing column id');
966
+		}
967
+		$heading = $id !== 'cb' ? $this->_columns[ $id ] : '';
968
+		$align   = in_array($align, ['start', 'center', 'end']) ? $align : 'start';
969
+		$align   = "ee-responsive-table-cell--$align";
970
+		$layout  = $layout === 'row' ? 'ee-layout-row' : 'ee-layout-stack';
971
+
972
+		$html = "<div class='ee-responsive-table-cell ee-responsive-table-cell--column-$id $align $layout'>";
973
+		$html .= "<div class='ee-responsive-table-cell__heading'>$heading</div>";
974
+		$html .= "<div class='ee-responsive-table-cell__content $layout'>$content</div>";
975
+		$html .= "</div>";
976
+		return $html;
977
+	}
978
+
979
+
980
+	protected function actionsModalMenu($actions): string
981
+	{
982
+		return '
983 983
         <div class="ee-modal-menu">
984 984
             <button class="ee-modal-menu__button button button--secondary button--icon-only ee-aria-tooltip"
985 985
                     aria-label="' . esc_attr__('list table actions menu', 'event_espresso') . '"
@@ -991,44 +991,44 @@  discard block
 block discarded – undo
991 991
                 ' . $actions . '
992 992
             </div>
993 993
         </div>';
994
-    }
994
+	}
995 995
 
996 996
 
997
-    public function actionsColumnHeader(): string
998
-    {
999
-        return '
997
+	public function actionsColumnHeader(): string
998
+	{
999
+		return '
1000 1000
             <span class="ee-actions-column-header-wrap">
1001 1001
                 <span class="dashicons dashicons-screenoptions"></span>
1002 1002
                 <span class="ee-actions-column-header">' . esc_html__('Actions', 'event_espresso') . '</span>
1003 1003
             </span>';
1004
-    }
1005
-
1006
-
1007
-    protected function getActionLink(string $url, string $display_text, string $label, $class = ''): string
1008
-    {
1009
-        $class = ! empty($class) ? "$class ee-list-table-action" : 'ee-list-table-action';
1010
-        $class = ! empty($label) ? "$class ee-aria-tooltip" : $class;
1011
-        $label = ! empty($label) ? " aria-label='$label'" : '';
1012
-        return "<a href='$url' class='$class'$label>$display_text</a>";
1013
-    }
1014
-
1015
-
1016
-    /**
1017
-     * Override the search box method of WP List Table to include a reset button
1018
-     *
1019
-     * @param string $text     The 'submit' button label.
1020
-     * @param string $input_id ID attribute value for the search input field.
1021
-     */
1022
-    public function search_box($text, $input_id)
1023
-    {
1024
-        if (empty($_REQUEST['s']) && ! $this->has_items()) {
1025
-            return;
1026
-        }
1027
-
1028
-        $this->admin_list_table_filters->searchBox(
1029
-            $text,
1030
-            $input_id,
1031
-            $this->get_admin_page()->get_current_page_view_url()
1032
-        );
1033
-    }
1004
+	}
1005
+
1006
+
1007
+	protected function getActionLink(string $url, string $display_text, string $label, $class = ''): string
1008
+	{
1009
+		$class = ! empty($class) ? "$class ee-list-table-action" : 'ee-list-table-action';
1010
+		$class = ! empty($label) ? "$class ee-aria-tooltip" : $class;
1011
+		$label = ! empty($label) ? " aria-label='$label'" : '';
1012
+		return "<a href='$url' class='$class'$label>$display_text</a>";
1013
+	}
1014
+
1015
+
1016
+	/**
1017
+	 * Override the search box method of WP List Table to include a reset button
1018
+	 *
1019
+	 * @param string $text     The 'submit' button label.
1020
+	 * @param string $input_id ID attribute value for the search input field.
1021
+	 */
1022
+	public function search_box($text, $input_id)
1023
+	{
1024
+		if (empty($_REQUEST['s']) && ! $this->has_items()) {
1025
+			return;
1026
+		}
1027
+
1028
+		$this->admin_list_table_filters->searchBox(
1029
+			$text,
1030
+			$input_id,
1031
+			$this->get_admin_page()->get_current_page_view_url()
1032
+		);
1033
+	}
1034 1034
 }
Please login to merge, or discard this patch.
Spacing   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -5,8 +5,8 @@  discard block
 block discarded – undo
5 5
 use EventEspresso\core\services\request\RequestInterface;
6 6
 use EventEspresso\core\services\request\sanitizers\AllowedTags;
7 7
 
8
-if (! class_exists('WP_List_Table')) {
9
-    require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
8
+if ( ! class_exists('WP_List_Table')) {
9
+    require_once ABSPATH.'wp-admin/includes/class-wp-list-table.php';
10 10
 }
11 11
 
12 12
 
@@ -236,13 +236,13 @@  discard block
 block discarded – undo
236 236
         $this->_view         = $this->_admin_page->get_view();
237 237
         $this->_views        = $this->_admin_page->get_list_table_view_RLs();
238 238
         $this->_current_page = $this->get_pagenum();
239
-        $this->_screen       = $this->_admin_page->get_current_page() . '_' . $this->_admin_page->get_current_view();
239
+        $this->_screen       = $this->_admin_page->get_current_page().'_'.$this->_admin_page->get_current_view();
240 240
         $this->_yes_no       = [
241 241
             esc_html__('No', 'event_espresso'),
242 242
             esc_html__('Yes', 'event_espresso'),
243 243
         ];
244 244
 
245
-        $this->_per_page = $this->get_items_per_page($this->_screen . '_per_page');
245
+        $this->_per_page = $this->get_items_per_page($this->_screen.'_per_page');
246 246
 
247 247
         $this->admin_list_table_filters = $filters instanceof AdminListTableFilters
248 248
             ? $filters
@@ -329,14 +329,14 @@  discard block
 block discarded – undo
329 329
         $action = $this->request->getRequestParam('action', $action);
330 330
         // if action is STILL empty, then we set it to default
331 331
         $action = empty($action) || $action === '-1' ? 'default' : $action;
332
-        $field  = '<input type="hidden" name="page" value="' . esc_attr($page) . '" />' . "\n";
333
-        $field  .= '<input type="hidden" name="route" value="' . esc_attr($action) . '" />' . "\n";
334
-        $field  .= '<input type="hidden" name="perpage" value="' . esc_attr($this->_per_page) . '" />' . "\n";
332
+        $field  = '<input type="hidden" name="page" value="'.esc_attr($page).'" />'."\n";
333
+        $field  .= '<input type="hidden" name="route" value="'.esc_attr($action).'" />'."\n";
334
+        $field  .= '<input type="hidden" name="perpage" value="'.esc_attr($this->_per_page).'" />'."\n";
335 335
 
336 336
         $bulk_actions = $this->_get_bulk_actions();
337 337
         foreach ($bulk_actions as $bulk_action => $label) {
338
-            $field .= '<input type="hidden" name="' . $bulk_action . '_nonce"'
339
-                . ' value="' . wp_create_nonce($bulk_action . '_nonce') . '" />' . "\n";
338
+            $field .= '<input type="hidden" name="'.$bulk_action.'_nonce"'
339
+                . ' value="'.wp_create_nonce($bulk_action.'_nonce').'" />'."\n";
340 340
         }
341 341
 
342 342
         return $field;
@@ -388,11 +388,11 @@  discard block
 block discarded – undo
388 388
 
389 389
             $data = (array) $data;
390 390
 
391
-            if (! isset($data[1])) {
391
+            if ( ! isset($data[1])) {
392 392
                 $_data[1] = false;
393 393
             }
394 394
 
395
-            $sortable[ $id ] = $_data;
395
+            $sortable[$id] = $_data;
396 396
         }
397 397
         $primary               = $this->get_primary_column_name();
398 398
         $this->_column_headers = [$columns, $hidden, $sortable, $primary];
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
                 // for that page route and skip adding the bulk action if no access for the current logged-in user.
453 453
                 foreach ($args['bulk_action'] as $route => $label) {
454 454
                     if ($this->_admin_page->check_user_access($route, true)) {
455
-                        $actions[ $route ] = $label;
455
+                        $actions[$route] = $label;
456 456
                     }
457 457
                 }
458 458
             }
@@ -471,7 +471,7 @@  discard block
 block discarded – undo
471 471
     public function display_tablenav($which)
472 472
     {
473 473
         if ('top' === $which) {
474
-            wp_nonce_field('bulk-' . $this->_args['plural']);
474
+            wp_nonce_field('bulk-'.$this->_args['plural']);
475 475
         }
476 476
 
477 477
         ob_start();
@@ -601,7 +601,7 @@  discard block
 block discarded – undo
601 601
          */
602 602
         ob_start();
603 603
         do_action(
604
-            'AHEE__EE_Admin_List_Table__column_' . $column_name . '__' . $this->screen->id,
604
+            'AHEE__EE_Admin_List_Table__column_'.$column_name.'__'.$this->screen->id,
605 605
             $item,
606 606
             $this->_screen
607 607
         );
@@ -663,15 +663,15 @@  discard block
 block discarded – undo
663 663
         foreach ($views as $view) {
664 664
             $count = ! empty($view['count']) ? absint($view['count']) : 0;
665 665
             if (isset($view['slug'], $view['class'], $view['url'], $view['label'])) {
666
-                $filter                           = "<li";
667
-                $filter                           .= $view['class'] ? " class='" . esc_attr($view['class']) . "'" : '';
666
+                $filter = "<li";
667
+                $filter                           .= $view['class'] ? " class='".esc_attr($view['class'])."'" : '';
668 668
                 $filter                           .= ">";
669
-                $filter                           .= '<a href="' . esc_url_raw($view['url']) . '">' . esc_html(
669
+                $filter                           .= '<a href="'.esc_url_raw($view['url']).'">'.esc_html(
670 670
                     $view['label']
671
-                ) . '</a>';
672
-                $filter                           .= '<span class="count">(' . $count . ')</span>';
671
+                ).'</a>';
672
+                $filter                           .= '<span class="count">('.$count.')</span>';
673 673
                 $filter                           .= '</li>';
674
-                $assembled_views[ $view['slug'] ] = $filter;
674
+                $assembled_views[$view['slug']] = $filter;
675 675
             }
676 676
         }
677 677
         $assembled_views = ! empty($assembled_views)
@@ -693,7 +693,7 @@  discard block
 block discarded – undo
693 693
      */
694 694
     public function single_row($item)
695 695
     {
696
-        echo '<tr class="' . esc_attr($this->_get_row_class($item)) . '">';
696
+        echo '<tr class="'.esc_attr($this->_get_row_class($item)).'">';
697 697
         $this->single_row_columns($item); // already escaped
698 698
         echo '</tr>';
699 699
     }
@@ -713,7 +713,7 @@  discard block
 block discarded – undo
713 713
 
714 714
         $new_row_class = $row_class;
715 715
 
716
-        if (! empty($this->_ajax_sorting_callback)) {
716
+        if ( ! empty($this->_ajax_sorting_callback)) {
717 717
             $new_row_class .= ' rowsortable';
718 718
         }
719 719
 
@@ -745,12 +745,12 @@  discard block
 block discarded – undo
745 745
     public function get_hidden_columns(): array
746 746
     {
747 747
         $user_id     = get_current_user_id();
748
-        $has_default = get_user_option('default' . $this->screen->id . 'columnshidden', $user_id);
748
+        $has_default = get_user_option('default'.$this->screen->id.'columnshidden', $user_id);
749 749
         if (empty($has_default) && ! empty($this->_hidden_columns)) {
750
-            update_user_option($user_id, 'default' . $this->screen->id . 'columnshidden', true);
751
-            update_user_option($user_id, 'manage' . $this->screen->id . 'columnshidden', $this->_hidden_columns, true);
750
+            update_user_option($user_id, 'default'.$this->screen->id.'columnshidden', true);
751
+            update_user_option($user_id, 'manage'.$this->screen->id.'columnshidden', $this->_hidden_columns, true);
752 752
         }
753
-        $ref = 'manage' . $this->screen->id . 'columnshidden';
753
+        $ref = 'manage'.$this->screen->id.'columnshidden';
754 754
         return (array) get_user_option($ref, $user_id);
755 755
     }
756 756
 
@@ -774,14 +774,14 @@  discard block
 block discarded – undo
774 774
              */
775 775
             $hidden_class = in_array($column_name, $hidden) ? ' hidden' : '';
776 776
 
777
-            $classes = $column_name . ' column-' . $column_name . $hidden_class;
777
+            $classes = $column_name.' column-'.$column_name.$hidden_class;
778 778
             if ($primary === $column_name) {
779 779
                 $classes .= ' has-row-actions column-primary';
780 780
             }
781 781
 
782
-            $data = ' data-colname="' . wp_strip_all_tags($column_display_name) . '"';
782
+            $data = ' data-colname="'.wp_strip_all_tags($column_display_name).'"';
783 783
 
784
-            $class = 'class="' . esc_attr($classes) . '"';
784
+            $class = 'class="'.esc_attr($classes).'"';
785 785
 
786 786
             $attributes = "$class$data";
787 787
 
@@ -797,7 +797,7 @@  discard block
 block discarded – undo
797 797
             } elseif (method_exists($this, "column_$column_name")) {
798 798
                 echo "<td $attributes>"; // already escaped
799 799
                 echo apply_filters(
800
-                    'FHEE__EE_Admin_List_Table__single_row_columns__column_' . $column_name . '__column_content',
800
+                    'FHEE__EE_Admin_List_Table__single_row_columns__column_'.$column_name.'__column_content',
801 801
                     call_user_func([$this, "column_$column_name"], $item),
802 802
                     $item,
803 803
                     $this
@@ -915,10 +915,10 @@  discard block
 block discarded – undo
915 915
         $action_class = '',
916 916
         $action_id = ''
917 917
     ) {
918
-        $action_class = ! empty($action_class) ? ' class="' . esc_attr($action_class) . '"' : '';
919
-        $action_id    = ! empty($action_id) ? ' id="' . esc_attr($action_id) . '"' : '';
920
-        $open_tag     = ! empty($action_container) ? '<' . $action_container . $action_class . $action_id . '>' : '';
921
-        $close_tag    = ! empty($action_container) ? '</' . $action_container . '>' : '';
918
+        $action_class = ! empty($action_class) ? ' class="'.esc_attr($action_class).'"' : '';
919
+        $action_id    = ! empty($action_id) ? ' id="'.esc_attr($action_id).'"' : '';
920
+        $open_tag     = ! empty($action_container) ? '<'.$action_container.$action_class.$action_id.'>' : '';
921
+        $close_tag    = ! empty($action_container) ? '</'.$action_container.'>' : '';
922 922
         try {
923 923
             $content = apply_filters(
924 924
                 'FHEE__EE_Admin_List_Table___action_string__action_items',
@@ -961,10 +961,10 @@  discard block
 block discarded – undo
961 961
         string $align = 'start',
962 962
         string $layout = 'row'
963 963
     ): string {
964
-        if (! isset($this->_columns[ $id ])) {
964
+        if ( ! isset($this->_columns[$id])) {
965 965
             throw new DomainException('missing column id');
966 966
         }
967
-        $heading = $id !== 'cb' ? $this->_columns[ $id ] : '';
967
+        $heading = $id !== 'cb' ? $this->_columns[$id] : '';
968 968
         $align   = in_array($align, ['start', 'center', 'end']) ? $align : 'start';
969 969
         $align   = "ee-responsive-table-cell--$align";
970 970
         $layout  = $layout === 'row' ? 'ee-layout-row' : 'ee-layout-stack';
@@ -982,13 +982,13 @@  discard block
 block discarded – undo
982 982
         return '
983 983
         <div class="ee-modal-menu">
984 984
             <button class="ee-modal-menu__button button button--secondary button--icon-only ee-aria-tooltip"
985
-                    aria-label="' . esc_attr__('list table actions menu', 'event_espresso') . '"
985
+                    aria-label="' . esc_attr__('list table actions menu', 'event_espresso').'"
986 986
             >
987 987
                 <span class="dashicons dashicons-menu"></span>
988 988
             </button>
989 989
             <div class="ee-modal-menu__content ee-admin-container">
990 990
                 <span class="ee-modal-menu__close dashicons dashicons-no"></span>
991
-                ' . $actions . '
991
+                ' . $actions.'
992 992
             </div>
993 993
         </div>';
994 994
     }
@@ -999,7 +999,7 @@  discard block
 block discarded – undo
999 999
         return '
1000 1000
             <span class="ee-actions-column-header-wrap">
1001 1001
                 <span class="dashicons dashicons-screenoptions"></span>
1002
-                <span class="ee-actions-column-header">' . esc_html__('Actions', 'event_espresso') . '</span>
1002
+                <span class="ee-actions-column-header">' . esc_html__('Actions', 'event_espresso').'</span>
1003 1003
             </span>';
1004 1004
     }
1005 1005
 
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page.core.php 2 patches
Indentation   +4235 added lines, -4235 removed lines patch added patch discarded remove patch
@@ -24,4331 +24,4331 @@
 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_Capabilities $capabilities       = null;
33
+	protected ?EE_Capabilities $capabilities       = null;
34 34
 
35
-    protected ?EE_Registry $EE                 = null;
35
+	protected ?EE_Registry $EE                 = null;
36 36
 
37
-    protected ?FeatureFlags $feature            = null;
37
+	protected ?FeatureFlags $feature            = null;
38 38
 
39
-    protected ?LoaderInterface $loader             = null;
39
+	protected ?LoaderInterface $loader             = null;
40 40
 
41
-    protected ?RequestInterface $request            = null;
41
+	protected ?RequestInterface $request            = null;
42 42
 
43
-    protected ?WP_Screen $_current_screen    = null;
44
-
45
-    /**
46
-     * @var array
47
-     * @since 5.0.0.p
48
-     */
49
-    private array $publish_post_meta_box_hidden_fields = [];
50
-
51
-    /**
52
-     * some default things shared by all child classes
53
-     *
54
-     * @var string[]
55
-     */
56
-    protected array $_default_espresso_metaboxes = [
57
-        '_espresso_news_post_box',
58
-        '_espresso_links_post_box',
59
-        '_espresso_ratings_request',
60
-        '_espresso_sponsors_post_box',
61
-    ];
62
-
63
-    /**
64
-     * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
65
-     * actions.
66
-     *
67
-     * @since 4.6.x
68
-     */
69
-    protected array $_default_route_query_args = [];
70
-
71
-    protected array $_labels                   = [];
72
-
73
-    protected array $_nav_tabs                 = [];
74
-
75
-    protected array $_page_config              = [];
76
-
77
-    /**
78
-     * action => method pairs used for routing incoming requests
79
-     *
80
-     * @var array
81
-     */
82
-    protected array $_page_routes   = [];
83
-
84
-    protected array $_req_data      = [];
85
-
86
-    protected array $_route_config  = [];
87
-
88
-    protected array $_template_args = [];
89
-
90
-    protected array $_views         = [];
91
-
92
-    /**
93
-     * yes / no array for admin form fields
94
-     *
95
-     * @var array|array[]
96
-     */
97
-    protected array $_yes_no_values = [];
98
-
99
-    /**
100
-     * this starts at null so we can have no header routes progress through two states.
101
-     */
102
-    protected ?bool $_is_UI_request = null;
103
-
104
-    /**
105
-     * flags whether the given route is a caffeinated route or not.
106
-     */
107
-    protected bool $_is_caf        = false;
108
-
109
-    protected bool $_routing       = false;
110
-
111
-    /**
112
-     * whether initializePage() has run
113
-     *
114
-     * @var bool
115
-     */
116
-    protected bool $initialized = false;
117
-
118
-
119
-    protected string $_admin_base_path      = '';
120
-
121
-    protected string $_admin_base_url       = '';
122
-
123
-    protected string $_admin_page_title     = '';
124
-
125
-    protected string $_column_template_path = '';
126
-
127
-    protected bool $_cpt_route            = false;
128
-
129
-    /**
130
-     * set via request page and action args.
131
-     */
132
-    protected string $_current_page          = '';
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
-    protected string $_req_action = '';
144
-
145
-    /**
146
-     * sanitized request action nonce
147
-     */
148
-    protected string $_req_nonce        = '';
149
-
150
-    protected string $_search_btn_label = '';
151
-
152
-    protected string $_template_path    = '';
153
-
154
-    protected string $_view             = '';
155
-
156
-    /**
157
-     * set early within EE_Admin_Init
158
-     *
159
-     * @var string
160
-     */
161
-    protected string $_wp_page_slug = '';
162
-
163
-    /**
164
-     * if the current class is an admin page extension, like: Extend_Events_Admin_Page,
165
-     * then this would be the parent classname: Events_Admin_Page
166
-     *
167
-     * @var string
168
-     */
169
-    public string $base_class_name = '';
170
-
171
-    public string $class_name      = '';
172
-
173
-    /**
174
-     * unprocessed value for the 'action' request param (default '')
175
-     *
176
-     * @var string
177
-     */
178
-    protected string $raw_req_action = '';
179
-
180
-    /**
181
-     * unprocessed value for the 'page' request param (default '')
182
-     *
183
-     * @var string
184
-     */
185
-    protected string $raw_req_page = '';
186
-
187
-    public string $page_folder  = '';
188
-
189
-    public string $page_label   = '';
190
-
191
-    public string $page_slug    = '';
192
-
193
-
194
-    /**
195
-     * the current page route and route config
196
-     *
197
-     * @var array|callable|string|null
198
-     */
199
-    protected $_route = null;
200
-
201
-
202
-    /**
203
-     * @Constructor
204
-     * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
205
-     * @throws InvalidArgumentException
206
-     * @throws InvalidDataTypeException
207
-     * @throws InvalidInterfaceException
208
-     * @throws ReflectionException
209
-     */
210
-    public function __construct($routing = true)
211
-    {
212
-        $this->_routing = $routing;
213
-
214
-        $this->loader       = LoaderFactory::getLoader();
215
-        $this->admin_config = $this->loader->getShared(EE_Admin_Config::class);
216
-        $this->feature      = $this->loader->getShared(FeatureFlags::class);
217
-        $this->request      = $this->loader->getShared(RequestInterface::class);
218
-        $this->capabilities = $this->loader->getShared(EE_Capabilities::class);
219
-
220
-        $this->class_name      = get_class($this);
221
-        $this->base_class_name = strpos($this->class_name, 'Extend_') === 0
222
-            ? str_replace('Extend_', '', $this->class_name)
223
-            : '';
224
-
225
-        if (strpos($this->_get_dir(), 'caffeinated') !== false) {
226
-            $this->_is_caf = true;
227
-        }
228
-        $this->_yes_no_values = [
229
-            ['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
230
-            ['id' => false, 'text' => esc_html__('No', 'event_espresso')],
231
-        ];
232
-        // set the _req_data property.
233
-        $this->_req_data = $this->request->requestParams();
234
-    }
235
-
236
-
237
-    /**
238
-     * @return EE_Admin_Config
239
-     */
240
-    public function adminConfig(): EE_Admin_Config
241
-    {
242
-        return $this->admin_config;
243
-    }
244
-
245
-
246
-    public function capabilities(): EE_Capabilities
247
-    {
248
-        if (! $this->capabilities instanceof EE_Capabilities) {
249
-            $this->capabilities = $this->loader->getShared(EE_Capabilities::class);
250
-        }
251
-        return $this->capabilities;
252
-    }
253
-
254
-
255
-    /**
256
-     * @return FeatureFlags
257
-     */
258
-    public function feature(): FeatureFlags
259
-    {
260
-        return $this->feature;
261
-    }
262
-
263
-
264
-    /**
265
-     * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
266
-     * for child classes that needed to set properties prior to these methods getting called,
267
-     * but also needed the parent class to have its construction completed as well.
268
-     * Bottom line is that constructors should ONLY be used for setting initial properties
269
-     * and any complex initialization logic should only run after instantiation is complete.
270
-     * This method gets called immediately after construction from within
271
-     *      EE_Admin_Page_Init::_initialize_admin_page()
272
-     *
273
-     * @throws EE_Error
274
-     * @throws InvalidArgumentException
275
-     * @throws InvalidDataTypeException
276
-     * @throws InvalidInterfaceException
277
-     * @throws ReflectionException
278
-     * @throws Throwable
279
-     * @since 5.0.0.p
280
-     */
281
-    public function initializePage()
282
-    {
283
-        if ($this->initialized) {
284
-            return;
285
-        }
286
-        // set initial page props (child method)
287
-        $this->_init_page_props();
288
-        // set global defaults
289
-        $this->_set_defaults();
290
-        // set early because incoming requests could be ajax related and we need to register those hooks.
291
-        if ($this->request->isAjax()) {
292
-            $this->_global_ajax_hooks();
293
-            $this->_ajax_hooks();
294
-        }
295
-        // other_page_hooks have to be early too.
296
-        $this->_do_other_page_hooks();
297
-        // set up page dependencies
298
-        $this->_before_page_setup();
299
-        $this->_page_setup();
300
-        $this->initialized = true;
301
-    }
302
-
303
-
304
-    /**
305
-     * _init_page_props
306
-     * Child classes use to set at least the following properties:
307
-     * $page_slug.
308
-     * $page_label.
309
-     *
310
-     * @abstract
311
-     * @return void
312
-     */
313
-    abstract protected function _init_page_props();
314
-
315
-
316
-    /**
317
-     * _ajax_hooks
318
-     * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
319
-     * Note: within the ajax callback methods.
320
-     *
321
-     * @abstract
322
-     * @return void
323
-     */
324
-    abstract protected function _ajax_hooks();
325
-
326
-
327
-    /**
328
-     * _define_page_props
329
-     * child classes define page properties in here.  Must include at least:
330
-     * $_admin_base_url = base_url for all admin pages
331
-     * $_admin_page_title = default admin_page_title for admin pages
332
-     * $_labels = array of default labels for various automatically generated elements:
333
-     *    array(
334
-     *        'buttons' => array(
335
-     *            'add' => esc_html__('label for add new button'),
336
-     *            'edit' => esc_html__('label for edit button'),
337
-     *            'delete' => esc_html__('label for delete button')
338
-     *            )
339
-     *        )
340
-     *
341
-     * @abstract
342
-     * @return void
343
-     */
344
-    abstract protected function _define_page_props();
345
-
346
-
347
-    /**
348
-     * _set_page_routes
349
-     * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
350
-     * assigned to an action => method pairs in an array and to the $_page_routes property.  Each page route must also
351
-     * have a 'default' route. Here's the format
352
-     * $this->_page_routes = array(
353
-     *        'default' => array(
354
-     *            'func' => '_default_method_handling_route',
355
-     *            'args' => array('array','of','args'),
356
-     *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
357
-     *            ajax request, backend processing)
358
-     *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
359
-     *            headers route after.  The string you enter here should match the defined route reference for a
360
-     *            headers sent route.
361
-     *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
362
-     *            this route.
363
-     *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
364
-     *            checks).
365
-     *        ),
366
-     *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
367
-     *        handling method.
368
-     *        )
369
-     * )
370
-     *
371
-     * @abstract
372
-     * @return void
373
-     */
374
-    abstract protected function _set_page_routes();
375
-
376
-
377
-    /**
378
-     * _set_page_config
379
-     * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
380
-     * array corresponds to the page_route for the loaded page. Format:
381
-     * $this->_page_config = array(
382
-     *        'default' => array(
383
-     *            'labels' => array(
384
-     *                'buttons' => array(
385
-     *                    'add' => esc_html__('label for adding item'),
386
-     *                    'edit' => esc_html__('label for editing item'),
387
-     *                    'delete' => esc_html__('label for deleting item')
388
-     *                ),
389
-     *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
390
-     *            ), //optional an array of custom labels for various automatically generated elements to use on the
391
-     *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
392
-     *            _define_page_props() method
393
-     *            'nav' => array(
394
-     *                'label' => esc_html__('Label for Tab', 'event_espresso').
395
-     *                'url' => 'http://someurl', //automatically generated UNLESS you define
396
-     *                'css_class' => 'css-class', //automatically generated UNLESS you define
397
-     *                'order' => 10, //required to indicate tab position.
398
-     *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
399
-     *                displayed then add this parameter.
400
-     *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
401
-     *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
402
-     *            metaboxes set for eventespresso admin pages.
403
-     *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
404
-     *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
405
-     *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
406
-     *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
407
-     *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
408
-     *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
409
-     *            array indicates the max number of columns (4) and the default number of columns on page load (2).
410
-     *            There is an option in the "screen_options" dropdown that is set up so users can pick what columns they
411
-     *            want to display.
412
-     *            'help_tabs' => array( //this is used for adding help tabs to a page
413
-     *                'tab_id' => array(
414
-     *                    'title' => 'tab_title',
415
-     *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
416
-     *                    help tab content.  The fallback if it isn't present is to try the callback.  Filename
417
-     *                    should match a file in the admin folder's "help_tabs" dir (ie..
418
-     *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
419
-     *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
420
-     *                    attempt to use the callback which should match the name of a method in the class
421
-     *                    ),
422
-     *                'tab2_id' => array(
423
-     *                    'title' => 'tab2 title',
424
-     *                    'filename' => 'file_name_2'
425
-     *                    'callback' => 'callback_method_for_content',
426
-     *                 ),
427
-     *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
428
-     *            help tab area on an admin page. @return void
429
-     *
430
-     * @abstract
431
-     */
432
-    abstract protected function _set_page_config();
433
-
434
-
435
-    /**
436
-     * _add_screen_options
437
-     * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
438
-     * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
439
-     * to a particular view.
440
-     *
441
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
442
-     *         see also WP_Screen object documents...
443
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
444
-     * @abstract
445
-     * @return void
446
-     */
447
-    abstract protected function _add_screen_options();
448
-
449
-
450
-    /**
451
-     * _add_feature_pointers
452
-     * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
453
-     * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
454
-     * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
455
-     * WP_Internal_Pointers class in wp-admin/includes/template.php for example (it's a final class so can't be
456
-     * extended) also see:
457
-     *
458
-     * @link   http://eamann.com/tech/wordpress-portland/
459
-     * @abstract
460
-     * @return void
461
-     */
462
-    abstract protected function _add_feature_pointers();
463
-
464
-
465
-    /**
466
-     * load_scripts_styles
467
-     * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
468
-     * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
469
-     * scripts/styles per view by putting them in a dynamic function in this format
470
-     * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
471
-     *
472
-     * @abstract
473
-     * @return void
474
-     */
475
-    abstract public function load_scripts_styles();
476
-
477
-
478
-    /**
479
-     * admin_init
480
-     * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
481
-     * all pages/views loaded by child class.
482
-     *
483
-     * @abstract
484
-     * @return void
485
-     */
486
-    abstract public function admin_init();
487
-
488
-
489
-    /**
490
-     * admin_notices
491
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
492
-     * all pages/views loaded by child class.
493
-     *
494
-     * @abstract
495
-     * @return void
496
-     */
497
-    abstract public function admin_notices();
498
-
499
-
500
-    /**
501
-     * admin_footer_scripts
502
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
503
-     * will apply to all pages/views loaded by child class.
504
-     *
505
-     * @return void
506
-     */
507
-    abstract public function admin_footer_scripts();
508
-
509
-
510
-    /**
511
-     * admin_footer
512
-     * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
513
-     * apply to all pages/views loaded by child class.
514
-     *
515
-     * @return void
516
-     */
517
-    public function admin_footer()
518
-    {
519
-    }
43
+	protected ?WP_Screen $_current_screen    = null;
520 44
 
45
+	/**
46
+	 * @var array
47
+	 * @since 5.0.0.p
48
+	 */
49
+	private array $publish_post_meta_box_hidden_fields = [];
521 50
 
522
-    /**
523
-     * _global_ajax_hooks
524
-     * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
525
-     * Note: within the ajax callback methods.
526
-     *
527
-     * @abstract
528
-     * @return void
529
-     */
530
-    protected function _global_ajax_hooks()
531
-    {
532
-        // for lazy loading of metabox content
533
-        add_action('wp_ajax_espresso-ajax-content', [$this, 'ajax_metabox_content']);
534
-
535
-        add_action(
536
-            'wp_ajax_espresso_hide_status_change_notice',
537
-            [$this, 'hideStatusChangeNotice']
538
-        );
539
-        add_action(
540
-            'wp_ajax_nopriv_espresso_hide_status_change_notice',
541
-            [$this, 'hideStatusChangeNotice']
542
-        );
543
-    }
544
-
545
-
546
-    public function ajax_metabox_content()
547
-    {
548
-        $content_id  = $this->request->getRequestParam('contentid', '');
549
-        $content_url = $this->request->getRequestParam('contenturl', '', DataType::URL);
550
-        EE_Admin_Page::cached_rss_display($content_id, $content_url);
551
-        wp_die();
552
-    }
553
-
554
-
555
-    public function hideStatusChangeNotice()
556
-    {
557
-        $response = [];
558
-        try {
559
-            /** @var StatusChangeNotice $status_change_notice */
560
-            $status_change_notice = $this->loader->getShared(
561
-                'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
562
-            );
563
-            $response['success']  = $status_change_notice->dismiss() > -1;
564
-        } catch (Exception $exception) {
565
-            $response['errors'] = $exception->getMessage();
566
-        }
567
-        wp_send_json($response);
568
-    }
51
+	/**
52
+	 * some default things shared by all child classes
53
+	 *
54
+	 * @var string[]
55
+	 */
56
+	protected array $_default_espresso_metaboxes = [
57
+		'_espresso_news_post_box',
58
+		'_espresso_links_post_box',
59
+		'_espresso_ratings_request',
60
+		'_espresso_sponsors_post_box',
61
+	];
569 62
 
63
+	/**
64
+	 * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
65
+	 * actions.
66
+	 *
67
+	 * @since 4.6.x
68
+	 */
69
+	protected array $_default_route_query_args = [];
570 70
 
571
-    /**
572
-     * allows extending classes do something specific before the parent constructor runs _page_setup().
573
-     *
574
-     * @return void
575
-     */
576
-    protected function _before_page_setup()
577
-    {
578
-        // default is to do nothing
579
-    }
71
+	protected array $_labels                   = [];
580 72
 
73
+	protected array $_nav_tabs                 = [];
581 74
 
582
-    /**
583
-     * Makes sure any things that need to be loaded early get handled.
584
-     * We also escape early here if the page requested doesn't match the object.
585
-     *
586
-     * @final
587
-     * @return void
588
-     * @throws EE_Error
589
-     * @throws InvalidArgumentException
590
-     * @throws ReflectionException
591
-     * @throws InvalidDataTypeException
592
-     * @throws InvalidInterfaceException
593
-     * @throws Throwable
594
-     */
595
-    final protected function _page_setup()
596
-    {
597
-        // requires?
598
-        // admin_init stuff - global - we're setting this REALLY early
599
-        // so if EE_Admin pages have to hook into other WP pages they can.
600
-        // But keep in mind, not everything is available from the EE_Admin Page object at this point.
601
-        add_action('admin_init', [$this, 'admin_init_global'], 5);
602
-        // next verify if we need to load anything...
603
-        $this->_current_page = $this->request->getRequestParam('page', '', DataType::KEY);
604
-        $this->_current_page = $this->request->getRequestParam('current_page', $this->_current_page, DataType::KEY);
605
-        $this->page_folder   = strtolower(
606
-            str_replace(['_Admin_Page', 'Extend_'], '', $this->class_name)
607
-        );
608
-        global $ee_menu_slugs;
609
-        $ee_menu_slugs = (array) $ee_menu_slugs;
610
-        if (
611
-            ! $this->request->isAjax()
612
-            && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
613
-        ) {
614
-            return;
615
-        }
616
-        // because WP List tables have two duplicate select inputs for choosing bulk actions,
617
-        // we need to copy the action from the second to the first
618
-        $action     = $this->request->getRequestParam('action', '-1', DataType::KEY);
619
-        $action2    = $this->request->getRequestParam('action2', '-1', DataType::KEY);
620
-        $action     = $action !== '-1' ? $action : $action2;
621
-        $req_action = $action !== '-1' ? $action : 'default';
622
-
623
-        // if a specific 'route' has been set, and the action is 'default' OR we are doing_ajax
624
-        // then let's use the route as the action.
625
-        // This covers cases where we're coming in from a list table that isn't on the default route.
626
-        $route = $this->request->getRequestParam('route');
627
-        $route = $route !== '-1' ? $route : 'default';
628
-        $this->_req_action = $route && ($req_action === 'default' || $this->request->isAjax())
629
-            ? $route
630
-            : $req_action;
631
-        $this->_current_view = $this->_req_action;
632
-        $this->_req_nonce    = $this->_req_action . '_nonce';
633
-        $this->_define_page_props();
634
-        $this->_current_page_view_url = add_query_arg(
635
-            ['page' => $this->_current_page, 'action' => $this->_current_view],
636
-            $this->_admin_base_url
637
-        );
638
-        // set page configs
639
-        $this->_set_page_routes();
640
-        $this->_set_page_config();
641
-        // let's include any referrer data in our default_query_args for this route for "stickiness".
642
-        if ($this->request->requestParamIsSet('wp_referer')) {
643
-            $wp_referer = $this->request->getRequestParam('wp_referer');
644
-            if ($wp_referer) {
645
-                $this->_default_route_query_args['wp_referer'] = $wp_referer;
646
-            }
647
-        }
648
-        // for CPT and other extended functionality.
649
-        // If there is an _extend_page_config_for_cpt
650
-        // then let's run that to modify all the various page configuration arrays.
651
-        if (method_exists($this, '_extend_page_config_for_cpt')) {
652
-            $this->_extend_page_config_for_cpt();
653
-        }
654
-        // filter routes and page_config so addons can add their stuff. Filtering done per class
655
-        $this->_page_routes = apply_filters(
656
-            'FHEE__' . $this->class_name . '__page_setup__page_routes',
657
-            $this->_page_routes,
658
-            $this
659
-        );
660
-        $this->_page_config = apply_filters(
661
-            'FHEE__' . $this->class_name . '__page_setup__page_config',
662
-            $this->_page_config,
663
-            $this
664
-        );
665
-        if ($this->base_class_name !== '') {
666
-            $this->_page_routes = apply_filters(
667
-                'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
668
-                $this->_page_routes,
669
-                $this
670
-            );
671
-            $this->_page_config = apply_filters(
672
-                'FHEE__' . $this->base_class_name . '__page_setup__page_config',
673
-                $this->_page_config,
674
-                $this
675
-            );
676
-        }
677
-        // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
678
-        // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
679
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
680
-            add_action(
681
-                'AHEE__EE_Admin_Page__route_admin_request',
682
-                [$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
683
-                10,
684
-                2
685
-            );
686
-        }
687
-        // next route only if routing enabled
688
-        if ($this->_routing && ! $this->request->isAjax()) {
689
-            $this->_verify_routes();
690
-            // next let's just check user_access and kill if no access
691
-            $this->check_user_access();
692
-            if ($this->_is_UI_request) {
693
-                // admin_init stuff - global, all views for this page class, specific view
694
-                add_action('admin_init', [$this, 'admin_init']);
695
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
696
-                    add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
697
-                }
698
-            } else {
699
-                // hijack regular WP loading and route admin request immediately
700
-                @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
701
-                $this->route_admin_request();
702
-            }
703
-        }
704
-    }
75
+	protected array $_page_config              = [];
705 76
 
77
+	/**
78
+	 * action => method pairs used for routing incoming requests
79
+	 *
80
+	 * @var array
81
+	 */
82
+	protected array $_page_routes   = [];
706 83
 
707
-    /**
708
-     * Provides a way for related child admin pages to load stuff on the loaded admin page.
709
-     *
710
-     * @return void
711
-     * @throws EE_Error
712
-     */
713
-    private function _do_other_page_hooks()
714
-    {
715
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
716
-        foreach ($registered_pages as $page) {
717
-            // now let's set up the file name and class that should be present
718
-            $classname = str_replace('.class.php', '', $page);
719
-            // autoloaders should take care of loading file
720
-            if (! class_exists($classname)) {
721
-                $error_msg[] = sprintf(
722
-                    esc_html__(
723
-                        'Something went wrong with loading the %s admin hooks page.',
724
-                        'event_espresso'
725
-                    ),
726
-                    $page
727
-                );
728
-                $error_msg[] = $error_msg[0]
729
-                               . "\r\n"
730
-                               . sprintf(
731
-                                   esc_html__(
732
-                                       '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',
733
-                                       'event_espresso'
734
-                                   ),
735
-                                   $page,
736
-                                   '<br />',
737
-                                   '<strong>' . $classname . '</strong>'
738
-                               );
739
-                throw new EE_Error(implode('||', $error_msg));
740
-            }
741
-            // don't load the same class twice
742
-            static $loaded = [];
743
-            if (in_array($classname, $loaded, true)) {
744
-                continue;
745
-            }
746
-            $loaded[] = $classname;
747
-            // notice we are passing the instance of this class to the hook object.
748
-            $this->loader->getShared($classname, [$this]);
749
-        }
750
-    }
84
+	protected array $_req_data      = [];
751 85
 
86
+	protected array $_route_config  = [];
752 87
 
753
-    /**
754
-     * @throws ReflectionException
755
-     * @throws EE_Error
756
-     */
757
-    public function load_page_dependencies()
758
-    {
759
-        try {
760
-            $this->_load_page_dependencies();
761
-        } catch (EE_Error $e) {
762
-            $e->get_error();
763
-        }
764
-    }
88
+	protected array $_template_args = [];
765 89
 
90
+	protected array $_views         = [];
766 91
 
767
-    /**
768
-     * load_page_dependencies
769
-     * loads things specific to this page class when it's loaded.  Really helps with efficiency.
770
-     *
771
-     * @return void
772
-     * @throws DomainException
773
-     * @throws EE_Error
774
-     * @throws InvalidArgumentException
775
-     * @throws InvalidDataTypeException
776
-     * @throws InvalidInterfaceException
777
-     * @throws ReflectionException
778
-     */
779
-    protected function _load_page_dependencies()
780
-    {
781
-        // let's set the current_screen and screen options to override what WP set
782
-        $this->_current_screen = get_current_screen();
783
-        // load admin_notices - global, page class, and view specific
784
-        add_action('admin_notices', [$this, 'admin_notices_global'], 5);
785
-        add_action('admin_notices', [$this, 'admin_notices']);
786
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
787
-            add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
788
-        }
789
-        // load network admin_notices - global, page class, and view specific
790
-        add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
791
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
792
-            add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
793
-        }
794
-        // this will save any per_page screen options if they are present
795
-        $this->_set_per_page_screen_options();
796
-        // setup list table properties
797
-        $this->_set_list_table();
798
-        // child classes can "register" a metabox to be automatically handled via the _page_config array property.
799
-        // However in some cases the metaboxes will need to be added within a route handling callback.
800
-        add_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
801
-        // hack because promos admin was loading the edited promotion object in the metaboxes callback
802
-        // which should NOT be generated on non-UI requests like POST updates/inserts
803
-        if (
804
-            $this->class_name === 'Promotions_Admin_Page'
805
-            && ($this->_req_action === 'edit' || $this->_req_action === 'create_new')
806
-        ) {
807
-            $this->addRegisteredMetaBoxes();
808
-        }
809
-        $this->_add_screen_columns();
810
-        // add screen options - global, page child class, and view specific
811
-        $this->_add_global_screen_options();
812
-        $this->_add_screen_options();
813
-        $add_screen_options = "_add_screen_options_$this->_current_view";
814
-        if (method_exists($this, $add_screen_options)) {
815
-            $this->{$add_screen_options}();
816
-        }
817
-        // add help tab(s) - set via page_config and qtips.
818
-        $this->_add_help_tabs();
819
-        $this->_add_qtips();
820
-        // add feature_pointers - global, page child class, and view specific
821
-        $this->_add_feature_pointers();
822
-        $this->_add_global_feature_pointers();
823
-        $add_feature_pointer = "_add_feature_pointer_$this->_current_view";
824
-        if (method_exists($this, $add_feature_pointer)) {
825
-            $this->{$add_feature_pointer}();
826
-        }
827
-        // enqueue scripts/styles - global, page class, and view specific
828
-        add_action('admin_enqueue_scripts', [$this, 'admin_footer_scripts_eei18n_js_strings'], 1);
829
-        add_action('admin_enqueue_scripts', [$this, 'load_global_scripts_styles'], 5);
830
-        add_action('admin_enqueue_scripts', [$this, 'load_scripts_styles']);
831
-        if (method_exists($this, "load_scripts_styles_$this->_current_view")) {
832
-            add_action('admin_enqueue_scripts', [$this, "load_scripts_styles_$this->_current_view"], 15);
833
-        }
834
-        // admin_print_footer_scripts - global, page child class, and view specific.
835
-        // NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
836
-        // In most cases that's doing_it_wrong().  But adding hidden container elements etc.
837
-        // is a good use case. Notice the late priority we're giving these
838
-        add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts_global'], 99);
839
-        add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts'], 100);
840
-        if (method_exists($this, "admin_footer_scripts_$this->_current_view")) {
841
-            add_action('admin_print_footer_scripts', [$this, "admin_footer_scripts_$this->_current_view"], 101);
842
-        }
843
-        // admin footer scripts
844
-        add_action('admin_footer', [$this, 'admin_footer_global'], 99);
845
-        add_action('admin_footer', [$this, 'admin_footer'], 100);
846
-        if (method_exists($this, "admin_footer_$this->_current_view")) {
847
-            add_action('admin_footer', [$this, "admin_footer_$this->_current_view"], 101);
848
-        }
849
-        do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
850
-        // targeted hook
851
-        do_action(
852
-            "FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__$this->_req_action"
853
-        );
854
-    }
92
+	/**
93
+	 * yes / no array for admin form fields
94
+	 *
95
+	 * @var array|array[]
96
+	 */
97
+	protected array $_yes_no_values = [];
855 98
 
99
+	/**
100
+	 * this starts at null so we can have no header routes progress through two states.
101
+	 */
102
+	protected ?bool $_is_UI_request = null;
856 103
 
857
-    /**
858
-     * _set_defaults
859
-     * This sets some global defaults for class properties.
860
-     */
861
-    private function _set_defaults()
862
-    {
863
-        // init template args
864
-        $this->set_template_args(
865
-            [
866
-                'admin_page_header'  => '',
867
-                'admin_page_content' => '',
868
-                'post_body_content'  => '',
869
-                'before_list_table'  => '',
870
-                'after_list_table'   => '',
871
-            ]
872
-        );
873
-    }
104
+	/**
105
+	 * flags whether the given route is a caffeinated route or not.
106
+	 */
107
+	protected bool $_is_caf        = false;
874 108
 
109
+	protected bool $_routing       = false;
875 110
 
876
-    /**
877
-     * route_admin_request
878
-     *
879
-     * @return void
880
-     * @throws InvalidArgumentException
881
-     * @throws InvalidInterfaceException
882
-     * @throws InvalidDataTypeException
883
-     * @throws EE_Error
884
-     * @throws ReflectionException
885
-     * @throws Throwable
886
-     * @see    _route_admin_request()
887
-     */
888
-    public function route_admin_request()
889
-    {
890
-        try {
891
-            $this->_route_admin_request();
892
-        } catch (EE_Error $e) {
893
-            $e->get_error();
894
-        }
895
-    }
896
-
897
-
898
-    public function set_wp_page_slug($wp_page_slug)
899
-    {
900
-        $this->_wp_page_slug = $wp_page_slug;
901
-        // if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
902
-        if (is_network_admin()) {
903
-            $this->_wp_page_slug .= '-network';
904
-        }
905
-    }
111
+	/**
112
+	 * whether initializePage() has run
113
+	 *
114
+	 * @var bool
115
+	 */
116
+	protected bool $initialized = false;
906 117
 
907 118
 
908
-    /**
909
-     * _verify_routes
910
-     * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
911
-     * we know if we need to drop out.
912
-     *
913
-     * @return bool
914
-     * @throws EE_Error
915
-     */
916
-    protected function _verify_routes(): bool
917
-    {
918
-        if (! $this->_current_page && ! $this->request->isAjax()) {
919
-            return false;
920
-        }
921
-        // check that the page_routes array is not empty
922
-        if (empty($this->_page_routes)) {
923
-            // user error msg
924
-            $error_msg = sprintf(
925
-                esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
926
-                $this->_admin_page_title
927
-            );
928
-            // developer error msg
929
-            $error_msg .= '||' . $error_msg
930
-                          . esc_html__(
931
-                              ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
932
-                              'event_espresso'
933
-                          );
934
-            throw new EE_Error($error_msg);
935
-        }
936
-        // route 'editpost' routes to CPT 'edit' routes
937
-        $alt_edit_route = $this instanceof EE_Admin_Page_CPT ? $this->cpt_editpost_route : 'edit';
938
-        if (
939
-            $this->_req_action === 'editpost'
940
-            && ! isset($this->_page_routes['editpost'])
941
-            && isset($this->_page_routes[ $alt_edit_route ])
942
-        ) {
943
-            $this->_req_action = $alt_edit_route;
944
-        }
945
-        // and that the requested page route exists
946
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
947
-            $this->_route        = $this->_page_routes[ $this->_req_action ];
948
-            $this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
949
-        } else {
950
-            // user error msg
951
-            $error_msg = sprintf(
952
-                esc_html__(
953
-                    'The requested page route does not exist for the %s admin page.',
954
-                    'event_espresso'
955
-                ),
956
-                $this->_admin_page_title
957
-            );
958
-            // developer error msg
959
-            $error_msg .= '||' . $error_msg
960
-                          . sprintf(
961
-                              esc_html__(
962
-                                  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
963
-                                  'event_espresso'
964
-                              ),
965
-                              $this->_req_action
966
-                          );
967
-            throw new EE_Error($error_msg);
968
-        }
969
-        // and that a default route exists
970
-        if (! array_key_exists('default', $this->_page_routes)) {
971
-            // user error msg
972
-            $error_msg = sprintf(
973
-                esc_html__(
974
-                    'A default page route has not been set for the % admin page.',
975
-                    'event_espresso'
976
-                ),
977
-                $this->_admin_page_title
978
-            );
979
-            // developer error msg
980
-            $error_msg .= '||' . $error_msg
981
-                          . esc_html__(
982
-                              ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
983
-                              'event_espresso'
984
-                          );
985
-            throw new EE_Error($error_msg);
986
-        }
987
-
988
-        // first lets' catch if the UI request has EVER been set.
989
-        if ($this->_is_UI_request === null) {
990
-            // let's set if this is a UI request or not.
991
-            $this->_is_UI_request = ! $this->request->getRequestParam('noheader', false, DataType::BOOL);
992
-            // wait a minute... we might have a noheader in the route array
993
-            $this->_is_UI_request = ! (isset($this->_route['noheader']) && $this->_route['noheader'])
994
-                ? $this->_is_UI_request
995
-                : false;
996
-        }
997
-        $this->_set_current_labels();
998
-        return true;
999
-    }
119
+	protected string $_admin_base_path      = '';
1000 120
 
121
+	protected string $_admin_base_url       = '';
1001 122
 
1002
-    /**
1003
-     * this method simply verifies a given route and makes sure it's an actual route available for the loaded page
1004
-     *
1005
-     * @param string $route the route name we're verifying
1006
-     * @return bool we'll throw an exception if this isn't a valid route.
1007
-     * @throws EE_Error
1008
-     */
1009
-    protected function _verify_route(string $route): bool
1010
-    {
1011
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
1012
-            return true;
1013
-        }
1014
-        // user error msg
1015
-        $error_msg = sprintf(
1016
-            esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
1017
-            $this->_admin_page_title
1018
-        );
1019
-        // developer error msg
1020
-        $error_msg .= '||' . $error_msg
1021
-                      . sprintf(
1022
-                          esc_html__(
1023
-                              ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
1024
-                              'event_espresso'
1025
-                          ),
1026
-                          $route
1027
-                      );
1028
-        throw new EE_Error($error_msg);
1029
-    }
123
+	protected string $_admin_page_title     = '';
1030 124
 
125
+	protected string $_column_template_path = '';
1031 126
 
1032
-    /**
1033
-     * perform nonce verification
1034
-     * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
1035
-     * using this method (and save retyping!)
1036
-     *
1037
-     * @param string $nonce     The nonce sent
1038
-     * @param string $nonce_ref The nonce reference string (name0)
1039
-     * @return void
1040
-     * @throws EE_Error
1041
-     * @throws InvalidArgumentException
1042
-     * @throws InvalidDataTypeException
1043
-     * @throws InvalidInterfaceException
1044
-     */
1045
-    protected function _verify_nonce(string $nonce, string $nonce_ref)
1046
-    {
1047
-        // verify nonce against expected value
1048
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
1049
-            // these are not the droids you are looking for !!!
1050
-            $msg = sprintf(
1051
-                esc_html__('%sNonce Fail.%s', 'event_espresso'),
1052
-                '<a href="https://www.youtube.com/watch?v=56_S0WeTkzs">',
1053
-                '</a>'
1054
-            );
1055
-            if (WP_DEBUG) {
1056
-                $msg .= "\n  ";
1057
-                $msg .= sprintf(
1058
-                    esc_html__(
1059
-                        'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
1060
-                        'event_espresso'
1061
-                    ),
1062
-                    __CLASS__
1063
-                );
1064
-            }
1065
-            if (! $this->request->isAjax()) {
1066
-                wp_die($msg);
1067
-            }
1068
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1069
-            $this->_return_json();
1070
-        }
1071
-    }
127
+	protected bool $_cpt_route            = false;
1072 128
 
129
+	/**
130
+	 * set via request page and action args.
131
+	 */
132
+	protected string $_current_page          = '';
1073 133
 
1074
-    /**
1075
-     * _route_admin_request()
1076
-     * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1077
-     * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1078
-     * in the page routes and then will try to load the corresponding method.
1079
-     *
1080
-     * @return void
1081
-     * @throws EE_Error
1082
-     * @throws InvalidArgumentException
1083
-     * @throws InvalidDataTypeException
1084
-     * @throws InvalidInterfaceException
1085
-     * @throws ReflectionException
1086
-     * @throws Throwable
1087
-     */
1088
-    protected function _route_admin_request()
1089
-    {
1090
-        if (! $this->_is_UI_request) {
1091
-            $this->_verify_routes();
1092
-        }
1093
-        $nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
1094
-        if ($this->_req_action !== 'default' && $nonce_check) {
1095
-            // set nonce from post data
1096
-            $nonce = $this->request->getRequestParam($this->_req_nonce, '');
1097
-            $this->_verify_nonce($nonce, $this->_req_nonce);
1098
-        }
1099
-        // set the nav_tabs array but ONLY if this is  UI_request
1100
-        if ($this->_is_UI_request) {
1101
-            $this->_set_nav_tabs();
1102
-        }
1103
-        // grab callback function
1104
-        $func = $this->_route['func'] ?? $this->_route;
1105
-        // check if callback has args
1106
-        $args = $this->_route['args'] ?? [];
1107
-
1108
-        // action right before calling route
1109
-        // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1110
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1111
-            do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1112
-        }
1113
-        // strip _wp_http_referer from the server REQUEST_URI
1114
-        // else it grows in length on every submission due to recursion,
1115
-        // ultimately causing a "Request-URI Too Large" error
1116
-        $this->request->unSetRequestParam('_wp_http_referer');
1117
-        $this->request->unSetServerParam('_wp_http_referer');
1118
-        $cleaner_request_uri = remove_query_arg(
1119
-            '_wp_http_referer',
1120
-            wp_unslash($this->request->getServerParam('REQUEST_URI'))
1121
-        );
1122
-        $this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1123
-        $this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1124
-        $route_callback = [];
1125
-        if (! empty($func)) {
1126
-            if (is_array($func) && is_callable($func)) {
1127
-                $route_callback = $func;
1128
-            } elseif (is_string($func)) {
1129
-                if (strpos($func, '::') !== false) {
1130
-                    $route_callback = explode('::', $func);
1131
-                } elseif (method_exists($this, $func)) {
1132
-                    $route_callback = [$this, $func];
1133
-                } else {
1134
-                    $route_callback = $func;
1135
-                }
1136
-            }
1137
-            [$class, $method] = $route_callback;
1138
-            // is it neither a class method NOR a standalone function?
1139
-            if (! is_callable($route_callback)) {
1140
-                // user error msg
1141
-                $error_msg = esc_html__(
1142
-                    'An error occurred. The  requested page route could not be found.',
1143
-                    'event_espresso'
1144
-                );
1145
-                // developer error msg
1146
-                $error_msg .= '||';
1147
-                $error_msg .= sprintf(
1148
-                    esc_html__(
1149
-                        'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1150
-                        'event_espresso'
1151
-                    ),
1152
-                    $method
1153
-                );
1154
-                throw new DomainException($error_msg);
1155
-            }
1156
-            if ($class !== $this && ! in_array($this, $args)) {
1157
-                // send along this admin page object for access by addons.
1158
-                $args['admin_page'] = $this;
1159
-            }
1160
-
1161
-            call_user_func_array($route_callback, $args);
1162
-        }
1163
-        // if we've routed and this route has a no headers route AND a sent_headers_route,
1164
-        // then we need to reset the routing properties to the new route.
1165
-        // 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.
1166
-        if (
1167
-            $this->_is_UI_request === false
1168
-            && is_array($this->_route)
1169
-            && ! empty($this->_route['headers_sent_route'])
1170
-        ) {
1171
-            $this->_reset_routing_properties($this->_route['headers_sent_route']);
1172
-        }
1173
-    }
134
+	protected string $_current_page_view_url = '';
1174 135
 
136
+	protected string $_current_view          = '';
1175 137
 
1176
-    /**
1177
-     * This method just allows the resetting of page properties in the case where a no headers
1178
-     * route redirects to a headers route in its route config.
1179
-     *
1180
-     * @param string $new_route New (non header) route to redirect to.
1181
-     * @return void
1182
-     * @throws ReflectionException
1183
-     * @throws InvalidArgumentException
1184
-     * @throws InvalidInterfaceException
1185
-     * @throws InvalidDataTypeException
1186
-     * @throws EE_Error
1187
-     * @throws Throwable
1188
-     * @since   4.3.0
1189
-     */
1190
-    protected function _reset_routing_properties(string $new_route)
1191
-    {
1192
-        $this->_is_UI_request = true;
1193
-        // now we set the current route to whatever the headers_sent_route is set at
1194
-        $this->request->setRequestParam('action', $new_route);
1195
-        // rerun page setup
1196
-        $this->_page_setup();
1197
-    }
138
+	protected string $_default_nav_tab_name  = 'overview';
1198 139
 
140
+	/**
141
+	 * sanitized request action
142
+	 */
143
+	protected string $_req_action = '';
144
+
145
+	/**
146
+	 * sanitized request action nonce
147
+	 */
148
+	protected string $_req_nonce        = '';
149
+
150
+	protected string $_search_btn_label = '';
151
+
152
+	protected string $_template_path    = '';
153
+
154
+	protected string $_view             = '';
1199 155
 
1200
-    /**
1201
-     * _add_query_arg
1202
-     * adds nonce to array of arguments then calls WP add_query_arg function
1203
-     *(internally just uses EEH_URL's function with the same name)
1204
-     *
1205
-     * @param array  $args
1206
-     * @param string $url
1207
-     * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1208
-     *                                        generated url in an associative array indexed by the key 'wp_referer';
1209
-     *                                        Example usage: If the current page is:
1210
-     *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1211
-     *                                        &action=default&event_id=20&month_range=March%202015
1212
-     *                                        &_wpnonce=5467821
1213
-     *                                        and you call:
1214
-     *                                        EE_Admin_Page::add_query_args_and_nonce(
1215
-     *                                        array(
1216
-     *                                        'action' => 'resend_something',
1217
-     *                                        'page=>espresso_registrations'
1218
-     *                                        ),
1219
-     *                                        $some_url,
1220
-     *                                        true
1221
-     *                                        );
1222
-     *                                        It will produce a URL in this structure:
1223
-     *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1224
-     *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1225
-     *                                        month_range]=March%202015
1226
-     * @param bool   $exclude_nonce           If true, the nonce will be excluded from the generated nonce.
1227
-     * @param int    $context
1228
-     * @return string
1229
-     */
1230
-    public static function add_query_args_and_nonce(
1231
-        array $args = [],
1232
-        string $url = '',
1233
-        bool $sticky = false,
1234
-        bool $exclude_nonce = false,
1235
-        int $context = EEH_URL::CONTEXT_NONE
1236
-    ): string {
1237
-        // if there is a _wp_http_referer include the values from the request but only if sticky = true
1238
-        if ($sticky) {
1239
-            /** @var RequestInterface $request */
1240
-            $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1241
-            $request->unSetRequestParams(['_wp_http_referer', 'wp_referer'], true);
1242
-            $request->unSetServerParam('_wp_http_referer', true);
1243
-            foreach ($request->requestParams() as $key => $value) {
1244
-                // do not add nonces
1245
-                if (strpos($key, 'nonce') !== false) {
1246
-                    continue;
1247
-                }
1248
-                $args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1249
-            }
1250
-        }
1251
-        return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce, $context);
1252
-    }
156
+	/**
157
+	 * set early within EE_Admin_Init
158
+	 *
159
+	 * @var string
160
+	 */
161
+	protected string $_wp_page_slug = '';
1253 162
 
163
+	/**
164
+	 * if the current class is an admin page extension, like: Extend_Events_Admin_Page,
165
+	 * then this would be the parent classname: Events_Admin_Page
166
+	 *
167
+	 * @var string
168
+	 */
169
+	public string $base_class_name = '';
1254 170
 
1255
-    /**
1256
-     * This returns a generated link that will load the related help tab.
1257
-     *
1258
-     * @param string $help_tab_id the id for the connected help tab
1259
-     * @param string $icon_style  (optional) include css class for the style you want to use for the help icon.
1260
-     * @param string $help_text   (optional) send help text you want to use for the link if default not to be used
1261
-     * @return string              generated link
1262
-     * @uses EEH_Template::get_help_tab_link()
1263
-     */
1264
-    protected function _get_help_tab_link(string $help_tab_id, string $icon_style = '', string $help_text = ''): string
1265
-    {
1266
-        return EEH_Template::get_help_tab_link(
1267
-            $help_tab_id,
1268
-            $this->page_slug,
1269
-            $this->_req_action,
1270
-            $icon_style,
1271
-            $help_text
1272
-        );
1273
-    }
1274
-
1275
-
1276
-    /**
1277
-     * _add_help_tabs
1278
-     * Note child classes define their help tabs within the page_config array.
1279
-     *
1280
-     * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1281
-     * @return void
1282
-     * @throws DomainException
1283
-     * @throws EE_Error
1284
-     * @throws ReflectionException
1285
-     */
1286
-    protected function _add_help_tabs()
1287
-    {
1288
-        if (isset($this->_page_config[ $this->_req_action ])) {
1289
-            $config = $this->_page_config[ $this->_req_action ];
1290
-            // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1291
-            if (is_array($config) && isset($config['help_sidebar'])) {
1292
-                // check that the callback given is valid
1293
-                if (! method_exists($this, $config['help_sidebar'])) {
1294
-                    throw new EE_Error(
1295
-                        sprintf(
1296
-                            esc_html__(
1297
-                                'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Double check the spelling and make sure this method exists for the class %s',
1298
-                                'event_espresso'
1299
-                            ),
1300
-                            $config['help_sidebar'],
1301
-                            $this->class_name
1302
-                        )
1303
-                    );
1304
-                }
1305
-                $content = apply_filters(
1306
-                    'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1307
-                    $this->{$config['help_sidebar']}()
1308
-                );
1309
-                $this->_current_screen->set_help_sidebar($content);
1310
-            }
1311
-            if (! isset($config['help_tabs'])) {
1312
-                return;
1313
-            } //no help tabs for this route
1314
-            foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1315
-                // we're here so there ARE help tabs!
1316
-                // make sure we've got what we need
1317
-                if (! isset($cfg['title'])) {
1318
-                    throw new EE_Error(
1319
-                        esc_html__(
1320
-                            'The _page_config array is not set up properly for help tabs.  It is missing a title',
1321
-                            'event_espresso'
1322
-                        )
1323
-                    );
1324
-                }
1325
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1326
-                    throw new EE_Error(
1327
-                        esc_html__(
1328
-                            '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',
1329
-                            'event_espresso'
1330
-                        )
1331
-                    );
1332
-                }
1333
-                // first priority goes to content.
1334
-                if (! empty($cfg['content'])) {
1335
-                    $content = $cfg['content'];
1336
-                    // second priority goes to filename
1337
-                } elseif (! empty($cfg['filename'])) {
1338
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1339
-                    // 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)
1340
-                    $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1341
-                                                             . basename($this->_get_dir())
1342
-                                                             . '/help_tabs/'
1343
-                                                             . $cfg['filename']
1344
-                                                             . '.help_tab.php' : $file_path;
1345
-                    // if file is STILL not readable then let's do an EE_Error so its more graceful than a fatal error.
1346
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1347
-                        EE_Error::add_error(
1348
-                            sprintf(
1349
-                                esc_html__(
1350
-                                    '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',
1351
-                                    'event_espresso'
1352
-                                ),
1353
-                                $tab_id,
1354
-                                key($config),
1355
-                                $file_path
1356
-                            ),
1357
-                            __FILE__,
1358
-                            __FUNCTION__,
1359
-                            __LINE__
1360
-                        );
1361
-                        return;
1362
-                    }
1363
-                    $template_args['admin_page_obj'] = $this;
1364
-                    $content                         = EEH_Template::display_template(
1365
-                        $file_path,
1366
-                        $template_args,
1367
-                        true
1368
-                    );
1369
-                } else {
1370
-                    $content = '';
1371
-                }
1372
-                // check if callback is valid
1373
-                if (
1374
-                    empty($content)
1375
-                    && (
1376
-                        ! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1377
-                    )
1378
-                ) {
1379
-                    EE_Error::add_error(
1380
-                        sprintf(
1381
-                            esc_html__(
1382
-                                '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.',
1383
-                                'event_espresso'
1384
-                            ),
1385
-                            $cfg['title']
1386
-                        ),
1387
-                        __FILE__,
1388
-                        __FUNCTION__,
1389
-                        __LINE__
1390
-                    );
1391
-                    return;
1392
-                }
1393
-                // setup config array for help tab method
1394
-                $id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1395
-                $_ht = [
1396
-                    'id'       => $id,
1397
-                    'title'    => $cfg['title'],
1398
-                    'callback' => isset($cfg['callback']) && empty($content) ? [$this, $cfg['callback']] : null,
1399
-                    'content'  => $content,
1400
-                ];
1401
-                $this->_current_screen->add_help_tab($_ht);
1402
-            }
1403
-        }
1404
-    }
1405
-
1406
-
1407
-    /**
1408
-     * This simply sets up any qtips that have been defined in the page config
1409
-     *
1410
-     * @return void
1411
-     * @throws ReflectionException
1412
-     * @throws EE_Error
1413
-     */
1414
-    protected function _add_qtips()
1415
-    {
1416
-        if (isset($this->_route_config['qtips'])) {
1417
-            $qtips = (array) $this->_route_config['qtips'];
1418
-            // load qtip loader
1419
-            $path = [
1420
-                $this->_get_dir() . '/qtips/',
1421
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1422
-            ];
1423
-            EEH_Qtip_Loader::instance()->register($qtips, $path);
1424
-        }
1425
-    }
171
+	public string $class_name      = '';
1426 172
 
1427
-
1428
-    /**
1429
-     * _set_nav_tabs
1430
-     * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1431
-     * wish to add additional tabs or modify accordingly.
1432
-     *
1433
-     * @return void
1434
-     * @throws InvalidArgumentException
1435
-     * @throws InvalidInterfaceException
1436
-     * @throws InvalidDataTypeException
1437
-     */
1438
-    protected function _set_nav_tabs()
1439
-    {
1440
-        $i        = 0;
1441
-        $only_tab = count($this->_page_config) < 2;
1442
-        foreach ($this->_page_config as $slug => $config) {
1443
-            if (! is_array($config) || empty($config['nav'])) {
1444
-                continue;
1445
-            }
1446
-            // no nav tab for this config
1447
-            // check for persistent flag
1448
-            if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1449
-                // nav tab is only to appear when route requested.
1450
-                continue;
1451
-            }
1452
-            if (! $this->check_user_access($slug, true)) {
1453
-                // no nav tab because current user does not have access.
1454
-                continue;
1455
-            }
1456
-            $css_class = $config['css_class'] ?? '';
1457
-            $css_class .= $only_tab ? ' ee-only-tab' : '';
1458
-            $css_class .= " ee-nav-tab__$slug";
1459
-
1460
-            $this->_nav_tabs[ $slug ] = [
1461
-                'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1462
-                    ['action' => $slug],
1463
-                    $this->_admin_base_url
1464
-                ),
1465
-                'link_text' => $this->navTabLabel($config['nav'], $slug),
1466
-                'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1467
-                'order'     => $config['nav']['order'] ?? $i,
1468
-            ];
1469
-            $i++;
1470
-        }
1471
-        // if $this->_nav_tabs is empty then lets set the default
1472
-        if (empty($this->_nav_tabs)) {
1473
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1474
-                'url'       => $this->_admin_base_url,
1475
-                'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1476
-                'css_class' => 'nav-tab-active',
1477
-                'order'     => 10,
1478
-            ];
1479
-        }
1480
-        // now let's sort the tabs according to order
1481
-        usort($this->_nav_tabs, [$this, '_sort_nav_tabs']);
1482
-    }
1483
-
1484
-
1485
-    private function navTabLabel(array $nav_tab, string $slug): string
1486
-    {
1487
-        $label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1488
-        $icon  = $nav_tab['icon'] ?? null;
1489
-        $icon  = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1490
-        return '
173
+	/**
174
+	 * unprocessed value for the 'action' request param (default '')
175
+	 *
176
+	 * @var string
177
+	 */
178
+	protected string $raw_req_action = '';
179
+
180
+	/**
181
+	 * unprocessed value for the 'page' request param (default '')
182
+	 *
183
+	 * @var string
184
+	 */
185
+	protected string $raw_req_page = '';
186
+
187
+	public string $page_folder  = '';
188
+
189
+	public string $page_label   = '';
190
+
191
+	public string $page_slug    = '';
192
+
193
+
194
+	/**
195
+	 * the current page route and route config
196
+	 *
197
+	 * @var array|callable|string|null
198
+	 */
199
+	protected $_route = null;
200
+
201
+
202
+	/**
203
+	 * @Constructor
204
+	 * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
205
+	 * @throws InvalidArgumentException
206
+	 * @throws InvalidDataTypeException
207
+	 * @throws InvalidInterfaceException
208
+	 * @throws ReflectionException
209
+	 */
210
+	public function __construct($routing = true)
211
+	{
212
+		$this->_routing = $routing;
213
+
214
+		$this->loader       = LoaderFactory::getLoader();
215
+		$this->admin_config = $this->loader->getShared(EE_Admin_Config::class);
216
+		$this->feature      = $this->loader->getShared(FeatureFlags::class);
217
+		$this->request      = $this->loader->getShared(RequestInterface::class);
218
+		$this->capabilities = $this->loader->getShared(EE_Capabilities::class);
219
+
220
+		$this->class_name      = get_class($this);
221
+		$this->base_class_name = strpos($this->class_name, 'Extend_') === 0
222
+			? str_replace('Extend_', '', $this->class_name)
223
+			: '';
224
+
225
+		if (strpos($this->_get_dir(), 'caffeinated') !== false) {
226
+			$this->_is_caf = true;
227
+		}
228
+		$this->_yes_no_values = [
229
+			['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
230
+			['id' => false, 'text' => esc_html__('No', 'event_espresso')],
231
+		];
232
+		// set the _req_data property.
233
+		$this->_req_data = $this->request->requestParams();
234
+	}
235
+
236
+
237
+	/**
238
+	 * @return EE_Admin_Config
239
+	 */
240
+	public function adminConfig(): EE_Admin_Config
241
+	{
242
+		return $this->admin_config;
243
+	}
244
+
245
+
246
+	public function capabilities(): EE_Capabilities
247
+	{
248
+		if (! $this->capabilities instanceof EE_Capabilities) {
249
+			$this->capabilities = $this->loader->getShared(EE_Capabilities::class);
250
+		}
251
+		return $this->capabilities;
252
+	}
253
+
254
+
255
+	/**
256
+	 * @return FeatureFlags
257
+	 */
258
+	public function feature(): FeatureFlags
259
+	{
260
+		return $this->feature;
261
+	}
262
+
263
+
264
+	/**
265
+	 * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
266
+	 * for child classes that needed to set properties prior to these methods getting called,
267
+	 * but also needed the parent class to have its construction completed as well.
268
+	 * Bottom line is that constructors should ONLY be used for setting initial properties
269
+	 * and any complex initialization logic should only run after instantiation is complete.
270
+	 * This method gets called immediately after construction from within
271
+	 *      EE_Admin_Page_Init::_initialize_admin_page()
272
+	 *
273
+	 * @throws EE_Error
274
+	 * @throws InvalidArgumentException
275
+	 * @throws InvalidDataTypeException
276
+	 * @throws InvalidInterfaceException
277
+	 * @throws ReflectionException
278
+	 * @throws Throwable
279
+	 * @since 5.0.0.p
280
+	 */
281
+	public function initializePage()
282
+	{
283
+		if ($this->initialized) {
284
+			return;
285
+		}
286
+		// set initial page props (child method)
287
+		$this->_init_page_props();
288
+		// set global defaults
289
+		$this->_set_defaults();
290
+		// set early because incoming requests could be ajax related and we need to register those hooks.
291
+		if ($this->request->isAjax()) {
292
+			$this->_global_ajax_hooks();
293
+			$this->_ajax_hooks();
294
+		}
295
+		// other_page_hooks have to be early too.
296
+		$this->_do_other_page_hooks();
297
+		// set up page dependencies
298
+		$this->_before_page_setup();
299
+		$this->_page_setup();
300
+		$this->initialized = true;
301
+	}
302
+
303
+
304
+	/**
305
+	 * _init_page_props
306
+	 * Child classes use to set at least the following properties:
307
+	 * $page_slug.
308
+	 * $page_label.
309
+	 *
310
+	 * @abstract
311
+	 * @return void
312
+	 */
313
+	abstract protected function _init_page_props();
314
+
315
+
316
+	/**
317
+	 * _ajax_hooks
318
+	 * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
319
+	 * Note: within the ajax callback methods.
320
+	 *
321
+	 * @abstract
322
+	 * @return void
323
+	 */
324
+	abstract protected function _ajax_hooks();
325
+
326
+
327
+	/**
328
+	 * _define_page_props
329
+	 * child classes define page properties in here.  Must include at least:
330
+	 * $_admin_base_url = base_url for all admin pages
331
+	 * $_admin_page_title = default admin_page_title for admin pages
332
+	 * $_labels = array of default labels for various automatically generated elements:
333
+	 *    array(
334
+	 *        'buttons' => array(
335
+	 *            'add' => esc_html__('label for add new button'),
336
+	 *            'edit' => esc_html__('label for edit button'),
337
+	 *            'delete' => esc_html__('label for delete button')
338
+	 *            )
339
+	 *        )
340
+	 *
341
+	 * @abstract
342
+	 * @return void
343
+	 */
344
+	abstract protected function _define_page_props();
345
+
346
+
347
+	/**
348
+	 * _set_page_routes
349
+	 * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
350
+	 * assigned to an action => method pairs in an array and to the $_page_routes property.  Each page route must also
351
+	 * have a 'default' route. Here's the format
352
+	 * $this->_page_routes = array(
353
+	 *        'default' => array(
354
+	 *            'func' => '_default_method_handling_route',
355
+	 *            'args' => array('array','of','args'),
356
+	 *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
357
+	 *            ajax request, backend processing)
358
+	 *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
359
+	 *            headers route after.  The string you enter here should match the defined route reference for a
360
+	 *            headers sent route.
361
+	 *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
362
+	 *            this route.
363
+	 *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
364
+	 *            checks).
365
+	 *        ),
366
+	 *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
367
+	 *        handling method.
368
+	 *        )
369
+	 * )
370
+	 *
371
+	 * @abstract
372
+	 * @return void
373
+	 */
374
+	abstract protected function _set_page_routes();
375
+
376
+
377
+	/**
378
+	 * _set_page_config
379
+	 * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
380
+	 * array corresponds to the page_route for the loaded page. Format:
381
+	 * $this->_page_config = array(
382
+	 *        'default' => array(
383
+	 *            'labels' => array(
384
+	 *                'buttons' => array(
385
+	 *                    'add' => esc_html__('label for adding item'),
386
+	 *                    'edit' => esc_html__('label for editing item'),
387
+	 *                    'delete' => esc_html__('label for deleting item')
388
+	 *                ),
389
+	 *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
390
+	 *            ), //optional an array of custom labels for various automatically generated elements to use on the
391
+	 *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
392
+	 *            _define_page_props() method
393
+	 *            'nav' => array(
394
+	 *                'label' => esc_html__('Label for Tab', 'event_espresso').
395
+	 *                'url' => 'http://someurl', //automatically generated UNLESS you define
396
+	 *                'css_class' => 'css-class', //automatically generated UNLESS you define
397
+	 *                'order' => 10, //required to indicate tab position.
398
+	 *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
399
+	 *                displayed then add this parameter.
400
+	 *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
401
+	 *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
402
+	 *            metaboxes set for eventespresso admin pages.
403
+	 *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
404
+	 *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
405
+	 *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
406
+	 *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
407
+	 *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
408
+	 *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
409
+	 *            array indicates the max number of columns (4) and the default number of columns on page load (2).
410
+	 *            There is an option in the "screen_options" dropdown that is set up so users can pick what columns they
411
+	 *            want to display.
412
+	 *            'help_tabs' => array( //this is used for adding help tabs to a page
413
+	 *                'tab_id' => array(
414
+	 *                    'title' => 'tab_title',
415
+	 *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
416
+	 *                    help tab content.  The fallback if it isn't present is to try the callback.  Filename
417
+	 *                    should match a file in the admin folder's "help_tabs" dir (ie..
418
+	 *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
419
+	 *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
420
+	 *                    attempt to use the callback which should match the name of a method in the class
421
+	 *                    ),
422
+	 *                'tab2_id' => array(
423
+	 *                    'title' => 'tab2 title',
424
+	 *                    'filename' => 'file_name_2'
425
+	 *                    'callback' => 'callback_method_for_content',
426
+	 *                 ),
427
+	 *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
428
+	 *            help tab area on an admin page. @return void
429
+	 *
430
+	 * @abstract
431
+	 */
432
+	abstract protected function _set_page_config();
433
+
434
+
435
+	/**
436
+	 * _add_screen_options
437
+	 * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
438
+	 * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
439
+	 * to a particular view.
440
+	 *
441
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
442
+	 *         see also WP_Screen object documents...
443
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
444
+	 * @abstract
445
+	 * @return void
446
+	 */
447
+	abstract protected function _add_screen_options();
448
+
449
+
450
+	/**
451
+	 * _add_feature_pointers
452
+	 * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
453
+	 * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
454
+	 * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
455
+	 * WP_Internal_Pointers class in wp-admin/includes/template.php for example (it's a final class so can't be
456
+	 * extended) also see:
457
+	 *
458
+	 * @link   http://eamann.com/tech/wordpress-portland/
459
+	 * @abstract
460
+	 * @return void
461
+	 */
462
+	abstract protected function _add_feature_pointers();
463
+
464
+
465
+	/**
466
+	 * load_scripts_styles
467
+	 * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
468
+	 * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
469
+	 * scripts/styles per view by putting them in a dynamic function in this format
470
+	 * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
471
+	 *
472
+	 * @abstract
473
+	 * @return void
474
+	 */
475
+	abstract public function load_scripts_styles();
476
+
477
+
478
+	/**
479
+	 * admin_init
480
+	 * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
481
+	 * all pages/views loaded by child class.
482
+	 *
483
+	 * @abstract
484
+	 * @return void
485
+	 */
486
+	abstract public function admin_init();
487
+
488
+
489
+	/**
490
+	 * admin_notices
491
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
492
+	 * all pages/views loaded by child class.
493
+	 *
494
+	 * @abstract
495
+	 * @return void
496
+	 */
497
+	abstract public function admin_notices();
498
+
499
+
500
+	/**
501
+	 * admin_footer_scripts
502
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
503
+	 * will apply to all pages/views loaded by child class.
504
+	 *
505
+	 * @return void
506
+	 */
507
+	abstract public function admin_footer_scripts();
508
+
509
+
510
+	/**
511
+	 * admin_footer
512
+	 * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
513
+	 * apply to all pages/views loaded by child class.
514
+	 *
515
+	 * @return void
516
+	 */
517
+	public function admin_footer()
518
+	{
519
+	}
520
+
521
+
522
+	/**
523
+	 * _global_ajax_hooks
524
+	 * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
525
+	 * Note: within the ajax callback methods.
526
+	 *
527
+	 * @abstract
528
+	 * @return void
529
+	 */
530
+	protected function _global_ajax_hooks()
531
+	{
532
+		// for lazy loading of metabox content
533
+		add_action('wp_ajax_espresso-ajax-content', [$this, 'ajax_metabox_content']);
534
+
535
+		add_action(
536
+			'wp_ajax_espresso_hide_status_change_notice',
537
+			[$this, 'hideStatusChangeNotice']
538
+		);
539
+		add_action(
540
+			'wp_ajax_nopriv_espresso_hide_status_change_notice',
541
+			[$this, 'hideStatusChangeNotice']
542
+		);
543
+	}
544
+
545
+
546
+	public function ajax_metabox_content()
547
+	{
548
+		$content_id  = $this->request->getRequestParam('contentid', '');
549
+		$content_url = $this->request->getRequestParam('contenturl', '', DataType::URL);
550
+		EE_Admin_Page::cached_rss_display($content_id, $content_url);
551
+		wp_die();
552
+	}
553
+
554
+
555
+	public function hideStatusChangeNotice()
556
+	{
557
+		$response = [];
558
+		try {
559
+			/** @var StatusChangeNotice $status_change_notice */
560
+			$status_change_notice = $this->loader->getShared(
561
+				'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
562
+			);
563
+			$response['success']  = $status_change_notice->dismiss() > -1;
564
+		} catch (Exception $exception) {
565
+			$response['errors'] = $exception->getMessage();
566
+		}
567
+		wp_send_json($response);
568
+	}
569
+
570
+
571
+	/**
572
+	 * allows extending classes do something specific before the parent constructor runs _page_setup().
573
+	 *
574
+	 * @return void
575
+	 */
576
+	protected function _before_page_setup()
577
+	{
578
+		// default is to do nothing
579
+	}
580
+
581
+
582
+	/**
583
+	 * Makes sure any things that need to be loaded early get handled.
584
+	 * We also escape early here if the page requested doesn't match the object.
585
+	 *
586
+	 * @final
587
+	 * @return void
588
+	 * @throws EE_Error
589
+	 * @throws InvalidArgumentException
590
+	 * @throws ReflectionException
591
+	 * @throws InvalidDataTypeException
592
+	 * @throws InvalidInterfaceException
593
+	 * @throws Throwable
594
+	 */
595
+	final protected function _page_setup()
596
+	{
597
+		// requires?
598
+		// admin_init stuff - global - we're setting this REALLY early
599
+		// so if EE_Admin pages have to hook into other WP pages they can.
600
+		// But keep in mind, not everything is available from the EE_Admin Page object at this point.
601
+		add_action('admin_init', [$this, 'admin_init_global'], 5);
602
+		// next verify if we need to load anything...
603
+		$this->_current_page = $this->request->getRequestParam('page', '', DataType::KEY);
604
+		$this->_current_page = $this->request->getRequestParam('current_page', $this->_current_page, DataType::KEY);
605
+		$this->page_folder   = strtolower(
606
+			str_replace(['_Admin_Page', 'Extend_'], '', $this->class_name)
607
+		);
608
+		global $ee_menu_slugs;
609
+		$ee_menu_slugs = (array) $ee_menu_slugs;
610
+		if (
611
+			! $this->request->isAjax()
612
+			&& (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
613
+		) {
614
+			return;
615
+		}
616
+		// because WP List tables have two duplicate select inputs for choosing bulk actions,
617
+		// we need to copy the action from the second to the first
618
+		$action     = $this->request->getRequestParam('action', '-1', DataType::KEY);
619
+		$action2    = $this->request->getRequestParam('action2', '-1', DataType::KEY);
620
+		$action     = $action !== '-1' ? $action : $action2;
621
+		$req_action = $action !== '-1' ? $action : 'default';
622
+
623
+		// if a specific 'route' has been set, and the action is 'default' OR we are doing_ajax
624
+		// then let's use the route as the action.
625
+		// This covers cases where we're coming in from a list table that isn't on the default route.
626
+		$route = $this->request->getRequestParam('route');
627
+		$route = $route !== '-1' ? $route : 'default';
628
+		$this->_req_action = $route && ($req_action === 'default' || $this->request->isAjax())
629
+			? $route
630
+			: $req_action;
631
+		$this->_current_view = $this->_req_action;
632
+		$this->_req_nonce    = $this->_req_action . '_nonce';
633
+		$this->_define_page_props();
634
+		$this->_current_page_view_url = add_query_arg(
635
+			['page' => $this->_current_page, 'action' => $this->_current_view],
636
+			$this->_admin_base_url
637
+		);
638
+		// set page configs
639
+		$this->_set_page_routes();
640
+		$this->_set_page_config();
641
+		// let's include any referrer data in our default_query_args for this route for "stickiness".
642
+		if ($this->request->requestParamIsSet('wp_referer')) {
643
+			$wp_referer = $this->request->getRequestParam('wp_referer');
644
+			if ($wp_referer) {
645
+				$this->_default_route_query_args['wp_referer'] = $wp_referer;
646
+			}
647
+		}
648
+		// for CPT and other extended functionality.
649
+		// If there is an _extend_page_config_for_cpt
650
+		// then let's run that to modify all the various page configuration arrays.
651
+		if (method_exists($this, '_extend_page_config_for_cpt')) {
652
+			$this->_extend_page_config_for_cpt();
653
+		}
654
+		// filter routes and page_config so addons can add their stuff. Filtering done per class
655
+		$this->_page_routes = apply_filters(
656
+			'FHEE__' . $this->class_name . '__page_setup__page_routes',
657
+			$this->_page_routes,
658
+			$this
659
+		);
660
+		$this->_page_config = apply_filters(
661
+			'FHEE__' . $this->class_name . '__page_setup__page_config',
662
+			$this->_page_config,
663
+			$this
664
+		);
665
+		if ($this->base_class_name !== '') {
666
+			$this->_page_routes = apply_filters(
667
+				'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
668
+				$this->_page_routes,
669
+				$this
670
+			);
671
+			$this->_page_config = apply_filters(
672
+				'FHEE__' . $this->base_class_name . '__page_setup__page_config',
673
+				$this->_page_config,
674
+				$this
675
+			);
676
+		}
677
+		// if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
678
+		// then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
679
+		if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
680
+			add_action(
681
+				'AHEE__EE_Admin_Page__route_admin_request',
682
+				[$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
683
+				10,
684
+				2
685
+			);
686
+		}
687
+		// next route only if routing enabled
688
+		if ($this->_routing && ! $this->request->isAjax()) {
689
+			$this->_verify_routes();
690
+			// next let's just check user_access and kill if no access
691
+			$this->check_user_access();
692
+			if ($this->_is_UI_request) {
693
+				// admin_init stuff - global, all views for this page class, specific view
694
+				add_action('admin_init', [$this, 'admin_init']);
695
+				if (method_exists($this, 'admin_init_' . $this->_current_view)) {
696
+					add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
697
+				}
698
+			} else {
699
+				// hijack regular WP loading and route admin request immediately
700
+				@ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
701
+				$this->route_admin_request();
702
+			}
703
+		}
704
+	}
705
+
706
+
707
+	/**
708
+	 * Provides a way for related child admin pages to load stuff on the loaded admin page.
709
+	 *
710
+	 * @return void
711
+	 * @throws EE_Error
712
+	 */
713
+	private function _do_other_page_hooks()
714
+	{
715
+		$registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
716
+		foreach ($registered_pages as $page) {
717
+			// now let's set up the file name and class that should be present
718
+			$classname = str_replace('.class.php', '', $page);
719
+			// autoloaders should take care of loading file
720
+			if (! class_exists($classname)) {
721
+				$error_msg[] = sprintf(
722
+					esc_html__(
723
+						'Something went wrong with loading the %s admin hooks page.',
724
+						'event_espresso'
725
+					),
726
+					$page
727
+				);
728
+				$error_msg[] = $error_msg[0]
729
+							   . "\r\n"
730
+							   . sprintf(
731
+								   esc_html__(
732
+									   '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',
733
+									   'event_espresso'
734
+								   ),
735
+								   $page,
736
+								   '<br />',
737
+								   '<strong>' . $classname . '</strong>'
738
+							   );
739
+				throw new EE_Error(implode('||', $error_msg));
740
+			}
741
+			// don't load the same class twice
742
+			static $loaded = [];
743
+			if (in_array($classname, $loaded, true)) {
744
+				continue;
745
+			}
746
+			$loaded[] = $classname;
747
+			// notice we are passing the instance of this class to the hook object.
748
+			$this->loader->getShared($classname, [$this]);
749
+		}
750
+	}
751
+
752
+
753
+	/**
754
+	 * @throws ReflectionException
755
+	 * @throws EE_Error
756
+	 */
757
+	public function load_page_dependencies()
758
+	{
759
+		try {
760
+			$this->_load_page_dependencies();
761
+		} catch (EE_Error $e) {
762
+			$e->get_error();
763
+		}
764
+	}
765
+
766
+
767
+	/**
768
+	 * load_page_dependencies
769
+	 * loads things specific to this page class when it's loaded.  Really helps with efficiency.
770
+	 *
771
+	 * @return void
772
+	 * @throws DomainException
773
+	 * @throws EE_Error
774
+	 * @throws InvalidArgumentException
775
+	 * @throws InvalidDataTypeException
776
+	 * @throws InvalidInterfaceException
777
+	 * @throws ReflectionException
778
+	 */
779
+	protected function _load_page_dependencies()
780
+	{
781
+		// let's set the current_screen and screen options to override what WP set
782
+		$this->_current_screen = get_current_screen();
783
+		// load admin_notices - global, page class, and view specific
784
+		add_action('admin_notices', [$this, 'admin_notices_global'], 5);
785
+		add_action('admin_notices', [$this, 'admin_notices']);
786
+		if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
787
+			add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
788
+		}
789
+		// load network admin_notices - global, page class, and view specific
790
+		add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
791
+		if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
792
+			add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
793
+		}
794
+		// this will save any per_page screen options if they are present
795
+		$this->_set_per_page_screen_options();
796
+		// setup list table properties
797
+		$this->_set_list_table();
798
+		// child classes can "register" a metabox to be automatically handled via the _page_config array property.
799
+		// However in some cases the metaboxes will need to be added within a route handling callback.
800
+		add_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
801
+		// hack because promos admin was loading the edited promotion object in the metaboxes callback
802
+		// which should NOT be generated on non-UI requests like POST updates/inserts
803
+		if (
804
+			$this->class_name === 'Promotions_Admin_Page'
805
+			&& ($this->_req_action === 'edit' || $this->_req_action === 'create_new')
806
+		) {
807
+			$this->addRegisteredMetaBoxes();
808
+		}
809
+		$this->_add_screen_columns();
810
+		// add screen options - global, page child class, and view specific
811
+		$this->_add_global_screen_options();
812
+		$this->_add_screen_options();
813
+		$add_screen_options = "_add_screen_options_$this->_current_view";
814
+		if (method_exists($this, $add_screen_options)) {
815
+			$this->{$add_screen_options}();
816
+		}
817
+		// add help tab(s) - set via page_config and qtips.
818
+		$this->_add_help_tabs();
819
+		$this->_add_qtips();
820
+		// add feature_pointers - global, page child class, and view specific
821
+		$this->_add_feature_pointers();
822
+		$this->_add_global_feature_pointers();
823
+		$add_feature_pointer = "_add_feature_pointer_$this->_current_view";
824
+		if (method_exists($this, $add_feature_pointer)) {
825
+			$this->{$add_feature_pointer}();
826
+		}
827
+		// enqueue scripts/styles - global, page class, and view specific
828
+		add_action('admin_enqueue_scripts', [$this, 'admin_footer_scripts_eei18n_js_strings'], 1);
829
+		add_action('admin_enqueue_scripts', [$this, 'load_global_scripts_styles'], 5);
830
+		add_action('admin_enqueue_scripts', [$this, 'load_scripts_styles']);
831
+		if (method_exists($this, "load_scripts_styles_$this->_current_view")) {
832
+			add_action('admin_enqueue_scripts', [$this, "load_scripts_styles_$this->_current_view"], 15);
833
+		}
834
+		// admin_print_footer_scripts - global, page child class, and view specific.
835
+		// NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
836
+		// In most cases that's doing_it_wrong().  But adding hidden container elements etc.
837
+		// is a good use case. Notice the late priority we're giving these
838
+		add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts_global'], 99);
839
+		add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts'], 100);
840
+		if (method_exists($this, "admin_footer_scripts_$this->_current_view")) {
841
+			add_action('admin_print_footer_scripts', [$this, "admin_footer_scripts_$this->_current_view"], 101);
842
+		}
843
+		// admin footer scripts
844
+		add_action('admin_footer', [$this, 'admin_footer_global'], 99);
845
+		add_action('admin_footer', [$this, 'admin_footer'], 100);
846
+		if (method_exists($this, "admin_footer_$this->_current_view")) {
847
+			add_action('admin_footer', [$this, "admin_footer_$this->_current_view"], 101);
848
+		}
849
+		do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
850
+		// targeted hook
851
+		do_action(
852
+			"FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__$this->_req_action"
853
+		);
854
+	}
855
+
856
+
857
+	/**
858
+	 * _set_defaults
859
+	 * This sets some global defaults for class properties.
860
+	 */
861
+	private function _set_defaults()
862
+	{
863
+		// init template args
864
+		$this->set_template_args(
865
+			[
866
+				'admin_page_header'  => '',
867
+				'admin_page_content' => '',
868
+				'post_body_content'  => '',
869
+				'before_list_table'  => '',
870
+				'after_list_table'   => '',
871
+			]
872
+		);
873
+	}
874
+
875
+
876
+	/**
877
+	 * route_admin_request
878
+	 *
879
+	 * @return void
880
+	 * @throws InvalidArgumentException
881
+	 * @throws InvalidInterfaceException
882
+	 * @throws InvalidDataTypeException
883
+	 * @throws EE_Error
884
+	 * @throws ReflectionException
885
+	 * @throws Throwable
886
+	 * @see    _route_admin_request()
887
+	 */
888
+	public function route_admin_request()
889
+	{
890
+		try {
891
+			$this->_route_admin_request();
892
+		} catch (EE_Error $e) {
893
+			$e->get_error();
894
+		}
895
+	}
896
+
897
+
898
+	public function set_wp_page_slug($wp_page_slug)
899
+	{
900
+		$this->_wp_page_slug = $wp_page_slug;
901
+		// if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
902
+		if (is_network_admin()) {
903
+			$this->_wp_page_slug .= '-network';
904
+		}
905
+	}
906
+
907
+
908
+	/**
909
+	 * _verify_routes
910
+	 * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
911
+	 * we know if we need to drop out.
912
+	 *
913
+	 * @return bool
914
+	 * @throws EE_Error
915
+	 */
916
+	protected function _verify_routes(): bool
917
+	{
918
+		if (! $this->_current_page && ! $this->request->isAjax()) {
919
+			return false;
920
+		}
921
+		// check that the page_routes array is not empty
922
+		if (empty($this->_page_routes)) {
923
+			// user error msg
924
+			$error_msg = sprintf(
925
+				esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
926
+				$this->_admin_page_title
927
+			);
928
+			// developer error msg
929
+			$error_msg .= '||' . $error_msg
930
+						  . esc_html__(
931
+							  ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
932
+							  'event_espresso'
933
+						  );
934
+			throw new EE_Error($error_msg);
935
+		}
936
+		// route 'editpost' routes to CPT 'edit' routes
937
+		$alt_edit_route = $this instanceof EE_Admin_Page_CPT ? $this->cpt_editpost_route : 'edit';
938
+		if (
939
+			$this->_req_action === 'editpost'
940
+			&& ! isset($this->_page_routes['editpost'])
941
+			&& isset($this->_page_routes[ $alt_edit_route ])
942
+		) {
943
+			$this->_req_action = $alt_edit_route;
944
+		}
945
+		// and that the requested page route exists
946
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
947
+			$this->_route        = $this->_page_routes[ $this->_req_action ];
948
+			$this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
949
+		} else {
950
+			// user error msg
951
+			$error_msg = sprintf(
952
+				esc_html__(
953
+					'The requested page route does not exist for the %s admin page.',
954
+					'event_espresso'
955
+				),
956
+				$this->_admin_page_title
957
+			);
958
+			// developer error msg
959
+			$error_msg .= '||' . $error_msg
960
+						  . sprintf(
961
+							  esc_html__(
962
+								  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
963
+								  'event_espresso'
964
+							  ),
965
+							  $this->_req_action
966
+						  );
967
+			throw new EE_Error($error_msg);
968
+		}
969
+		// and that a default route exists
970
+		if (! array_key_exists('default', $this->_page_routes)) {
971
+			// user error msg
972
+			$error_msg = sprintf(
973
+				esc_html__(
974
+					'A default page route has not been set for the % admin page.',
975
+					'event_espresso'
976
+				),
977
+				$this->_admin_page_title
978
+			);
979
+			// developer error msg
980
+			$error_msg .= '||' . $error_msg
981
+						  . esc_html__(
982
+							  ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
983
+							  'event_espresso'
984
+						  );
985
+			throw new EE_Error($error_msg);
986
+		}
987
+
988
+		// first lets' catch if the UI request has EVER been set.
989
+		if ($this->_is_UI_request === null) {
990
+			// let's set if this is a UI request or not.
991
+			$this->_is_UI_request = ! $this->request->getRequestParam('noheader', false, DataType::BOOL);
992
+			// wait a minute... we might have a noheader in the route array
993
+			$this->_is_UI_request = ! (isset($this->_route['noheader']) && $this->_route['noheader'])
994
+				? $this->_is_UI_request
995
+				: false;
996
+		}
997
+		$this->_set_current_labels();
998
+		return true;
999
+	}
1000
+
1001
+
1002
+	/**
1003
+	 * this method simply verifies a given route and makes sure it's an actual route available for the loaded page
1004
+	 *
1005
+	 * @param string $route the route name we're verifying
1006
+	 * @return bool we'll throw an exception if this isn't a valid route.
1007
+	 * @throws EE_Error
1008
+	 */
1009
+	protected function _verify_route(string $route): bool
1010
+	{
1011
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
1012
+			return true;
1013
+		}
1014
+		// user error msg
1015
+		$error_msg = sprintf(
1016
+			esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
1017
+			$this->_admin_page_title
1018
+		);
1019
+		// developer error msg
1020
+		$error_msg .= '||' . $error_msg
1021
+					  . sprintf(
1022
+						  esc_html__(
1023
+							  ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
1024
+							  'event_espresso'
1025
+						  ),
1026
+						  $route
1027
+					  );
1028
+		throw new EE_Error($error_msg);
1029
+	}
1030
+
1031
+
1032
+	/**
1033
+	 * perform nonce verification
1034
+	 * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
1035
+	 * using this method (and save retyping!)
1036
+	 *
1037
+	 * @param string $nonce     The nonce sent
1038
+	 * @param string $nonce_ref The nonce reference string (name0)
1039
+	 * @return void
1040
+	 * @throws EE_Error
1041
+	 * @throws InvalidArgumentException
1042
+	 * @throws InvalidDataTypeException
1043
+	 * @throws InvalidInterfaceException
1044
+	 */
1045
+	protected function _verify_nonce(string $nonce, string $nonce_ref)
1046
+	{
1047
+		// verify nonce against expected value
1048
+		if (! wp_verify_nonce($nonce, $nonce_ref)) {
1049
+			// these are not the droids you are looking for !!!
1050
+			$msg = sprintf(
1051
+				esc_html__('%sNonce Fail.%s', 'event_espresso'),
1052
+				'<a href="https://www.youtube.com/watch?v=56_S0WeTkzs">',
1053
+				'</a>'
1054
+			);
1055
+			if (WP_DEBUG) {
1056
+				$msg .= "\n  ";
1057
+				$msg .= sprintf(
1058
+					esc_html__(
1059
+						'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
1060
+						'event_espresso'
1061
+					),
1062
+					__CLASS__
1063
+				);
1064
+			}
1065
+			if (! $this->request->isAjax()) {
1066
+				wp_die($msg);
1067
+			}
1068
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1069
+			$this->_return_json();
1070
+		}
1071
+	}
1072
+
1073
+
1074
+	/**
1075
+	 * _route_admin_request()
1076
+	 * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1077
+	 * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1078
+	 * in the page routes and then will try to load the corresponding method.
1079
+	 *
1080
+	 * @return void
1081
+	 * @throws EE_Error
1082
+	 * @throws InvalidArgumentException
1083
+	 * @throws InvalidDataTypeException
1084
+	 * @throws InvalidInterfaceException
1085
+	 * @throws ReflectionException
1086
+	 * @throws Throwable
1087
+	 */
1088
+	protected function _route_admin_request()
1089
+	{
1090
+		if (! $this->_is_UI_request) {
1091
+			$this->_verify_routes();
1092
+		}
1093
+		$nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
1094
+		if ($this->_req_action !== 'default' && $nonce_check) {
1095
+			// set nonce from post data
1096
+			$nonce = $this->request->getRequestParam($this->_req_nonce, '');
1097
+			$this->_verify_nonce($nonce, $this->_req_nonce);
1098
+		}
1099
+		// set the nav_tabs array but ONLY if this is  UI_request
1100
+		if ($this->_is_UI_request) {
1101
+			$this->_set_nav_tabs();
1102
+		}
1103
+		// grab callback function
1104
+		$func = $this->_route['func'] ?? $this->_route;
1105
+		// check if callback has args
1106
+		$args = $this->_route['args'] ?? [];
1107
+
1108
+		// action right before calling route
1109
+		// (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1110
+		if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1111
+			do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1112
+		}
1113
+		// strip _wp_http_referer from the server REQUEST_URI
1114
+		// else it grows in length on every submission due to recursion,
1115
+		// ultimately causing a "Request-URI Too Large" error
1116
+		$this->request->unSetRequestParam('_wp_http_referer');
1117
+		$this->request->unSetServerParam('_wp_http_referer');
1118
+		$cleaner_request_uri = remove_query_arg(
1119
+			'_wp_http_referer',
1120
+			wp_unslash($this->request->getServerParam('REQUEST_URI'))
1121
+		);
1122
+		$this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1123
+		$this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1124
+		$route_callback = [];
1125
+		if (! empty($func)) {
1126
+			if (is_array($func) && is_callable($func)) {
1127
+				$route_callback = $func;
1128
+			} elseif (is_string($func)) {
1129
+				if (strpos($func, '::') !== false) {
1130
+					$route_callback = explode('::', $func);
1131
+				} elseif (method_exists($this, $func)) {
1132
+					$route_callback = [$this, $func];
1133
+				} else {
1134
+					$route_callback = $func;
1135
+				}
1136
+			}
1137
+			[$class, $method] = $route_callback;
1138
+			// is it neither a class method NOR a standalone function?
1139
+			if (! is_callable($route_callback)) {
1140
+				// user error msg
1141
+				$error_msg = esc_html__(
1142
+					'An error occurred. The  requested page route could not be found.',
1143
+					'event_espresso'
1144
+				);
1145
+				// developer error msg
1146
+				$error_msg .= '||';
1147
+				$error_msg .= sprintf(
1148
+					esc_html__(
1149
+						'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1150
+						'event_espresso'
1151
+					),
1152
+					$method
1153
+				);
1154
+				throw new DomainException($error_msg);
1155
+			}
1156
+			if ($class !== $this && ! in_array($this, $args)) {
1157
+				// send along this admin page object for access by addons.
1158
+				$args['admin_page'] = $this;
1159
+			}
1160
+
1161
+			call_user_func_array($route_callback, $args);
1162
+		}
1163
+		// if we've routed and this route has a no headers route AND a sent_headers_route,
1164
+		// then we need to reset the routing properties to the new route.
1165
+		// 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.
1166
+		if (
1167
+			$this->_is_UI_request === false
1168
+			&& is_array($this->_route)
1169
+			&& ! empty($this->_route['headers_sent_route'])
1170
+		) {
1171
+			$this->_reset_routing_properties($this->_route['headers_sent_route']);
1172
+		}
1173
+	}
1174
+
1175
+
1176
+	/**
1177
+	 * This method just allows the resetting of page properties in the case where a no headers
1178
+	 * route redirects to a headers route in its route config.
1179
+	 *
1180
+	 * @param string $new_route New (non header) route to redirect to.
1181
+	 * @return void
1182
+	 * @throws ReflectionException
1183
+	 * @throws InvalidArgumentException
1184
+	 * @throws InvalidInterfaceException
1185
+	 * @throws InvalidDataTypeException
1186
+	 * @throws EE_Error
1187
+	 * @throws Throwable
1188
+	 * @since   4.3.0
1189
+	 */
1190
+	protected function _reset_routing_properties(string $new_route)
1191
+	{
1192
+		$this->_is_UI_request = true;
1193
+		// now we set the current route to whatever the headers_sent_route is set at
1194
+		$this->request->setRequestParam('action', $new_route);
1195
+		// rerun page setup
1196
+		$this->_page_setup();
1197
+	}
1198
+
1199
+
1200
+	/**
1201
+	 * _add_query_arg
1202
+	 * adds nonce to array of arguments then calls WP add_query_arg function
1203
+	 *(internally just uses EEH_URL's function with the same name)
1204
+	 *
1205
+	 * @param array  $args
1206
+	 * @param string $url
1207
+	 * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1208
+	 *                                        generated url in an associative array indexed by the key 'wp_referer';
1209
+	 *                                        Example usage: If the current page is:
1210
+	 *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1211
+	 *                                        &action=default&event_id=20&month_range=March%202015
1212
+	 *                                        &_wpnonce=5467821
1213
+	 *                                        and you call:
1214
+	 *                                        EE_Admin_Page::add_query_args_and_nonce(
1215
+	 *                                        array(
1216
+	 *                                        'action' => 'resend_something',
1217
+	 *                                        'page=>espresso_registrations'
1218
+	 *                                        ),
1219
+	 *                                        $some_url,
1220
+	 *                                        true
1221
+	 *                                        );
1222
+	 *                                        It will produce a URL in this structure:
1223
+	 *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1224
+	 *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1225
+	 *                                        month_range]=March%202015
1226
+	 * @param bool   $exclude_nonce           If true, the nonce will be excluded from the generated nonce.
1227
+	 * @param int    $context
1228
+	 * @return string
1229
+	 */
1230
+	public static function add_query_args_and_nonce(
1231
+		array $args = [],
1232
+		string $url = '',
1233
+		bool $sticky = false,
1234
+		bool $exclude_nonce = false,
1235
+		int $context = EEH_URL::CONTEXT_NONE
1236
+	): string {
1237
+		// if there is a _wp_http_referer include the values from the request but only if sticky = true
1238
+		if ($sticky) {
1239
+			/** @var RequestInterface $request */
1240
+			$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1241
+			$request->unSetRequestParams(['_wp_http_referer', 'wp_referer'], true);
1242
+			$request->unSetServerParam('_wp_http_referer', true);
1243
+			foreach ($request->requestParams() as $key => $value) {
1244
+				// do not add nonces
1245
+				if (strpos($key, 'nonce') !== false) {
1246
+					continue;
1247
+				}
1248
+				$args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1249
+			}
1250
+		}
1251
+		return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce, $context);
1252
+	}
1253
+
1254
+
1255
+	/**
1256
+	 * This returns a generated link that will load the related help tab.
1257
+	 *
1258
+	 * @param string $help_tab_id the id for the connected help tab
1259
+	 * @param string $icon_style  (optional) include css class for the style you want to use for the help icon.
1260
+	 * @param string $help_text   (optional) send help text you want to use for the link if default not to be used
1261
+	 * @return string              generated link
1262
+	 * @uses EEH_Template::get_help_tab_link()
1263
+	 */
1264
+	protected function _get_help_tab_link(string $help_tab_id, string $icon_style = '', string $help_text = ''): string
1265
+	{
1266
+		return EEH_Template::get_help_tab_link(
1267
+			$help_tab_id,
1268
+			$this->page_slug,
1269
+			$this->_req_action,
1270
+			$icon_style,
1271
+			$help_text
1272
+		);
1273
+	}
1274
+
1275
+
1276
+	/**
1277
+	 * _add_help_tabs
1278
+	 * Note child classes define their help tabs within the page_config array.
1279
+	 *
1280
+	 * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1281
+	 * @return void
1282
+	 * @throws DomainException
1283
+	 * @throws EE_Error
1284
+	 * @throws ReflectionException
1285
+	 */
1286
+	protected function _add_help_tabs()
1287
+	{
1288
+		if (isset($this->_page_config[ $this->_req_action ])) {
1289
+			$config = $this->_page_config[ $this->_req_action ];
1290
+			// let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1291
+			if (is_array($config) && isset($config['help_sidebar'])) {
1292
+				// check that the callback given is valid
1293
+				if (! method_exists($this, $config['help_sidebar'])) {
1294
+					throw new EE_Error(
1295
+						sprintf(
1296
+							esc_html__(
1297
+								'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Double check the spelling and make sure this method exists for the class %s',
1298
+								'event_espresso'
1299
+							),
1300
+							$config['help_sidebar'],
1301
+							$this->class_name
1302
+						)
1303
+					);
1304
+				}
1305
+				$content = apply_filters(
1306
+					'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1307
+					$this->{$config['help_sidebar']}()
1308
+				);
1309
+				$this->_current_screen->set_help_sidebar($content);
1310
+			}
1311
+			if (! isset($config['help_tabs'])) {
1312
+				return;
1313
+			} //no help tabs for this route
1314
+			foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1315
+				// we're here so there ARE help tabs!
1316
+				// make sure we've got what we need
1317
+				if (! isset($cfg['title'])) {
1318
+					throw new EE_Error(
1319
+						esc_html__(
1320
+							'The _page_config array is not set up properly for help tabs.  It is missing a title',
1321
+							'event_espresso'
1322
+						)
1323
+					);
1324
+				}
1325
+				if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1326
+					throw new EE_Error(
1327
+						esc_html__(
1328
+							'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',
1329
+							'event_espresso'
1330
+						)
1331
+					);
1332
+				}
1333
+				// first priority goes to content.
1334
+				if (! empty($cfg['content'])) {
1335
+					$content = $cfg['content'];
1336
+					// second priority goes to filename
1337
+				} elseif (! empty($cfg['filename'])) {
1338
+					$file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1339
+					// 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)
1340
+					$file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1341
+															 . basename($this->_get_dir())
1342
+															 . '/help_tabs/'
1343
+															 . $cfg['filename']
1344
+															 . '.help_tab.php' : $file_path;
1345
+					// if file is STILL not readable then let's do an EE_Error so its more graceful than a fatal error.
1346
+					if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1347
+						EE_Error::add_error(
1348
+							sprintf(
1349
+								esc_html__(
1350
+									'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',
1351
+									'event_espresso'
1352
+								),
1353
+								$tab_id,
1354
+								key($config),
1355
+								$file_path
1356
+							),
1357
+							__FILE__,
1358
+							__FUNCTION__,
1359
+							__LINE__
1360
+						);
1361
+						return;
1362
+					}
1363
+					$template_args['admin_page_obj'] = $this;
1364
+					$content                         = EEH_Template::display_template(
1365
+						$file_path,
1366
+						$template_args,
1367
+						true
1368
+					);
1369
+				} else {
1370
+					$content = '';
1371
+				}
1372
+				// check if callback is valid
1373
+				if (
1374
+					empty($content)
1375
+					&& (
1376
+						! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1377
+					)
1378
+				) {
1379
+					EE_Error::add_error(
1380
+						sprintf(
1381
+							esc_html__(
1382
+								'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.',
1383
+								'event_espresso'
1384
+							),
1385
+							$cfg['title']
1386
+						),
1387
+						__FILE__,
1388
+						__FUNCTION__,
1389
+						__LINE__
1390
+					);
1391
+					return;
1392
+				}
1393
+				// setup config array for help tab method
1394
+				$id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1395
+				$_ht = [
1396
+					'id'       => $id,
1397
+					'title'    => $cfg['title'],
1398
+					'callback' => isset($cfg['callback']) && empty($content) ? [$this, $cfg['callback']] : null,
1399
+					'content'  => $content,
1400
+				];
1401
+				$this->_current_screen->add_help_tab($_ht);
1402
+			}
1403
+		}
1404
+	}
1405
+
1406
+
1407
+	/**
1408
+	 * This simply sets up any qtips that have been defined in the page config
1409
+	 *
1410
+	 * @return void
1411
+	 * @throws ReflectionException
1412
+	 * @throws EE_Error
1413
+	 */
1414
+	protected function _add_qtips()
1415
+	{
1416
+		if (isset($this->_route_config['qtips'])) {
1417
+			$qtips = (array) $this->_route_config['qtips'];
1418
+			// load qtip loader
1419
+			$path = [
1420
+				$this->_get_dir() . '/qtips/',
1421
+				EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1422
+			];
1423
+			EEH_Qtip_Loader::instance()->register($qtips, $path);
1424
+		}
1425
+	}
1426
+
1427
+
1428
+	/**
1429
+	 * _set_nav_tabs
1430
+	 * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1431
+	 * wish to add additional tabs or modify accordingly.
1432
+	 *
1433
+	 * @return void
1434
+	 * @throws InvalidArgumentException
1435
+	 * @throws InvalidInterfaceException
1436
+	 * @throws InvalidDataTypeException
1437
+	 */
1438
+	protected function _set_nav_tabs()
1439
+	{
1440
+		$i        = 0;
1441
+		$only_tab = count($this->_page_config) < 2;
1442
+		foreach ($this->_page_config as $slug => $config) {
1443
+			if (! is_array($config) || empty($config['nav'])) {
1444
+				continue;
1445
+			}
1446
+			// no nav tab for this config
1447
+			// check for persistent flag
1448
+			if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1449
+				// nav tab is only to appear when route requested.
1450
+				continue;
1451
+			}
1452
+			if (! $this->check_user_access($slug, true)) {
1453
+				// no nav tab because current user does not have access.
1454
+				continue;
1455
+			}
1456
+			$css_class = $config['css_class'] ?? '';
1457
+			$css_class .= $only_tab ? ' ee-only-tab' : '';
1458
+			$css_class .= " ee-nav-tab__$slug";
1459
+
1460
+			$this->_nav_tabs[ $slug ] = [
1461
+				'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1462
+					['action' => $slug],
1463
+					$this->_admin_base_url
1464
+				),
1465
+				'link_text' => $this->navTabLabel($config['nav'], $slug),
1466
+				'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1467
+				'order'     => $config['nav']['order'] ?? $i,
1468
+			];
1469
+			$i++;
1470
+		}
1471
+		// if $this->_nav_tabs is empty then lets set the default
1472
+		if (empty($this->_nav_tabs)) {
1473
+			$this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1474
+				'url'       => $this->_admin_base_url,
1475
+				'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1476
+				'css_class' => 'nav-tab-active',
1477
+				'order'     => 10,
1478
+			];
1479
+		}
1480
+		// now let's sort the tabs according to order
1481
+		usort($this->_nav_tabs, [$this, '_sort_nav_tabs']);
1482
+	}
1483
+
1484
+
1485
+	private function navTabLabel(array $nav_tab, string $slug): string
1486
+	{
1487
+		$label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1488
+		$icon  = $nav_tab['icon'] ?? null;
1489
+		$icon  = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1490
+		return '
1491 1491
             <span class="ee-admin-screen-tab__label">
1492 1492
                 ' . $icon . '
1493 1493
                 <span class="ee-nav-label__text">' . $label . '</span>
1494 1494
             </span>';
1495
-    }
1496
-
1497
-
1498
-    /**
1499
-     * _set_current_labels
1500
-     * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1501
-     * property array
1502
-     *
1503
-     * @return void
1504
-     */
1505
-    private function _set_current_labels()
1506
-    {
1507
-        if (isset($this->_route_config['labels'])) {
1508
-            foreach ($this->_route_config['labels'] as $label => $text) {
1509
-                if (is_array($text)) {
1510
-                    foreach ($text as $sublabel => $subtext) {
1511
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1512
-                    }
1513
-                } else {
1514
-                    $this->_labels[ $label ] = $text;
1515
-                }
1516
-            }
1517
-        }
1518
-    }
1519
-
1520
-
1521
-    /**
1522
-     *        verifies user access for this admin page
1523
-     *
1524
-     * @param string $route_to_check if present then the capability for the route matching this string is checked.
1525
-     * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1526
-     *                               return false if verify fail.
1527
-     * @return bool
1528
-     * @throws InvalidArgumentException
1529
-     * @throws InvalidDataTypeException
1530
-     * @throws InvalidInterfaceException
1531
-     */
1532
-    public function check_user_access(string $route_to_check = '', bool $verify_only = false): bool
1533
-    {
1534
-        // if no route_to_check is passed in then use the current route set via _req_action
1535
-        $action = $route_to_check ?: $this->_req_action;
1536
-        $capability = ! empty($this->_page_routes[ $action ]['capability'])
1537
-            ? $this->_page_routes[ $action ]['capability']
1538
-            : null;
1539
-
1540
-        if (empty($capability)) {
1541
-            $capability = empty($route_to_check) && ! empty($this->_route['capability'])
1542
-                ? $this->_route['capability']
1543
-                : 'manage_options';
1544
-        }
1545
-
1546
-        $id = $this->_route['obj_id'] ?? 0;
1547
-
1548
-        if (
1549
-            ! $this->request->isAjax()
1550
-            && (
1551
-                ! function_exists('is_admin')
1552
-                || ! $this->capabilities->current_user_can($capability, "{$this->page_slug}_$route_to_check", $id)
1553
-            )
1554
-        ) {
1555
-            if ($verify_only) {
1556
-                return false;
1557
-            }
1558
-            if (is_user_logged_in()) {
1559
-                wp_die(esc_html__('You do not have access to this route.', 'event_espresso'));
1560
-            }
1561
-            return false;
1562
-        }
1563
-        return true;
1564
-    }
1565
-
1566
-
1567
-    /**
1568
-     * @param string                 $box_id
1569
-     * @param string                 $title
1570
-     * @param callable|string|null   $callback
1571
-     * @param string|array|WP_Screen $screen
1572
-     * @param string                 $context       Post edit screen contexts include 'normal', 'side', and 'advanced'.
1573
-     *                                              Comments screen contexts include 'normal' and 'side'.
1574
-     *                                              Menus meta boxes (accordion sections) all use the 'side' context.
1575
-     * @param string                 $priority      Accepts 'high', 'core', 'default', or 'low'.
1576
-     * @param array|null             $callback_args
1577
-     */
1578
-    protected function addMetaBox(
1579
-        string $box_id,
1580
-        string $title,
1581
-        $callback,
1582
-        $screen,
1583
-        string $context = 'normal',
1584
-        string $priority = 'default',
1585
-        ?array $callback_args = null
1586
-    ) {
1587
-        if (! (is_callable($callback) || ! function_exists($callback))) {
1588
-            return;
1589
-        }
1590
-
1591
-        add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1592
-        add_filter(
1593
-            "postbox_classes_{$this->_wp_page_slug}_$box_id",
1594
-            function ($classes) {
1595
-                $classes[] = 'ee-admin-container';
1596
-                return $classes;
1597
-            }
1598
-        );
1599
-    }
1600
-
1601
-
1602
-    /**
1603
-     * admin_init_global
1604
-     * This runs all the code that we want executed within the WP admin_init hook.
1605
-     * This method executes for ALL EE Admin pages.
1606
-     *
1607
-     * @return void
1608
-     */
1609
-    public function admin_init_global()
1610
-    {
1611
-    }
1612
-
1613
-
1614
-    /**
1615
-     * wp_loaded_global
1616
-     * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1617
-     * EE_Admin page and will execute on every EE Admin Page load
1618
-     *
1619
-     * @return void
1620
-     */
1621
-    public function wp_loaded()
1622
-    {
1623
-    }
1624
-
1625
-
1626
-    /**
1627
-     * admin_notices
1628
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1629
-     * ALL EE_Admin pages.
1630
-     *
1631
-     * @return void
1632
-     */
1633
-    public function admin_notices_global()
1634
-    {
1635
-        $this->_display_no_javascript_warning();
1636
-        $this->_display_espresso_notices();
1637
-    }
1638
-
1639
-
1640
-    public function network_admin_notices_global()
1641
-    {
1642
-        $this->_display_no_javascript_warning();
1643
-        $this->_display_espresso_notices();
1644
-    }
1645
-
1646
-
1647
-    /**
1648
-     * admin_footer_scripts_global
1649
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1650
-     * will apply on ALL EE_Admin pages.
1651
-     *
1652
-     * @return void
1653
-     */
1654
-    public function admin_footer_scripts_global()
1655
-    {
1656
-        $this->_add_admin_page_ajax_loading_img();
1657
-        $this->_add_admin_page_overlay();
1658
-        // if metaboxes are present we need to add the nonce field
1659
-        if (
1660
-            isset($this->_route_config['metaboxes'])
1661
-            || isset($this->_route_config['list_table'])
1662
-            || (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1663
-        ) {
1664
-            wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1665
-            wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1666
-        }
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * admin_footer_global
1672
-     * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1673
-     * This particular method will apply on ALL EE_Admin Pages.
1674
-     *
1675
-     * @return void
1676
-     */
1677
-    public function admin_footer_global()
1678
-    {
1679
-        // dialog container for dialog helper
1680
-        echo '
1495
+	}
1496
+
1497
+
1498
+	/**
1499
+	 * _set_current_labels
1500
+	 * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1501
+	 * property array
1502
+	 *
1503
+	 * @return void
1504
+	 */
1505
+	private function _set_current_labels()
1506
+	{
1507
+		if (isset($this->_route_config['labels'])) {
1508
+			foreach ($this->_route_config['labels'] as $label => $text) {
1509
+				if (is_array($text)) {
1510
+					foreach ($text as $sublabel => $subtext) {
1511
+						$this->_labels[ $label ][ $sublabel ] = $subtext;
1512
+					}
1513
+				} else {
1514
+					$this->_labels[ $label ] = $text;
1515
+				}
1516
+			}
1517
+		}
1518
+	}
1519
+
1520
+
1521
+	/**
1522
+	 *        verifies user access for this admin page
1523
+	 *
1524
+	 * @param string $route_to_check if present then the capability for the route matching this string is checked.
1525
+	 * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1526
+	 *                               return false if verify fail.
1527
+	 * @return bool
1528
+	 * @throws InvalidArgumentException
1529
+	 * @throws InvalidDataTypeException
1530
+	 * @throws InvalidInterfaceException
1531
+	 */
1532
+	public function check_user_access(string $route_to_check = '', bool $verify_only = false): bool
1533
+	{
1534
+		// if no route_to_check is passed in then use the current route set via _req_action
1535
+		$action = $route_to_check ?: $this->_req_action;
1536
+		$capability = ! empty($this->_page_routes[ $action ]['capability'])
1537
+			? $this->_page_routes[ $action ]['capability']
1538
+			: null;
1539
+
1540
+		if (empty($capability)) {
1541
+			$capability = empty($route_to_check) && ! empty($this->_route['capability'])
1542
+				? $this->_route['capability']
1543
+				: 'manage_options';
1544
+		}
1545
+
1546
+		$id = $this->_route['obj_id'] ?? 0;
1547
+
1548
+		if (
1549
+			! $this->request->isAjax()
1550
+			&& (
1551
+				! function_exists('is_admin')
1552
+				|| ! $this->capabilities->current_user_can($capability, "{$this->page_slug}_$route_to_check", $id)
1553
+			)
1554
+		) {
1555
+			if ($verify_only) {
1556
+				return false;
1557
+			}
1558
+			if (is_user_logged_in()) {
1559
+				wp_die(esc_html__('You do not have access to this route.', 'event_espresso'));
1560
+			}
1561
+			return false;
1562
+		}
1563
+		return true;
1564
+	}
1565
+
1566
+
1567
+	/**
1568
+	 * @param string                 $box_id
1569
+	 * @param string                 $title
1570
+	 * @param callable|string|null   $callback
1571
+	 * @param string|array|WP_Screen $screen
1572
+	 * @param string                 $context       Post edit screen contexts include 'normal', 'side', and 'advanced'.
1573
+	 *                                              Comments screen contexts include 'normal' and 'side'.
1574
+	 *                                              Menus meta boxes (accordion sections) all use the 'side' context.
1575
+	 * @param string                 $priority      Accepts 'high', 'core', 'default', or 'low'.
1576
+	 * @param array|null             $callback_args
1577
+	 */
1578
+	protected function addMetaBox(
1579
+		string $box_id,
1580
+		string $title,
1581
+		$callback,
1582
+		$screen,
1583
+		string $context = 'normal',
1584
+		string $priority = 'default',
1585
+		?array $callback_args = null
1586
+	) {
1587
+		if (! (is_callable($callback) || ! function_exists($callback))) {
1588
+			return;
1589
+		}
1590
+
1591
+		add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1592
+		add_filter(
1593
+			"postbox_classes_{$this->_wp_page_slug}_$box_id",
1594
+			function ($classes) {
1595
+				$classes[] = 'ee-admin-container';
1596
+				return $classes;
1597
+			}
1598
+		);
1599
+	}
1600
+
1601
+
1602
+	/**
1603
+	 * admin_init_global
1604
+	 * This runs all the code that we want executed within the WP admin_init hook.
1605
+	 * This method executes for ALL EE Admin pages.
1606
+	 *
1607
+	 * @return void
1608
+	 */
1609
+	public function admin_init_global()
1610
+	{
1611
+	}
1612
+
1613
+
1614
+	/**
1615
+	 * wp_loaded_global
1616
+	 * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1617
+	 * EE_Admin page and will execute on every EE Admin Page load
1618
+	 *
1619
+	 * @return void
1620
+	 */
1621
+	public function wp_loaded()
1622
+	{
1623
+	}
1624
+
1625
+
1626
+	/**
1627
+	 * admin_notices
1628
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1629
+	 * ALL EE_Admin pages.
1630
+	 *
1631
+	 * @return void
1632
+	 */
1633
+	public function admin_notices_global()
1634
+	{
1635
+		$this->_display_no_javascript_warning();
1636
+		$this->_display_espresso_notices();
1637
+	}
1638
+
1639
+
1640
+	public function network_admin_notices_global()
1641
+	{
1642
+		$this->_display_no_javascript_warning();
1643
+		$this->_display_espresso_notices();
1644
+	}
1645
+
1646
+
1647
+	/**
1648
+	 * admin_footer_scripts_global
1649
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1650
+	 * will apply on ALL EE_Admin pages.
1651
+	 *
1652
+	 * @return void
1653
+	 */
1654
+	public function admin_footer_scripts_global()
1655
+	{
1656
+		$this->_add_admin_page_ajax_loading_img();
1657
+		$this->_add_admin_page_overlay();
1658
+		// if metaboxes are present we need to add the nonce field
1659
+		if (
1660
+			isset($this->_route_config['metaboxes'])
1661
+			|| isset($this->_route_config['list_table'])
1662
+			|| (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1663
+		) {
1664
+			wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1665
+			wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1666
+		}
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * admin_footer_global
1672
+	 * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1673
+	 * This particular method will apply on ALL EE_Admin Pages.
1674
+	 *
1675
+	 * @return void
1676
+	 */
1677
+	public function admin_footer_global()
1678
+	{
1679
+		// dialog container for dialog helper
1680
+		echo '
1681 1681
         <div class="ee-admin-dialog-container auto-hide hidden">
1682 1682
             <div class="ee-notices"></div>
1683 1683
             <div class="ee-admin-dialog-container-inner-content"></div>
1684 1684
         </div>
1685 1685
         <span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>
1686 1686
         <input type="hidden" id="espresso_admin_current_page" value="' . esc_attr($this->_current_page) . '"/>';
1687
-    }
1688
-
1689
-
1690
-    /**
1691
-     * This function sees if there is a method for help popup content existing for the given route.  If there is then
1692
-     * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1693
-     * help popups then in your templates or your content you set "triggers" for the content using the
1694
-     * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1695
-     * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1696
-     * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1697
-     * for the
1698
-     * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1699
-     * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1700
-     *    'help_trigger_id' => array(
1701
-     *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1702
-     *        'content' => esc_html__('localized content for popup', 'event_espresso')
1703
-     *    )
1704
-     * );
1705
-     * Then the EE_Admin_Parent will take care of making sure that is set up properly on the correct route.
1706
-     *
1707
-     * @param array $help_array
1708
-     * @param bool  $display
1709
-     * @return string content
1710
-     * @throws DomainException
1711
-     * @throws EE_Error
1712
-     */
1713
-    protected function _set_help_popup_content(array $help_array = [], bool $display = false): string
1714
-    {
1715
-        $content    = '';
1716
-        $help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1717
-        // loop through the array and setup content
1718
-        foreach ($help_array as $trigger => $help) {
1719
-            // make sure the array is set up properly
1720
-            if (! isset($help['title'], $help['content'])) {
1721
-                throw new EE_Error(
1722
-                    esc_html__(
1723
-                        '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',
1724
-                        'event_espresso'
1725
-                    )
1726
-                );
1727
-            }
1728
-            // we're good so let's set up the template vars and then assign parsed template content to our content.
1729
-            $template_args = [
1730
-                'help_popup_id'      => $trigger,
1731
-                'help_popup_title'   => $help['title'],
1732
-                'help_popup_content' => $help['content'],
1733
-            ];
1734
-            $content       .= EEH_Template::display_template(
1735
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1736
-                $template_args,
1737
-                true
1738
-            );
1739
-        }
1740
-        if ($display) {
1741
-            echo wp_kses($content, AllowedTags::getWithFormTags());
1742
-            return '';
1743
-        }
1744
-        return $content;
1745
-    }
1746
-
1747
-
1748
-    /**
1749
-     * All this does is retrieve the help content array if set by the EE_Admin_Page child
1750
-     *
1751
-     * @return array properly formatted array for help popup content
1752
-     * @throws EE_Error
1753
-     */
1754
-    private function _get_help_content(): array
1755
-    {
1756
-        // what is the method we're looking for?
1757
-        $method_name = '_help_popup_content_' . $this->_req_action;
1758
-        // if method doesn't exist let's get out.
1759
-        if (! method_exists($this, $method_name)) {
1760
-            return [];
1761
-        }
1762
-        // k we're good to go let's retrieve the help array
1763
-        $help_array = $this->{$method_name}();
1764
-        // make sure we've got an array!
1765
-        if (! is_array($help_array)) {
1766
-            throw new EE_Error(
1767
-                esc_html__(
1768
-                    'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1769
-                    'event_espresso'
1770
-                )
1771
-            );
1772
-        }
1773
-        return $help_array;
1774
-    }
1775
-
1776
-
1777
-    /**
1778
-     * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1779
-     * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1780
-     * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1781
-     *
1782
-     * @param string $trigger_id reference for retrieving the trigger content for the popup
1783
-     * @param bool   $display    if false then we return the trigger string
1784
-     * @param array  $dimensions an array of dimensions for the box (array(h,w))
1785
-     * @return string
1786
-     * @throws DomainException
1787
-     * @throws EE_Error
1788
-     */
1789
-    protected function _set_help_trigger(string $trigger_id, bool $display = true, array $dimensions = ['400', '640'])
1790
-    {
1791
-        if ($this->request->isAjax()) {
1792
-            return '';
1793
-        }
1794
-        // 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
1795
-        $help_array   = $this->_get_help_content();
1796
-        $help_content = '';
1797
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1798
-            $help_array[ $trigger_id ] = [
1799
-                'title'   => esc_html__('Missing Content', 'event_espresso'),
1800
-                'content' => esc_html__(
1801
-                    '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.)',
1802
-                    'event_espresso'
1803
-                ),
1804
-            ];
1805
-            $help_content              = $this->_set_help_popup_content($help_array);
1806
-        }
1807
-        $height   = esc_attr($dimensions[0]) ?? 400;
1808
-        $width    = esc_attr($dimensions[1]) ?? 640;
1809
-        $inlineId = esc_attr($trigger_id);
1810
-        // let's setup the trigger
1811
-        $content = "
1687
+	}
1688
+
1689
+
1690
+	/**
1691
+	 * This function sees if there is a method for help popup content existing for the given route.  If there is then
1692
+	 * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1693
+	 * help popups then in your templates or your content you set "triggers" for the content using the
1694
+	 * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1695
+	 * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1696
+	 * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1697
+	 * for the
1698
+	 * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1699
+	 * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1700
+	 *    'help_trigger_id' => array(
1701
+	 *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1702
+	 *        'content' => esc_html__('localized content for popup', 'event_espresso')
1703
+	 *    )
1704
+	 * );
1705
+	 * Then the EE_Admin_Parent will take care of making sure that is set up properly on the correct route.
1706
+	 *
1707
+	 * @param array $help_array
1708
+	 * @param bool  $display
1709
+	 * @return string content
1710
+	 * @throws DomainException
1711
+	 * @throws EE_Error
1712
+	 */
1713
+	protected function _set_help_popup_content(array $help_array = [], bool $display = false): string
1714
+	{
1715
+		$content    = '';
1716
+		$help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1717
+		// loop through the array and setup content
1718
+		foreach ($help_array as $trigger => $help) {
1719
+			// make sure the array is set up properly
1720
+			if (! isset($help['title'], $help['content'])) {
1721
+				throw new EE_Error(
1722
+					esc_html__(
1723
+						'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',
1724
+						'event_espresso'
1725
+					)
1726
+				);
1727
+			}
1728
+			// we're good so let's set up the template vars and then assign parsed template content to our content.
1729
+			$template_args = [
1730
+				'help_popup_id'      => $trigger,
1731
+				'help_popup_title'   => $help['title'],
1732
+				'help_popup_content' => $help['content'],
1733
+			];
1734
+			$content       .= EEH_Template::display_template(
1735
+				EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1736
+				$template_args,
1737
+				true
1738
+			);
1739
+		}
1740
+		if ($display) {
1741
+			echo wp_kses($content, AllowedTags::getWithFormTags());
1742
+			return '';
1743
+		}
1744
+		return $content;
1745
+	}
1746
+
1747
+
1748
+	/**
1749
+	 * All this does is retrieve the help content array if set by the EE_Admin_Page child
1750
+	 *
1751
+	 * @return array properly formatted array for help popup content
1752
+	 * @throws EE_Error
1753
+	 */
1754
+	private function _get_help_content(): array
1755
+	{
1756
+		// what is the method we're looking for?
1757
+		$method_name = '_help_popup_content_' . $this->_req_action;
1758
+		// if method doesn't exist let's get out.
1759
+		if (! method_exists($this, $method_name)) {
1760
+			return [];
1761
+		}
1762
+		// k we're good to go let's retrieve the help array
1763
+		$help_array = $this->{$method_name}();
1764
+		// make sure we've got an array!
1765
+		if (! is_array($help_array)) {
1766
+			throw new EE_Error(
1767
+				esc_html__(
1768
+					'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1769
+					'event_espresso'
1770
+				)
1771
+			);
1772
+		}
1773
+		return $help_array;
1774
+	}
1775
+
1776
+
1777
+	/**
1778
+	 * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1779
+	 * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1780
+	 * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1781
+	 *
1782
+	 * @param string $trigger_id reference for retrieving the trigger content for the popup
1783
+	 * @param bool   $display    if false then we return the trigger string
1784
+	 * @param array  $dimensions an array of dimensions for the box (array(h,w))
1785
+	 * @return string
1786
+	 * @throws DomainException
1787
+	 * @throws EE_Error
1788
+	 */
1789
+	protected function _set_help_trigger(string $trigger_id, bool $display = true, array $dimensions = ['400', '640'])
1790
+	{
1791
+		if ($this->request->isAjax()) {
1792
+			return '';
1793
+		}
1794
+		// 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
1795
+		$help_array   = $this->_get_help_content();
1796
+		$help_content = '';
1797
+		if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1798
+			$help_array[ $trigger_id ] = [
1799
+				'title'   => esc_html__('Missing Content', 'event_espresso'),
1800
+				'content' => esc_html__(
1801
+					'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.)',
1802
+					'event_espresso'
1803
+				),
1804
+			];
1805
+			$help_content              = $this->_set_help_popup_content($help_array);
1806
+		}
1807
+		$height   = esc_attr($dimensions[0]) ?? 400;
1808
+		$width    = esc_attr($dimensions[1]) ?? 640;
1809
+		$inlineId = esc_attr($trigger_id);
1810
+		// let's setup the trigger
1811
+		$content = "
1812 1812
         <a class='ee-dialog' href='?height=$height&width=$width&inlineId=$inlineId' target='_blank'>
1813 1813
             <span class='question ee-help-popup-question'></span>
1814 1814
         </a>";
1815
-        $content .= $help_content;
1816
-        if ($display) {
1817
-            echo wp_kses($content, AllowedTags::getWithFormTags());
1818
-            return '';
1819
-        }
1820
-        return $content;
1821
-    }
1822
-
1823
-
1824
-    /**
1825
-     * _add_global_screen_options
1826
-     * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1827
-     * This particular method will add_screen_options on ALL EE_Admin Pages
1828
-     *
1829
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1830
-     *         see also WP_Screen object documents...
1831
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1832
-     * @abstract
1833
-     * @return void
1834
-     */
1835
-    private function _add_global_screen_options()
1836
-    {
1837
-    }
1838
-
1839
-
1840
-    /**
1841
-     * _add_global_feature_pointers
1842
-     * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1843
-     * This particular method will implement feature pointers for ALL EE_Admin pages.
1844
-     * Note: this is just a placeholder for now.  Implementation will come down the road
1845
-     *
1846
-     * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (it's a final class so can't be
1847
-     *         extended) also see:
1848
-     * @link   http://eamann.com/tech/wordpress-portland/
1849
-     * @abstract
1850
-     * @return void
1851
-     */
1852
-    private function _add_global_feature_pointers()
1853
-    {
1854
-    }
1855
-
1856
-
1857
-    /**
1858
-     * load_global_scripts_styles
1859
-     * The scripts and styles enqueued in here will be loaded on every EE Admin page
1860
-     *
1861
-     * @return void
1862
-     */
1863
-    public function load_global_scripts_styles()
1864
-    {
1865
-        // add debugging styles
1866
-        if (WP_DEBUG) {
1867
-            add_action('admin_head', [$this, 'add_xdebug_style']);
1868
-        }
1869
-        // taking care of metaboxes
1870
-        if (
1871
-            empty($this->_cpt_route)
1872
-            && (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1873
-        ) {
1874
-            wp_enqueue_script('dashboard');
1875
-        }
1876
-
1877
-        wp_enqueue_script(JqueryAssetManager::JS_HANDLE_JQUERY_UI_TOUCH_PUNCH);
1878
-        wp_enqueue_script(EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN);
1879
-        // LOCALIZED DATA
1880
-        // localize script for ajax lazy loading
1881
-        wp_localize_script(
1882
-            EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1883
-            'eeLazyLoadingContainers',
1884
-            apply_filters(
1885
-                'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1886
-                ['espresso_news_post_box_content']
1887
-            )
1888
-        );
1889
-        StatusChangeNotice::loadAssets();
1890
-
1891
-        add_filter(
1892
-            'admin_body_class',
1893
-            function ($classes) {
1894
-                if (strpos($classes, 'espresso-admin') === false) {
1895
-                    $classes .= ' espresso-admin';
1896
-                }
1897
-                return $classes;
1898
-            }
1899
-        );
1900
-    }
1901
-
1902
-
1903
-    /**
1904
-     *        admin_footer_scripts_eei18n_js_strings
1905
-     *
1906
-     * @return        void
1907
-     */
1908
-    public function admin_footer_scripts_eei18n_js_strings()
1909
-    {
1910
-        EE_Registry::$i18n_js_strings['confirm_delete'] = wp_strip_all_tags(
1911
-            __(
1912
-                '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!!!',
1913
-                'event_espresso'
1914
-            )
1915
-        );
1916
-        // now add months and days of the week
1917
-        EE_Registry::$i18n_js_strings['January']   = wp_strip_all_tags(__('January', 'event_espresso'));
1918
-        EE_Registry::$i18n_js_strings['February']  = wp_strip_all_tags(__('February', 'event_espresso'));
1919
-        EE_Registry::$i18n_js_strings['March']     = wp_strip_all_tags(__('March', 'event_espresso'));
1920
-        EE_Registry::$i18n_js_strings['April']     = wp_strip_all_tags(__('April', 'event_espresso'));
1921
-        EE_Registry::$i18n_js_strings['May']       = wp_strip_all_tags(__('May', 'event_espresso'));
1922
-        EE_Registry::$i18n_js_strings['June']      = wp_strip_all_tags(__('June', 'event_espresso'));
1923
-        EE_Registry::$i18n_js_strings['July']      = wp_strip_all_tags(__('July', 'event_espresso'));
1924
-        EE_Registry::$i18n_js_strings['August']    = wp_strip_all_tags(__('August', 'event_espresso'));
1925
-        EE_Registry::$i18n_js_strings['September'] = wp_strip_all_tags(__('September', 'event_espresso'));
1926
-        EE_Registry::$i18n_js_strings['October']   = wp_strip_all_tags(__('October', 'event_espresso'));
1927
-        EE_Registry::$i18n_js_strings['November']  = wp_strip_all_tags(__('November', 'event_espresso'));
1928
-        EE_Registry::$i18n_js_strings['December']  = wp_strip_all_tags(__('December', 'event_espresso'));
1929
-        EE_Registry::$i18n_js_strings['Jan']       = wp_strip_all_tags(__('Jan', 'event_espresso'));
1930
-        EE_Registry::$i18n_js_strings['Feb']       = wp_strip_all_tags(__('Feb', 'event_espresso'));
1931
-        EE_Registry::$i18n_js_strings['Mar']       = wp_strip_all_tags(__('Mar', 'event_espresso'));
1932
-        EE_Registry::$i18n_js_strings['Apr']       = wp_strip_all_tags(__('Apr', 'event_espresso'));
1933
-        EE_Registry::$i18n_js_strings['May']       = wp_strip_all_tags(__('May', 'event_espresso'));
1934
-        EE_Registry::$i18n_js_strings['Jun']       = wp_strip_all_tags(__('Jun', 'event_espresso'));
1935
-        EE_Registry::$i18n_js_strings['Jul']       = wp_strip_all_tags(__('Jul', 'event_espresso'));
1936
-        EE_Registry::$i18n_js_strings['Aug']       = wp_strip_all_tags(__('Aug', 'event_espresso'));
1937
-        EE_Registry::$i18n_js_strings['Sep']       = wp_strip_all_tags(__('Sep', 'event_espresso'));
1938
-        EE_Registry::$i18n_js_strings['Oct']       = wp_strip_all_tags(__('Oct', 'event_espresso'));
1939
-        EE_Registry::$i18n_js_strings['Nov']       = wp_strip_all_tags(__('Nov', 'event_espresso'));
1940
-        EE_Registry::$i18n_js_strings['Dec']       = wp_strip_all_tags(__('Dec', 'event_espresso'));
1941
-        EE_Registry::$i18n_js_strings['Sunday']    = wp_strip_all_tags(__('Sunday', 'event_espresso'));
1942
-        EE_Registry::$i18n_js_strings['Monday']    = wp_strip_all_tags(__('Monday', 'event_espresso'));
1943
-        EE_Registry::$i18n_js_strings['Tuesday']   = wp_strip_all_tags(__('Tuesday', 'event_espresso'));
1944
-        EE_Registry::$i18n_js_strings['Wednesday'] = wp_strip_all_tags(__('Wednesday', 'event_espresso'));
1945
-        EE_Registry::$i18n_js_strings['Thursday']  = wp_strip_all_tags(__('Thursday', 'event_espresso'));
1946
-        EE_Registry::$i18n_js_strings['Friday']    = wp_strip_all_tags(__('Friday', 'event_espresso'));
1947
-        EE_Registry::$i18n_js_strings['Saturday']  = wp_strip_all_tags(__('Saturday', 'event_espresso'));
1948
-        EE_Registry::$i18n_js_strings['Sun']       = wp_strip_all_tags(__('Sun', 'event_espresso'));
1949
-        EE_Registry::$i18n_js_strings['Mon']       = wp_strip_all_tags(__('Mon', 'event_espresso'));
1950
-        EE_Registry::$i18n_js_strings['Tue']       = wp_strip_all_tags(__('Tue', 'event_espresso'));
1951
-        EE_Registry::$i18n_js_strings['Wed']       = wp_strip_all_tags(__('Wed', 'event_espresso'));
1952
-        EE_Registry::$i18n_js_strings['Thu']       = wp_strip_all_tags(__('Thu', 'event_espresso'));
1953
-        EE_Registry::$i18n_js_strings['Fri']       = wp_strip_all_tags(__('Fri', 'event_espresso'));
1954
-        EE_Registry::$i18n_js_strings['Sat']       = wp_strip_all_tags(__('Sat', 'event_espresso'));
1955
-    }
1956
-
1957
-
1958
-    /**
1959
-     *        load enhanced xdebug styles for ppl with failing eyesight
1960
-     *
1961
-     * @return        void
1962
-     */
1963
-    public function add_xdebug_style()
1964
-    {
1965
-        echo '<style>.xdebug-error { font-size:1.5em; }</style>';
1966
-    }
1967
-
1968
-
1969
-    /************************/
1970
-    /** LIST TABLE METHODS **/
1971
-    /************************/
1972
-    /**
1973
-     * this sets up the list table if the current view requires it.
1974
-     *
1975
-     * @return void
1976
-     * @throws EE_Error
1977
-     * @throws InvalidArgumentException
1978
-     * @throws InvalidDataTypeException
1979
-     * @throws InvalidInterfaceException
1980
-     */
1981
-    protected function _set_list_table()
1982
-    {
1983
-        // first is this a list_table view?
1984
-        if (! isset($this->_route_config['list_table'])) {
1985
-            return;
1986
-        } //not a list_table view so get out.
1987
-        // list table functions are per view specific (because some admin pages might have more than one list table!)
1988
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
1989
-        if (! method_exists($this, $list_table_view)) {
1990
-            // user error msg
1991
-            $error_msg = esc_html__(
1992
-                'An error occurred. The requested list table views could not be found.',
1993
-                'event_espresso'
1994
-            );
1995
-            // developer error msg
1996
-            $error_msg .= '||'
1997
-                          . sprintf(
1998
-                              esc_html__(
1999
-                                  '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.',
2000
-                                  'event_espresso'
2001
-                              ),
2002
-                              $this->_req_action,
2003
-                              $list_table_view
2004
-                          );
2005
-            throw new EE_Error($error_msg);
2006
-        }
2007
-        $this->{$list_table_view}();
2008
-        // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2009
-        $this->_views = apply_filters(
2010
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2011
-            $this->_views
2012
-        );
2013
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2014
-        $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2015
-        $this->_set_list_table_view();
2016
-        $this->_set_list_table_object();
2017
-    }
2018
-
2019
-
2020
-    /**
2021
-     * set current view for List Table
2022
-     *
2023
-     * @return void
2024
-     */
2025
-    protected function _set_list_table_view()
2026
-    {
2027
-        $this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2028
-        $status      = $this->request->getRequestParam('status', null, DataType::KEY);
2029
-        $this->_view = $status && array_key_exists($status, $this->_views)
2030
-            ? $status
2031
-            : $this->_view;
2032
-    }
2033
-
2034
-
2035
-    /**
2036
-     * _set_list_table_object
2037
-     * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2038
-     *
2039
-     * @throws InvalidInterfaceException
2040
-     * @throws InvalidArgumentException
2041
-     * @throws InvalidDataTypeException
2042
-     * @throws EE_Error
2043
-     * @throws InvalidInterfaceException
2044
-     */
2045
-    protected function _set_list_table_object()
2046
-    {
2047
-        if (isset($this->_route_config['list_table'])) {
2048
-            if (! class_exists($this->_route_config['list_table'])) {
2049
-                throw new EE_Error(
2050
-                    sprintf(
2051
-                        esc_html__(
2052
-                            '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.',
2053
-                            'event_espresso'
2054
-                        ),
2055
-                        $this->_route_config['list_table'],
2056
-                        $this->class_name
2057
-                    )
2058
-                );
2059
-            }
2060
-            $this->_list_table_object = $this->loader->getShared(
2061
-                $this->_route_config['list_table'],
2062
-                [
2063
-                    $this,
2064
-                    LoaderFactory::getShared(AdminListTableFilters::class),
2065
-                ]
2066
-            );
2067
-        }
2068
-    }
2069
-
2070
-
2071
-    /**
2072
-     * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2073
-     *
2074
-     * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2075
-     *                                                    urls.  The array should be indexed by the view it is being
2076
-     *                                                    added to.
2077
-     * @return array
2078
-     */
2079
-    public function get_list_table_view_RLs(array $extra_query_args = []): array
2080
-    {
2081
-        $extra_query_args = apply_filters(
2082
-            'FHEE__EE_Admin_Page__get_list_table_view_RLs__extra_query_args',
2083
-            $extra_query_args,
2084
-            $this
2085
-        );
2086
-        $action_nonce = "{$this->_req_action}_nonce";
2087
-        $nonce = wp_create_nonce($action_nonce);
2088
-        // cycle thru views
2089
-        foreach ($this->_views as $key => $view) {
2090
-            $query_args = [];
2091
-            if ( ! isset($this->_views[ $key ]['class'])) {
2092
-                $this->_views[ $key ]['class'] = '';
2093
-            }
2094
-            // check for current view
2095
-            $this->_views[ $key ]['class'] .= $this->_view === $view['slug'] ? ' current' : '';
2096
-            $query_args['action']          = $this->_req_action;
2097
-            $query_args[ $action_nonce ]   = $nonce;
2098
-            $query_args['status']          = $view['slug'];
2099
-            // merge any other arguments sent in.
2100
-            if (isset($extra_query_args[ $view['slug'] ])) {
2101
-                $query_args = array_merge($query_args, $extra_query_args[ $view['slug'] ]);
2102
-            }
2103
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2104
-        }
2105
-        return $this->_views;
2106
-    }
2107
-
2108
-
2109
-    /**
2110
-     * generates a dropdown box for selecting the number of visible rows in an admin page list table
2111
-     *
2112
-     * @param int $max_entries total number of rows in the table
2113
-     * @return string
2114
-     * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2115
-     *                         WP does it.
2116
-     */
2117
-    protected function _entries_per_page_dropdown(int $max_entries = 0): string
2118
-    {
2119
-        $values   = [10, 25, 50, 100];
2120
-        $per_page = $this->request->getRequestParam('per_page', 10, DataType::INT);
2121
-        if ($max_entries) {
2122
-            $values[] = $max_entries;
2123
-            sort($values);
2124
-        }
2125
-        $entries_per_page_dropdown = '
1815
+		$content .= $help_content;
1816
+		if ($display) {
1817
+			echo wp_kses($content, AllowedTags::getWithFormTags());
1818
+			return '';
1819
+		}
1820
+		return $content;
1821
+	}
1822
+
1823
+
1824
+	/**
1825
+	 * _add_global_screen_options
1826
+	 * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1827
+	 * This particular method will add_screen_options on ALL EE_Admin Pages
1828
+	 *
1829
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1830
+	 *         see also WP_Screen object documents...
1831
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1832
+	 * @abstract
1833
+	 * @return void
1834
+	 */
1835
+	private function _add_global_screen_options()
1836
+	{
1837
+	}
1838
+
1839
+
1840
+	/**
1841
+	 * _add_global_feature_pointers
1842
+	 * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1843
+	 * This particular method will implement feature pointers for ALL EE_Admin pages.
1844
+	 * Note: this is just a placeholder for now.  Implementation will come down the road
1845
+	 *
1846
+	 * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (it's a final class so can't be
1847
+	 *         extended) also see:
1848
+	 * @link   http://eamann.com/tech/wordpress-portland/
1849
+	 * @abstract
1850
+	 * @return void
1851
+	 */
1852
+	private function _add_global_feature_pointers()
1853
+	{
1854
+	}
1855
+
1856
+
1857
+	/**
1858
+	 * load_global_scripts_styles
1859
+	 * The scripts and styles enqueued in here will be loaded on every EE Admin page
1860
+	 *
1861
+	 * @return void
1862
+	 */
1863
+	public function load_global_scripts_styles()
1864
+	{
1865
+		// add debugging styles
1866
+		if (WP_DEBUG) {
1867
+			add_action('admin_head', [$this, 'add_xdebug_style']);
1868
+		}
1869
+		// taking care of metaboxes
1870
+		if (
1871
+			empty($this->_cpt_route)
1872
+			&& (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1873
+		) {
1874
+			wp_enqueue_script('dashboard');
1875
+		}
1876
+
1877
+		wp_enqueue_script(JqueryAssetManager::JS_HANDLE_JQUERY_UI_TOUCH_PUNCH);
1878
+		wp_enqueue_script(EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN);
1879
+		// LOCALIZED DATA
1880
+		// localize script for ajax lazy loading
1881
+		wp_localize_script(
1882
+			EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1883
+			'eeLazyLoadingContainers',
1884
+			apply_filters(
1885
+				'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1886
+				['espresso_news_post_box_content']
1887
+			)
1888
+		);
1889
+		StatusChangeNotice::loadAssets();
1890
+
1891
+		add_filter(
1892
+			'admin_body_class',
1893
+			function ($classes) {
1894
+				if (strpos($classes, 'espresso-admin') === false) {
1895
+					$classes .= ' espresso-admin';
1896
+				}
1897
+				return $classes;
1898
+			}
1899
+		);
1900
+	}
1901
+
1902
+
1903
+	/**
1904
+	 *        admin_footer_scripts_eei18n_js_strings
1905
+	 *
1906
+	 * @return        void
1907
+	 */
1908
+	public function admin_footer_scripts_eei18n_js_strings()
1909
+	{
1910
+		EE_Registry::$i18n_js_strings['confirm_delete'] = wp_strip_all_tags(
1911
+			__(
1912
+				'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!!!',
1913
+				'event_espresso'
1914
+			)
1915
+		);
1916
+		// now add months and days of the week
1917
+		EE_Registry::$i18n_js_strings['January']   = wp_strip_all_tags(__('January', 'event_espresso'));
1918
+		EE_Registry::$i18n_js_strings['February']  = wp_strip_all_tags(__('February', 'event_espresso'));
1919
+		EE_Registry::$i18n_js_strings['March']     = wp_strip_all_tags(__('March', 'event_espresso'));
1920
+		EE_Registry::$i18n_js_strings['April']     = wp_strip_all_tags(__('April', 'event_espresso'));
1921
+		EE_Registry::$i18n_js_strings['May']       = wp_strip_all_tags(__('May', 'event_espresso'));
1922
+		EE_Registry::$i18n_js_strings['June']      = wp_strip_all_tags(__('June', 'event_espresso'));
1923
+		EE_Registry::$i18n_js_strings['July']      = wp_strip_all_tags(__('July', 'event_espresso'));
1924
+		EE_Registry::$i18n_js_strings['August']    = wp_strip_all_tags(__('August', 'event_espresso'));
1925
+		EE_Registry::$i18n_js_strings['September'] = wp_strip_all_tags(__('September', 'event_espresso'));
1926
+		EE_Registry::$i18n_js_strings['October']   = wp_strip_all_tags(__('October', 'event_espresso'));
1927
+		EE_Registry::$i18n_js_strings['November']  = wp_strip_all_tags(__('November', 'event_espresso'));
1928
+		EE_Registry::$i18n_js_strings['December']  = wp_strip_all_tags(__('December', 'event_espresso'));
1929
+		EE_Registry::$i18n_js_strings['Jan']       = wp_strip_all_tags(__('Jan', 'event_espresso'));
1930
+		EE_Registry::$i18n_js_strings['Feb']       = wp_strip_all_tags(__('Feb', 'event_espresso'));
1931
+		EE_Registry::$i18n_js_strings['Mar']       = wp_strip_all_tags(__('Mar', 'event_espresso'));
1932
+		EE_Registry::$i18n_js_strings['Apr']       = wp_strip_all_tags(__('Apr', 'event_espresso'));
1933
+		EE_Registry::$i18n_js_strings['May']       = wp_strip_all_tags(__('May', 'event_espresso'));
1934
+		EE_Registry::$i18n_js_strings['Jun']       = wp_strip_all_tags(__('Jun', 'event_espresso'));
1935
+		EE_Registry::$i18n_js_strings['Jul']       = wp_strip_all_tags(__('Jul', 'event_espresso'));
1936
+		EE_Registry::$i18n_js_strings['Aug']       = wp_strip_all_tags(__('Aug', 'event_espresso'));
1937
+		EE_Registry::$i18n_js_strings['Sep']       = wp_strip_all_tags(__('Sep', 'event_espresso'));
1938
+		EE_Registry::$i18n_js_strings['Oct']       = wp_strip_all_tags(__('Oct', 'event_espresso'));
1939
+		EE_Registry::$i18n_js_strings['Nov']       = wp_strip_all_tags(__('Nov', 'event_espresso'));
1940
+		EE_Registry::$i18n_js_strings['Dec']       = wp_strip_all_tags(__('Dec', 'event_espresso'));
1941
+		EE_Registry::$i18n_js_strings['Sunday']    = wp_strip_all_tags(__('Sunday', 'event_espresso'));
1942
+		EE_Registry::$i18n_js_strings['Monday']    = wp_strip_all_tags(__('Monday', 'event_espresso'));
1943
+		EE_Registry::$i18n_js_strings['Tuesday']   = wp_strip_all_tags(__('Tuesday', 'event_espresso'));
1944
+		EE_Registry::$i18n_js_strings['Wednesday'] = wp_strip_all_tags(__('Wednesday', 'event_espresso'));
1945
+		EE_Registry::$i18n_js_strings['Thursday']  = wp_strip_all_tags(__('Thursday', 'event_espresso'));
1946
+		EE_Registry::$i18n_js_strings['Friday']    = wp_strip_all_tags(__('Friday', 'event_espresso'));
1947
+		EE_Registry::$i18n_js_strings['Saturday']  = wp_strip_all_tags(__('Saturday', 'event_espresso'));
1948
+		EE_Registry::$i18n_js_strings['Sun']       = wp_strip_all_tags(__('Sun', 'event_espresso'));
1949
+		EE_Registry::$i18n_js_strings['Mon']       = wp_strip_all_tags(__('Mon', 'event_espresso'));
1950
+		EE_Registry::$i18n_js_strings['Tue']       = wp_strip_all_tags(__('Tue', 'event_espresso'));
1951
+		EE_Registry::$i18n_js_strings['Wed']       = wp_strip_all_tags(__('Wed', 'event_espresso'));
1952
+		EE_Registry::$i18n_js_strings['Thu']       = wp_strip_all_tags(__('Thu', 'event_espresso'));
1953
+		EE_Registry::$i18n_js_strings['Fri']       = wp_strip_all_tags(__('Fri', 'event_espresso'));
1954
+		EE_Registry::$i18n_js_strings['Sat']       = wp_strip_all_tags(__('Sat', 'event_espresso'));
1955
+	}
1956
+
1957
+
1958
+	/**
1959
+	 *        load enhanced xdebug styles for ppl with failing eyesight
1960
+	 *
1961
+	 * @return        void
1962
+	 */
1963
+	public function add_xdebug_style()
1964
+	{
1965
+		echo '<style>.xdebug-error { font-size:1.5em; }</style>';
1966
+	}
1967
+
1968
+
1969
+	/************************/
1970
+	/** LIST TABLE METHODS **/
1971
+	/************************/
1972
+	/**
1973
+	 * this sets up the list table if the current view requires it.
1974
+	 *
1975
+	 * @return void
1976
+	 * @throws EE_Error
1977
+	 * @throws InvalidArgumentException
1978
+	 * @throws InvalidDataTypeException
1979
+	 * @throws InvalidInterfaceException
1980
+	 */
1981
+	protected function _set_list_table()
1982
+	{
1983
+		// first is this a list_table view?
1984
+		if (! isset($this->_route_config['list_table'])) {
1985
+			return;
1986
+		} //not a list_table view so get out.
1987
+		// list table functions are per view specific (because some admin pages might have more than one list table!)
1988
+		$list_table_view = '_set_list_table_views_' . $this->_req_action;
1989
+		if (! method_exists($this, $list_table_view)) {
1990
+			// user error msg
1991
+			$error_msg = esc_html__(
1992
+				'An error occurred. The requested list table views could not be found.',
1993
+				'event_espresso'
1994
+			);
1995
+			// developer error msg
1996
+			$error_msg .= '||'
1997
+						  . sprintf(
1998
+							  esc_html__(
1999
+								  '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.',
2000
+								  'event_espresso'
2001
+							  ),
2002
+							  $this->_req_action,
2003
+							  $list_table_view
2004
+						  );
2005
+			throw new EE_Error($error_msg);
2006
+		}
2007
+		$this->{$list_table_view}();
2008
+		// let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2009
+		$this->_views = apply_filters(
2010
+			'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2011
+			$this->_views
2012
+		);
2013
+		$this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2014
+		$this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2015
+		$this->_set_list_table_view();
2016
+		$this->_set_list_table_object();
2017
+	}
2018
+
2019
+
2020
+	/**
2021
+	 * set current view for List Table
2022
+	 *
2023
+	 * @return void
2024
+	 */
2025
+	protected function _set_list_table_view()
2026
+	{
2027
+		$this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2028
+		$status      = $this->request->getRequestParam('status', null, DataType::KEY);
2029
+		$this->_view = $status && array_key_exists($status, $this->_views)
2030
+			? $status
2031
+			: $this->_view;
2032
+	}
2033
+
2034
+
2035
+	/**
2036
+	 * _set_list_table_object
2037
+	 * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2038
+	 *
2039
+	 * @throws InvalidInterfaceException
2040
+	 * @throws InvalidArgumentException
2041
+	 * @throws InvalidDataTypeException
2042
+	 * @throws EE_Error
2043
+	 * @throws InvalidInterfaceException
2044
+	 */
2045
+	protected function _set_list_table_object()
2046
+	{
2047
+		if (isset($this->_route_config['list_table'])) {
2048
+			if (! class_exists($this->_route_config['list_table'])) {
2049
+				throw new EE_Error(
2050
+					sprintf(
2051
+						esc_html__(
2052
+							'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.',
2053
+							'event_espresso'
2054
+						),
2055
+						$this->_route_config['list_table'],
2056
+						$this->class_name
2057
+					)
2058
+				);
2059
+			}
2060
+			$this->_list_table_object = $this->loader->getShared(
2061
+				$this->_route_config['list_table'],
2062
+				[
2063
+					$this,
2064
+					LoaderFactory::getShared(AdminListTableFilters::class),
2065
+				]
2066
+			);
2067
+		}
2068
+	}
2069
+
2070
+
2071
+	/**
2072
+	 * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2073
+	 *
2074
+	 * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2075
+	 *                                                    urls.  The array should be indexed by the view it is being
2076
+	 *                                                    added to.
2077
+	 * @return array
2078
+	 */
2079
+	public function get_list_table_view_RLs(array $extra_query_args = []): array
2080
+	{
2081
+		$extra_query_args = apply_filters(
2082
+			'FHEE__EE_Admin_Page__get_list_table_view_RLs__extra_query_args',
2083
+			$extra_query_args,
2084
+			$this
2085
+		);
2086
+		$action_nonce = "{$this->_req_action}_nonce";
2087
+		$nonce = wp_create_nonce($action_nonce);
2088
+		// cycle thru views
2089
+		foreach ($this->_views as $key => $view) {
2090
+			$query_args = [];
2091
+			if ( ! isset($this->_views[ $key ]['class'])) {
2092
+				$this->_views[ $key ]['class'] = '';
2093
+			}
2094
+			// check for current view
2095
+			$this->_views[ $key ]['class'] .= $this->_view === $view['slug'] ? ' current' : '';
2096
+			$query_args['action']          = $this->_req_action;
2097
+			$query_args[ $action_nonce ]   = $nonce;
2098
+			$query_args['status']          = $view['slug'];
2099
+			// merge any other arguments sent in.
2100
+			if (isset($extra_query_args[ $view['slug'] ])) {
2101
+				$query_args = array_merge($query_args, $extra_query_args[ $view['slug'] ]);
2102
+			}
2103
+			$this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2104
+		}
2105
+		return $this->_views;
2106
+	}
2107
+
2108
+
2109
+	/**
2110
+	 * generates a dropdown box for selecting the number of visible rows in an admin page list table
2111
+	 *
2112
+	 * @param int $max_entries total number of rows in the table
2113
+	 * @return string
2114
+	 * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2115
+	 *                         WP does it.
2116
+	 */
2117
+	protected function _entries_per_page_dropdown(int $max_entries = 0): string
2118
+	{
2119
+		$values   = [10, 25, 50, 100];
2120
+		$per_page = $this->request->getRequestParam('per_page', 10, DataType::INT);
2121
+		if ($max_entries) {
2122
+			$values[] = $max_entries;
2123
+			sort($values);
2124
+		}
2125
+		$entries_per_page_dropdown = '
2126 2126
 			<div id="entries-per-page-dv" class="alignleft actions">
2127 2127
 				<label class="hide-if-no-js">
2128 2128
 					Show
2129 2129
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2130
-        foreach ($values as $value) {
2131
-            if ($value < $max_entries) {
2132
-                $selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2133
-                $entries_per_page_dropdown .= '
2130
+		foreach ($values as $value) {
2131
+			if ($value < $max_entries) {
2132
+				$selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2133
+				$entries_per_page_dropdown .= '
2134 2134
 						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2135
-            }
2136
-        }
2137
-        $selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2138
-        $entries_per_page_dropdown .= '
2135
+			}
2136
+		}
2137
+		$selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2138
+		$entries_per_page_dropdown .= '
2139 2139
 						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2140
-        $entries_per_page_dropdown .= '
2140
+		$entries_per_page_dropdown .= '
2141 2141
 					</select>
2142 2142
 					entries
2143 2143
 				</label>
2144 2144
 				<input id="entries-per-page-btn" class="button button--secondary" type="submit" value="Go" >
2145 2145
 			</div>
2146 2146
 		';
2147
-        return $entries_per_page_dropdown;
2148
-    }
2149
-
2150
-
2151
-    /**
2152
-     *        _set_search_attributes
2153
-     *
2154
-     * @return        void
2155
-     */
2156
-    public function _set_search_attributes()
2157
-    {
2158
-        $this->_template_args['search']['btn_label'] = sprintf(
2159
-            esc_html__('Search %s', 'event_espresso'),
2160
-            empty($this->_search_btn_label) ? $this->page_label
2161
-                : $this->_search_btn_label
2162
-        );
2163
-        $this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2164
-    }
2165
-
2166
-
2167
-
2168
-    /*** END LIST TABLE METHODS **/
2169
-
2170
-    /**
2171
-     * @return void
2172
-     * @throws EE_Error
2173
-     */
2174
-    public function addRegisteredMetaBoxes()
2175
-    {
2176
-        remove_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
2177
-        $this->_add_registered_meta_boxes();
2178
-    }
2179
-
2180
-
2181
-    /**
2182
-     * _add_registered_metaboxes
2183
-     *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2184
-     *
2185
-     * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2186
-     * @return void
2187
-     * @throws EE_Error
2188
-     */
2189
-    private function _add_registered_meta_boxes()
2190
-    {
2191
-        // we only add meta boxes if the page_route calls for it
2192
-        if (isset($this->_route_config['metaboxes']) && is_array($this->_route_config['metaboxes'])) {
2193
-            // this simply loops through the callbacks provided
2194
-            // and checks if there is a corresponding callback registered by the child
2195
-            // if there is then we go ahead and process the metabox loader.
2196
-            foreach ($this->_route_config['metaboxes'] as $key => $metabox_callback) {
2197
-                // first check for Closures
2198
-                if ($metabox_callback instanceof Closure) {
2199
-                    $result = $metabox_callback();
2200
-                } elseif (is_callable($metabox_callback)) {
2201
-                    $result = call_user_func($metabox_callback);
2202
-                } elseif (method_exists($this, $metabox_callback)) {
2203
-                    $result = $this->{$metabox_callback}();
2204
-                } else {
2205
-                    $result = false;
2206
-                }
2207
-                if ($result === false) {
2208
-                    // user error msg
2209
-                    $error_msg = esc_html__(
2210
-                        'An error occurred. The  requested metabox could not be found.',
2211
-                        'event_espresso'
2212
-                    );
2213
-                    // developer error msg
2214
-                    $error_msg .= '||'
2215
-                                  . sprintf(
2216
-                                      esc_html__(
2217
-                                          '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.',
2218
-                                          'event_espresso'
2219
-                                      ),
2220
-                                      $metabox_callback
2221
-                                  );
2222
-                    throw new EE_Error($error_msg);
2223
-                }
2224
-                unset($this->_route_config['metaboxes'][ $key ]);
2225
-            }
2226
-        }
2227
-    }
2228
-
2229
-
2230
-    /**
2231
-     * _add_screen_columns
2232
-     * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2233
-     * the dynamic column template and we'll set up the column options for the page.
2234
-     *
2235
-     * @return void
2236
-     */
2237
-    private function _add_screen_columns()
2238
-    {
2239
-        if (
2240
-            isset($this->_route_config['columns'])
2241
-            && is_array($this->_route_config['columns'])
2242
-            && count($this->_route_config['columns']) === 2
2243
-        ) {
2244
-            add_screen_option(
2245
-                'layout_columns',
2246
-                [
2247
-                    'max'     => (int) $this->_route_config['columns'][0],
2248
-                    'default' => (int) $this->_route_config['columns'][1],
2249
-                ]
2250
-            );
2251
-            $this->_template_args['num_columns']                 = $this->_route_config['columns'][0];
2252
-            $screen_id                                           = $this->_current_screen->id;
2253
-            $screen_columns                                      = (int) get_user_option("screen_layout_$screen_id");
2254
-            $total_columns                                       = ! empty($screen_columns)
2255
-                ? $screen_columns
2256
-                : $this->_route_config['columns'][1];
2257
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2258
-            $this->_template_args['current_page']                = $this->_wp_page_slug;
2259
-            $this->_template_args['screen']                      = $this->_current_screen;
2260
-            $this->_column_template_path                         = EE_ADMIN_TEMPLATE
2261
-                                                                   . 'admin_details_metabox_column_wrapper.template.php';
2262
-            // finally if we don't have has_metaboxes set in the route config
2263
-            // let's make sure it IS set otherwise the necessary hidden fields for this won't be loaded.
2264
-            $this->_route_config['has_metaboxes'] = true;
2265
-        }
2266
-    }
2267
-
2268
-
2269
-
2270
-    /** GLOBALLY AVAILABLE METABOXES **/
2271
-
2272
-
2273
-    /**
2274
-     * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2275
-     * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2276
-     * these get loaded on.
2277
-     */
2278
-    private function _espresso_news_post_box()
2279
-    {
2280
-        $news_box_title = apply_filters(
2281
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2282
-            esc_html__('New @ Event Espresso', 'event_espresso')
2283
-        );
2284
-        $this->addMetaBox(
2285
-            'espresso_news_post_box',
2286
-            $news_box_title,
2287
-            [
2288
-                $this,
2289
-                'espresso_news_post_box',
2290
-            ],
2291
-            $this->_wp_page_slug,
2292
-            'side',
2293
-            'low'
2294
-        );
2295
-    }
2296
-
2297
-
2298
-    /**
2299
-     * Code for setting up espresso ratings request metabox.
2300
-     */
2301
-    protected function _espresso_ratings_request()
2302
-    {
2303
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2304
-            return;
2305
-        }
2306
-        $ratings_box_title = apply_filters(
2307
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2308
-            esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2309
-        );
2310
-        $this->addMetaBox(
2311
-            'espresso_ratings_request',
2312
-            $ratings_box_title,
2313
-            [
2314
-                $this,
2315
-                'espresso_ratings_request',
2316
-            ],
2317
-            $this->_wp_page_slug,
2318
-            'side'
2319
-        );
2320
-    }
2321
-
2322
-
2323
-    /**
2324
-     * Code for setting up espresso ratings request metabox content.
2325
-     *
2326
-     * @throws DomainException
2327
-     */
2328
-    public function espresso_ratings_request()
2329
-    {
2330
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2331
-    }
2332
-
2333
-
2334
-    public static function cached_rss_display(string $rss_id, string $url): bool
2335
-    {
2336
-        $loading   = '<p class="widget-loading hide-if-no-js">'
2337
-                     . esc_html__('Loading&#8230;', 'event_espresso')
2338
-                     . '</p><p class="hide-if-js">'
2339
-                     . esc_html__('This widget requires JavaScript.', 'event_espresso')
2340
-                     . '</p>';
2341
-        $pre       = '<div class="espresso-rss-display">' . "\n\t";
2342
-        $pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2343
-        $post      = '</div>' . "\n";
2344
-        $cache_key = 'ee_rss_' . md5($rss_id);
2345
-        $output    = get_transient($cache_key);
2346
-        if ($output !== false) {
2347
-            echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2348
-            return true;
2349
-        }
2350
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2351
-            echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2352
-            return false;
2353
-        }
2354
-        ob_start();
2355
-        wp_widget_rss_output($url, ['show_date' => 0, 'items' => 5]);
2356
-        set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2357
-        return true;
2358
-    }
2359
-
2360
-
2361
-    public function espresso_news_post_box()
2362
-    {
2363
-        ?>
2147
+		return $entries_per_page_dropdown;
2148
+	}
2149
+
2150
+
2151
+	/**
2152
+	 *        _set_search_attributes
2153
+	 *
2154
+	 * @return        void
2155
+	 */
2156
+	public function _set_search_attributes()
2157
+	{
2158
+		$this->_template_args['search']['btn_label'] = sprintf(
2159
+			esc_html__('Search %s', 'event_espresso'),
2160
+			empty($this->_search_btn_label) ? $this->page_label
2161
+				: $this->_search_btn_label
2162
+		);
2163
+		$this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2164
+	}
2165
+
2166
+
2167
+
2168
+	/*** END LIST TABLE METHODS **/
2169
+
2170
+	/**
2171
+	 * @return void
2172
+	 * @throws EE_Error
2173
+	 */
2174
+	public function addRegisteredMetaBoxes()
2175
+	{
2176
+		remove_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
2177
+		$this->_add_registered_meta_boxes();
2178
+	}
2179
+
2180
+
2181
+	/**
2182
+	 * _add_registered_metaboxes
2183
+	 *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2184
+	 *
2185
+	 * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2186
+	 * @return void
2187
+	 * @throws EE_Error
2188
+	 */
2189
+	private function _add_registered_meta_boxes()
2190
+	{
2191
+		// we only add meta boxes if the page_route calls for it
2192
+		if (isset($this->_route_config['metaboxes']) && is_array($this->_route_config['metaboxes'])) {
2193
+			// this simply loops through the callbacks provided
2194
+			// and checks if there is a corresponding callback registered by the child
2195
+			// if there is then we go ahead and process the metabox loader.
2196
+			foreach ($this->_route_config['metaboxes'] as $key => $metabox_callback) {
2197
+				// first check for Closures
2198
+				if ($metabox_callback instanceof Closure) {
2199
+					$result = $metabox_callback();
2200
+				} elseif (is_callable($metabox_callback)) {
2201
+					$result = call_user_func($metabox_callback);
2202
+				} elseif (method_exists($this, $metabox_callback)) {
2203
+					$result = $this->{$metabox_callback}();
2204
+				} else {
2205
+					$result = false;
2206
+				}
2207
+				if ($result === false) {
2208
+					// user error msg
2209
+					$error_msg = esc_html__(
2210
+						'An error occurred. The  requested metabox could not be found.',
2211
+						'event_espresso'
2212
+					);
2213
+					// developer error msg
2214
+					$error_msg .= '||'
2215
+								  . sprintf(
2216
+									  esc_html__(
2217
+										  '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.',
2218
+										  'event_espresso'
2219
+									  ),
2220
+									  $metabox_callback
2221
+								  );
2222
+					throw new EE_Error($error_msg);
2223
+				}
2224
+				unset($this->_route_config['metaboxes'][ $key ]);
2225
+			}
2226
+		}
2227
+	}
2228
+
2229
+
2230
+	/**
2231
+	 * _add_screen_columns
2232
+	 * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2233
+	 * the dynamic column template and we'll set up the column options for the page.
2234
+	 *
2235
+	 * @return void
2236
+	 */
2237
+	private function _add_screen_columns()
2238
+	{
2239
+		if (
2240
+			isset($this->_route_config['columns'])
2241
+			&& is_array($this->_route_config['columns'])
2242
+			&& count($this->_route_config['columns']) === 2
2243
+		) {
2244
+			add_screen_option(
2245
+				'layout_columns',
2246
+				[
2247
+					'max'     => (int) $this->_route_config['columns'][0],
2248
+					'default' => (int) $this->_route_config['columns'][1],
2249
+				]
2250
+			);
2251
+			$this->_template_args['num_columns']                 = $this->_route_config['columns'][0];
2252
+			$screen_id                                           = $this->_current_screen->id;
2253
+			$screen_columns                                      = (int) get_user_option("screen_layout_$screen_id");
2254
+			$total_columns                                       = ! empty($screen_columns)
2255
+				? $screen_columns
2256
+				: $this->_route_config['columns'][1];
2257
+			$this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2258
+			$this->_template_args['current_page']                = $this->_wp_page_slug;
2259
+			$this->_template_args['screen']                      = $this->_current_screen;
2260
+			$this->_column_template_path                         = EE_ADMIN_TEMPLATE
2261
+																   . 'admin_details_metabox_column_wrapper.template.php';
2262
+			// finally if we don't have has_metaboxes set in the route config
2263
+			// let's make sure it IS set otherwise the necessary hidden fields for this won't be loaded.
2264
+			$this->_route_config['has_metaboxes'] = true;
2265
+		}
2266
+	}
2267
+
2268
+
2269
+
2270
+	/** GLOBALLY AVAILABLE METABOXES **/
2271
+
2272
+
2273
+	/**
2274
+	 * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2275
+	 * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2276
+	 * these get loaded on.
2277
+	 */
2278
+	private function _espresso_news_post_box()
2279
+	{
2280
+		$news_box_title = apply_filters(
2281
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2282
+			esc_html__('New @ Event Espresso', 'event_espresso')
2283
+		);
2284
+		$this->addMetaBox(
2285
+			'espresso_news_post_box',
2286
+			$news_box_title,
2287
+			[
2288
+				$this,
2289
+				'espresso_news_post_box',
2290
+			],
2291
+			$this->_wp_page_slug,
2292
+			'side',
2293
+			'low'
2294
+		);
2295
+	}
2296
+
2297
+
2298
+	/**
2299
+	 * Code for setting up espresso ratings request metabox.
2300
+	 */
2301
+	protected function _espresso_ratings_request()
2302
+	{
2303
+		if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2304
+			return;
2305
+		}
2306
+		$ratings_box_title = apply_filters(
2307
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2308
+			esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2309
+		);
2310
+		$this->addMetaBox(
2311
+			'espresso_ratings_request',
2312
+			$ratings_box_title,
2313
+			[
2314
+				$this,
2315
+				'espresso_ratings_request',
2316
+			],
2317
+			$this->_wp_page_slug,
2318
+			'side'
2319
+		);
2320
+	}
2321
+
2322
+
2323
+	/**
2324
+	 * Code for setting up espresso ratings request metabox content.
2325
+	 *
2326
+	 * @throws DomainException
2327
+	 */
2328
+	public function espresso_ratings_request()
2329
+	{
2330
+		EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2331
+	}
2332
+
2333
+
2334
+	public static function cached_rss_display(string $rss_id, string $url): bool
2335
+	{
2336
+		$loading   = '<p class="widget-loading hide-if-no-js">'
2337
+					 . esc_html__('Loading&#8230;', 'event_espresso')
2338
+					 . '</p><p class="hide-if-js">'
2339
+					 . esc_html__('This widget requires JavaScript.', 'event_espresso')
2340
+					 . '</p>';
2341
+		$pre       = '<div class="espresso-rss-display">' . "\n\t";
2342
+		$pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2343
+		$post      = '</div>' . "\n";
2344
+		$cache_key = 'ee_rss_' . md5($rss_id);
2345
+		$output    = get_transient($cache_key);
2346
+		if ($output !== false) {
2347
+			echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2348
+			return true;
2349
+		}
2350
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2351
+			echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2352
+			return false;
2353
+		}
2354
+		ob_start();
2355
+		wp_widget_rss_output($url, ['show_date' => 0, 'items' => 5]);
2356
+		set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2357
+		return true;
2358
+	}
2359
+
2360
+
2361
+	public function espresso_news_post_box()
2362
+	{
2363
+		?>
2364 2364
 <div class="padding">
2365 2365
     <div id="espresso_news_post_box_content" class="infolinks">
2366 2366
         <?php
2367
-                // Get RSS Feed(s)
2368
-                EE_Admin_Page::cached_rss_display(
2369
-                    'espresso_news_post_box_content',
2370
-                    esc_url_raw(
2371
-                        apply_filters(
2372
-                            'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2373
-                            'https://eventespresso.com/feed/'
2374
-                        )
2375
-                    )
2376
-                );
2377
-        ?>
2367
+				// Get RSS Feed(s)
2368
+				EE_Admin_Page::cached_rss_display(
2369
+					'espresso_news_post_box_content',
2370
+					esc_url_raw(
2371
+						apply_filters(
2372
+							'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2373
+							'https://eventespresso.com/feed/'
2374
+						)
2375
+					)
2376
+				);
2377
+		?>
2378 2378
     </div>
2379 2379
     <?php do_action('AHEE__EE_Admin_Page__espresso_news_post_box__after_content'); ?>
2380 2380
 </div>
2381 2381
 <?php
2382
-    }
2383
-
2384
-
2385
-    private function _espresso_links_post_box()
2386
-    {
2387
-        // Hiding until we actually have content to put in here...
2388
-        // $this->addMetaBox('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2389
-    }
2390
-
2391
-
2392
-    public function espresso_links_post_box()
2393
-    {
2394
-        // Hiding until we actually have content to put in here...
2395
-        // EEH_Template::display_template(
2396
-        //     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2397
-        // );
2398
-    }
2399
-
2400
-
2401
-    protected function _espresso_sponsors_post_box()
2402
-    {
2403
-        if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2404
-            $this->addMetaBox(
2405
-                'espresso_sponsors_post_box',
2406
-                esc_html__('Event Espresso Highlights', 'event_espresso'),
2407
-                [$this, 'espresso_sponsors_post_box'],
2408
-                $this->_wp_page_slug,
2409
-                'side'
2410
-            );
2411
-        }
2412
-    }
2413
-
2414
-
2415
-    public function espresso_sponsors_post_box()
2416
-    {
2417
-        EEH_Template::display_template(
2418
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2419
-        );
2420
-    }
2421
-
2422
-
2423
-    /**
2424
-     * if there is [ 'label' => [ 'publishbox' => 'some title' ]]
2425
-     * present in the _page_config array, then we'll use that for the metabox label.
2426
-     * Otherwise we'll just use publish
2427
-     * (publishbox itself could be an array of labels indexed by routes)
2428
-     *
2429
-     * @return string
2430
-     * @since   5.0.0.p
2431
-     */
2432
-    protected function getPublishBoxTitle(): string
2433
-    {
2434
-        $publish_box_title = esc_html__('Publish', 'event_espresso');
2435
-        if (! empty($this->_labels['publishbox'])) {
2436
-            if (is_array($this->_labels['publishbox'])) {
2437
-                $publish_box_title = $this->_labels['publishbox'][ $this->_req_action ] ?? $publish_box_title;
2438
-            } else {
2439
-                $publish_box_title = $this->_labels['publishbox'];
2440
-            }
2441
-        }
2442
-        return apply_filters(
2443
-            'FHEE__EE_Admin_Page___publish_post_box__box_label',
2444
-            $publish_box_title,
2445
-            $this->_req_action,
2446
-            $this
2447
-        );
2448
-    }
2449
-
2450
-
2451
-    /**
2452
-     * @throws EE_Error
2453
-     */
2454
-    private function _publish_post_box()
2455
-    {
2456
-        $title = $this->getPublishBoxTitle();
2457
-        if (empty($this->_template_args['save_buttons'])) {
2458
-            $this->_set_publish_post_box_vars(sanitize_key($title), "espresso_{$this->page_slug}_editor_overview");
2459
-        } else {
2460
-            $this->addPublishPostMetaBoxHiddenFields(
2461
-                sanitize_key($title),
2462
-                ['type' => 'hidden', 'value' => "espresso_{$this->page_slug}_editor_overview"]
2463
-            );
2464
-        }
2465
-        $this->addMetaBox(
2466
-            "espresso_{$this->page_slug}_editor_overview",
2467
-            $title,
2468
-            [$this, 'editor_overview'],
2469
-            $this->_current_screen->id,
2470
-            'side',
2471
-            'high'
2472
-        );
2473
-    }
2474
-
2475
-
2476
-    public function editor_overview()
2477
-    {
2478
-        /**
2479
-         * @var string $publish_box_extra_content
2480
-         * @var string $publish_hidden_fields
2481
-         * @var string $publish_delete_link
2482
-         * @var string $save_buttons
2483
-         */
2484
-        // if we have extra content set let's add it in if not make sure its empty
2485
-        $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2486
-        echo EEH_Template::display_template(
2487
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2488
-            $this->_template_args,
2489
-            true
2490
-        );
2491
-    }
2492
-
2493
-
2494
-    /** end of globally available metaboxes section **/
2495
-
2496
-
2497
-    /**
2498
-     * Sets the _template_args arguments used by the _publish_post_box shortcut
2499
-     * Note: currently there is no validation for this.  However, if you want the delete button, the
2500
-     * save, and save and close buttons to work properly, then you will want to include a
2501
-     * values for the name and id arguments.
2502
-     *
2503
-     * @param string|null $name                     key used for the action ID (i.e. event_id)
2504
-     * @param int|string  $id                       id attached to the item published
2505
-     * @param string|null $delete                   page route callback for the delete action
2506
-     * @param string|null $save_close_redirect_URL  custom URL to redirect to after Save & Close has been completed
2507
-     * @param bool        $both_btns                whether to display BOTH the "Save & Close" and "Save" buttons
2508
-     *                                              or just the "Save" button
2509
-     * @throws EE_Error
2510
-     * @throws InvalidArgumentException
2511
-     * @throws InvalidDataTypeException
2512
-     * @throws InvalidInterfaceException
2513
-     * @todo  Add in validation for name/id arguments.
2514
-     */
2515
-    protected function _set_publish_post_box_vars(
2516
-        ?string $name = '',
2517
-        $id = 0,
2518
-        ?string $delete = '',
2519
-        ?string $save_close_redirect_URL = '',
2520
-        bool $both_btns = true
2521
-    ) {
2522
-        // if Save & Close, use a custom redirect URL or default to the main page?
2523
-        $save_close_redirect_URL = ! empty($save_close_redirect_URL)
2524
-            ? $save_close_redirect_URL
2525
-            : $this->_admin_base_url;
2526
-        // create the Save & Close and Save buttons
2527
-        $this->_set_save_buttons($both_btns, [], [], $save_close_redirect_URL);
2528
-        // if we have extra content set let's add it in if not make sure its empty
2529
-        $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2530
-        if ($delete && ! empty($id) && empty($this->_template_args['publish_delete_link'])) {
2531
-            // make sure we have a default if just true is sent.
2532
-            $delete                                      = ! empty($delete) ? $delete : 'delete';
2533
-            $this->_template_args['publish_delete_link'] = $this->get_action_link_or_button(
2534
-                $delete,
2535
-                $delete,
2536
-                [$name => $id],
2537
-                'submitdelete deletion button button--outline button--caution'
2538
-            );
2539
-        }
2540
-        if (! isset($this->_template_args['publish_delete_link'])) {
2541
-            $this->_template_args['publish_delete_link'] = '';
2542
-        }
2543
-        if (! empty($name) && ! empty($id)) {
2544
-            $this->addPublishPostMetaBoxHiddenFields($name, ['type' => 'hidden', 'value' => $id]);
2545
-        }
2546
-        $hidden_fields = $this->_generate_admin_form_fields($this->publish_post_meta_box_hidden_fields, 'array');
2547
-        // add hidden fields
2548
-        $this->_template_args['publish_hidden_fields'] = $this->_template_args['publish_hidden_fields'] ?? '';
2549
-        foreach ($hidden_fields as $hidden_field) {
2550
-            $this->_template_args['publish_hidden_fields'] .= $hidden_field['field'] ?? '';
2551
-        }
2552
-    }
2553
-
2554
-
2555
-    /**
2556
-     * @param string|null $name
2557
-     * @param int|string  $id
2558
-     * @param string|null $delete
2559
-     * @param string|null $save_close_redirect_URL
2560
-     * @param bool        $both_btns
2561
-     * @throws EE_Error
2562
-     */
2563
-    public function set_publish_post_box_vars(
2564
-        ?string $name = '',
2565
-        $id = 0,
2566
-        ?string $delete = '',
2567
-        ?string $save_close_redirect_URL = '',
2568
-        bool $both_btns = false
2569
-    ) {
2570
-        $this->_set_publish_post_box_vars($name, $id, $delete, $save_close_redirect_URL, $both_btns);
2571
-    }
2572
-
2573
-
2574
-    protected function addPublishPostMetaBoxHiddenFields(string $field_name, array $field_attributes)
2575
-    {
2576
-        $this->publish_post_meta_box_hidden_fields[ $field_name ] = $field_attributes;
2577
-    }
2578
-
2579
-
2580
-    /**
2581
-     * displays an error message to ppl who have javascript disabled
2582
-     *
2583
-     * @return void
2584
-     */
2585
-    private function _display_no_javascript_warning()
2586
-    {
2587
-        ?>
2382
+	}
2383
+
2384
+
2385
+	private function _espresso_links_post_box()
2386
+	{
2387
+		// Hiding until we actually have content to put in here...
2388
+		// $this->addMetaBox('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2389
+	}
2390
+
2391
+
2392
+	public function espresso_links_post_box()
2393
+	{
2394
+		// Hiding until we actually have content to put in here...
2395
+		// EEH_Template::display_template(
2396
+		//     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2397
+		// );
2398
+	}
2399
+
2400
+
2401
+	protected function _espresso_sponsors_post_box()
2402
+	{
2403
+		if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2404
+			$this->addMetaBox(
2405
+				'espresso_sponsors_post_box',
2406
+				esc_html__('Event Espresso Highlights', 'event_espresso'),
2407
+				[$this, 'espresso_sponsors_post_box'],
2408
+				$this->_wp_page_slug,
2409
+				'side'
2410
+			);
2411
+		}
2412
+	}
2413
+
2414
+
2415
+	public function espresso_sponsors_post_box()
2416
+	{
2417
+		EEH_Template::display_template(
2418
+			EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2419
+		);
2420
+	}
2421
+
2422
+
2423
+	/**
2424
+	 * if there is [ 'label' => [ 'publishbox' => 'some title' ]]
2425
+	 * present in the _page_config array, then we'll use that for the metabox label.
2426
+	 * Otherwise we'll just use publish
2427
+	 * (publishbox itself could be an array of labels indexed by routes)
2428
+	 *
2429
+	 * @return string
2430
+	 * @since   5.0.0.p
2431
+	 */
2432
+	protected function getPublishBoxTitle(): string
2433
+	{
2434
+		$publish_box_title = esc_html__('Publish', 'event_espresso');
2435
+		if (! empty($this->_labels['publishbox'])) {
2436
+			if (is_array($this->_labels['publishbox'])) {
2437
+				$publish_box_title = $this->_labels['publishbox'][ $this->_req_action ] ?? $publish_box_title;
2438
+			} else {
2439
+				$publish_box_title = $this->_labels['publishbox'];
2440
+			}
2441
+		}
2442
+		return apply_filters(
2443
+			'FHEE__EE_Admin_Page___publish_post_box__box_label',
2444
+			$publish_box_title,
2445
+			$this->_req_action,
2446
+			$this
2447
+		);
2448
+	}
2449
+
2450
+
2451
+	/**
2452
+	 * @throws EE_Error
2453
+	 */
2454
+	private function _publish_post_box()
2455
+	{
2456
+		$title = $this->getPublishBoxTitle();
2457
+		if (empty($this->_template_args['save_buttons'])) {
2458
+			$this->_set_publish_post_box_vars(sanitize_key($title), "espresso_{$this->page_slug}_editor_overview");
2459
+		} else {
2460
+			$this->addPublishPostMetaBoxHiddenFields(
2461
+				sanitize_key($title),
2462
+				['type' => 'hidden', 'value' => "espresso_{$this->page_slug}_editor_overview"]
2463
+			);
2464
+		}
2465
+		$this->addMetaBox(
2466
+			"espresso_{$this->page_slug}_editor_overview",
2467
+			$title,
2468
+			[$this, 'editor_overview'],
2469
+			$this->_current_screen->id,
2470
+			'side',
2471
+			'high'
2472
+		);
2473
+	}
2474
+
2475
+
2476
+	public function editor_overview()
2477
+	{
2478
+		/**
2479
+		 * @var string $publish_box_extra_content
2480
+		 * @var string $publish_hidden_fields
2481
+		 * @var string $publish_delete_link
2482
+		 * @var string $save_buttons
2483
+		 */
2484
+		// if we have extra content set let's add it in if not make sure its empty
2485
+		$this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2486
+		echo EEH_Template::display_template(
2487
+			EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2488
+			$this->_template_args,
2489
+			true
2490
+		);
2491
+	}
2492
+
2493
+
2494
+	/** end of globally available metaboxes section **/
2495
+
2496
+
2497
+	/**
2498
+	 * Sets the _template_args arguments used by the _publish_post_box shortcut
2499
+	 * Note: currently there is no validation for this.  However, if you want the delete button, the
2500
+	 * save, and save and close buttons to work properly, then you will want to include a
2501
+	 * values for the name and id arguments.
2502
+	 *
2503
+	 * @param string|null $name                     key used for the action ID (i.e. event_id)
2504
+	 * @param int|string  $id                       id attached to the item published
2505
+	 * @param string|null $delete                   page route callback for the delete action
2506
+	 * @param string|null $save_close_redirect_URL  custom URL to redirect to after Save & Close has been completed
2507
+	 * @param bool        $both_btns                whether to display BOTH the "Save & Close" and "Save" buttons
2508
+	 *                                              or just the "Save" button
2509
+	 * @throws EE_Error
2510
+	 * @throws InvalidArgumentException
2511
+	 * @throws InvalidDataTypeException
2512
+	 * @throws InvalidInterfaceException
2513
+	 * @todo  Add in validation for name/id arguments.
2514
+	 */
2515
+	protected function _set_publish_post_box_vars(
2516
+		?string $name = '',
2517
+		$id = 0,
2518
+		?string $delete = '',
2519
+		?string $save_close_redirect_URL = '',
2520
+		bool $both_btns = true
2521
+	) {
2522
+		// if Save & Close, use a custom redirect URL or default to the main page?
2523
+		$save_close_redirect_URL = ! empty($save_close_redirect_URL)
2524
+			? $save_close_redirect_URL
2525
+			: $this->_admin_base_url;
2526
+		// create the Save & Close and Save buttons
2527
+		$this->_set_save_buttons($both_btns, [], [], $save_close_redirect_URL);
2528
+		// if we have extra content set let's add it in if not make sure its empty
2529
+		$this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2530
+		if ($delete && ! empty($id) && empty($this->_template_args['publish_delete_link'])) {
2531
+			// make sure we have a default if just true is sent.
2532
+			$delete                                      = ! empty($delete) ? $delete : 'delete';
2533
+			$this->_template_args['publish_delete_link'] = $this->get_action_link_or_button(
2534
+				$delete,
2535
+				$delete,
2536
+				[$name => $id],
2537
+				'submitdelete deletion button button--outline button--caution'
2538
+			);
2539
+		}
2540
+		if (! isset($this->_template_args['publish_delete_link'])) {
2541
+			$this->_template_args['publish_delete_link'] = '';
2542
+		}
2543
+		if (! empty($name) && ! empty($id)) {
2544
+			$this->addPublishPostMetaBoxHiddenFields($name, ['type' => 'hidden', 'value' => $id]);
2545
+		}
2546
+		$hidden_fields = $this->_generate_admin_form_fields($this->publish_post_meta_box_hidden_fields, 'array');
2547
+		// add hidden fields
2548
+		$this->_template_args['publish_hidden_fields'] = $this->_template_args['publish_hidden_fields'] ?? '';
2549
+		foreach ($hidden_fields as $hidden_field) {
2550
+			$this->_template_args['publish_hidden_fields'] .= $hidden_field['field'] ?? '';
2551
+		}
2552
+	}
2553
+
2554
+
2555
+	/**
2556
+	 * @param string|null $name
2557
+	 * @param int|string  $id
2558
+	 * @param string|null $delete
2559
+	 * @param string|null $save_close_redirect_URL
2560
+	 * @param bool        $both_btns
2561
+	 * @throws EE_Error
2562
+	 */
2563
+	public function set_publish_post_box_vars(
2564
+		?string $name = '',
2565
+		$id = 0,
2566
+		?string $delete = '',
2567
+		?string $save_close_redirect_URL = '',
2568
+		bool $both_btns = false
2569
+	) {
2570
+		$this->_set_publish_post_box_vars($name, $id, $delete, $save_close_redirect_URL, $both_btns);
2571
+	}
2572
+
2573
+
2574
+	protected function addPublishPostMetaBoxHiddenFields(string $field_name, array $field_attributes)
2575
+	{
2576
+		$this->publish_post_meta_box_hidden_fields[ $field_name ] = $field_attributes;
2577
+	}
2578
+
2579
+
2580
+	/**
2581
+	 * displays an error message to ppl who have javascript disabled
2582
+	 *
2583
+	 * @return void
2584
+	 */
2585
+	private function _display_no_javascript_warning()
2586
+	{
2587
+		?>
2588 2588
 <noscript>
2589 2589
     <div id="no-js-message" class="error">
2590 2590
         <p style="font-size:1.3em;">
2591 2591
             <span style="color:red;"><?php esc_html_e('Warning!', 'event_espresso'); ?></span>
2592 2592
             <?php esc_html_e(
2593
-                '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.',
2594
-                'event_espresso'
2595
-            ); ?>
2593
+				'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.',
2594
+				'event_espresso'
2595
+			); ?>
2596 2596
         </p>
2597 2597
     </div>
2598 2598
 </noscript>
2599 2599
 <?php
2600
-    }
2601
-
2602
-
2603
-    /**
2604
-     * displays espresso success and/or error notices
2605
-     *
2606
-     * @return void
2607
-     */
2608
-    protected function _display_espresso_notices()
2609
-    {
2610
-        $notices = (array) $this->_get_transient(true);
2611
-        foreach ($notices as $notice) {
2612
-            echo $notice ? stripslashes($notice) : '';
2613
-        }
2614
-    }
2615
-
2616
-
2617
-    /**
2618
-     * spinny things pacify the masses
2619
-     *
2620
-     * @return void
2621
-     */
2622
-    protected function _add_admin_page_ajax_loading_img()
2623
-    {
2624
-        ?>
2600
+	}
2601
+
2602
+
2603
+	/**
2604
+	 * displays espresso success and/or error notices
2605
+	 *
2606
+	 * @return void
2607
+	 */
2608
+	protected function _display_espresso_notices()
2609
+	{
2610
+		$notices = (array) $this->_get_transient(true);
2611
+		foreach ($notices as $notice) {
2612
+			echo $notice ? stripslashes($notice) : '';
2613
+		}
2614
+	}
2615
+
2616
+
2617
+	/**
2618
+	 * spinny things pacify the masses
2619
+	 *
2620
+	 * @return void
2621
+	 */
2622
+	protected function _add_admin_page_ajax_loading_img()
2623
+	{
2624
+		?>
2625 2625
 <div id="espresso-ajax-loading" class="ajax-loading-grey">
2626 2626
     <span class="ee-spinner ee-spin"></span><span class="hidden"><?php
2627
-                esc_html_e('loading...', 'event_espresso'); ?></span>
2627
+				esc_html_e('loading...', 'event_espresso'); ?></span>
2628 2628
 </div>
2629 2629
 <?php
2630
-    }
2630
+	}
2631 2631
 
2632 2632
 
2633
-    /**
2634
-     * add admin page overlay for modal boxes
2635
-     *
2636
-     * @return void
2637
-     */
2638
-    protected function _add_admin_page_overlay()
2639
-    {
2640
-        ?>
2633
+	/**
2634
+	 * add admin page overlay for modal boxes
2635
+	 *
2636
+	 * @return void
2637
+	 */
2638
+	protected function _add_admin_page_overlay()
2639
+	{
2640
+		?>
2641 2641
 <div id="espresso-admin-page-overlay-dv" class=""></div>
2642 2642
 <?php
2643
-    }
2644
-
2645
-
2646
-    /**
2647
-     * facade for $this->addMetaBox()
2648
-     *
2649
-     * @param string   $action        where the metabox gets displayed
2650
-     * @param string   $title         Title of Metabox (output in metabox header)
2651
-     * @param callable $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2652
-     *                                instead of the one created in here.
2653
-     * @param array    $callback_args an array of args supplied for the metabox
2654
-     * @param string   $column        what metabox column
2655
-     * @param string   $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2656
-     * @param bool     $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2657
-     *                                created but just set our own callback for wp's add_meta_box.
2658
-     * @throws DomainException
2659
-     */
2660
-    public function _add_admin_page_meta_box(
2661
-        string $action,
2662
-        string $title,
2663
-        callable $callback,
2664
-        array $callback_args,
2665
-        string $column = 'normal',
2666
-        string $priority = 'high',
2667
-        bool $create_func = true
2668
-    ) {
2669
-        // 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.
2670
-        if (empty($callback_args) && $create_func) {
2671
-            $callback_args = [
2672
-                'template_path' => $this->_template_path,
2673
-                'template_args' => $this->_template_args,
2674
-            ];
2675
-        }
2676
-        // 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)
2677
-        $call_back_func = $create_func
2678
-            ? static function ($post, $metabox) {
2679
-                echo EEH_Template::display_template(
2680
-                    $metabox['args']['template_path'],
2681
-                    $metabox['args']['template_args'],
2682
-                    true
2683
-                );
2684
-            }
2685
-            : $callback;
2686
-        $this->addMetaBox(
2687
-            str_replace('_', '-', $action) . '-mbox',
2688
-            $title,
2689
-            $call_back_func,
2690
-            $this->_wp_page_slug,
2691
-            $column,
2692
-            $priority,
2693
-            $callback_args
2694
-        );
2695
-    }
2696
-
2697
-
2698
-    /**
2699
-     * generates HTML wrapper for and admin details page that contains metaboxes in columns
2700
-     *
2701
-     * @throws DomainException
2702
-     * @throws EE_Error
2703
-     * @throws InvalidArgumentException
2704
-     * @throws InvalidDataTypeException
2705
-     * @throws InvalidInterfaceException
2706
-     */
2707
-    public function display_admin_page_with_metabox_columns()
2708
-    {
2709
-        $this->_template_args['post_body_content']  = $this->_template_args['admin_page_content'];
2710
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2711
-            $this->_column_template_path,
2712
-            $this->_template_args,
2713
-            true
2714
-        );
2715
-        // the final wrapper
2716
-        $this->admin_page_wrapper();
2717
-    }
2718
-
2719
-
2720
-    /**
2721
-     * generates  HTML wrapper for an admin details page
2722
-     *
2723
-     * @return void
2724
-     * @throws DomainException
2725
-     * @throws EE_Error
2726
-     * @throws InvalidArgumentException
2727
-     * @throws InvalidDataTypeException
2728
-     * @throws InvalidInterfaceException
2729
-     */
2730
-    public function display_admin_page_with_sidebar()
2731
-    {
2732
-        $this->_display_admin_page(true);
2733
-    }
2734
-
2735
-
2736
-    /**
2737
-     * generates  HTML wrapper for an admin details page (except no sidebar)
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_no_sidebar()
2747
-    {
2748
-        $this->_display_admin_page();
2749
-    }
2750
-
2751
-
2752
-    /**
2753
-     * generates HTML wrapper for an EE about admin page (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_about_admin_page()
2763
-    {
2764
-        $this->_display_admin_page(false, true);
2765
-    }
2766
-
2767
-
2768
-    /**
2769
-     * display_admin_page
2770
-     * contains the code for actually displaying an admin page
2771
-     *
2772
-     * @param bool $sidebar true with sidebar, false without
2773
-     * @param bool $about   use the About admin wrapper instead of the default.
2774
-     * @return void
2775
-     * @throws DomainException
2776
-     * @throws EE_Error
2777
-     * @throws InvalidArgumentException
2778
-     * @throws InvalidDataTypeException
2779
-     * @throws InvalidInterfaceException
2780
-     */
2781
-    private function _display_admin_page(bool $sidebar = false, bool $about = false): void
2782
-    {
2783
-        // custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2784
-        do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2785
-
2786
-        // set current wp page slug - looks like: event-espresso_page_event_categories
2787
-        // keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2788
-        $post_body_content = $this->_template_args['before_admin_page_content'] ?? '';
2789
-
2790
-        $this->_template_args['add_page_frame'] = $this->_req_action !== 'system_status'
2791
-                                                  && $this->_req_action !== 'data_reset'
2792
-                                                  && $this->_wp_page_slug !== 'event-espresso_page_espresso_packages'
2793
-                                                  && strpos($post_body_content, 'wp-list-table') === false;
2794
-
2795
-        $this->_template_args['current_page']                 = $this->_wp_page_slug;
2796
-        $this->_template_args['admin_page_wrapper_div_id']    = $this->_cpt_route
2797
-            ? 'poststuff'
2798
-            : 'espresso-default-admin';
2799
-        $this->_template_args['admin_page_wrapper_div_class'] = str_replace(
2800
-            'event-espresso_page_espresso_',
2801
-            '',
2802
-            $this->_wp_page_slug
2803
-        ) . ' ' . $this->_req_action . '-route';
2804
-
2805
-        $template_path = $sidebar
2806
-            ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2807
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2808
-
2809
-        $this->_template_args['is_ajax'] = $this->request->isAjax();
2810
-        if ($this->request->isAjax()) {
2811
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2812
-        }
2813
-        $template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2814
-
2815
-        $this->_template_args['post_body_content']         = $this->_template_args['admin_page_content'] ?? '';
2816
-        $this->_template_args['before_admin_page_content'] = $post_body_content;
2817
-        $this->_template_args['after_admin_page_content']  = $this->_template_args['after_admin_page_content'] ?? '';
2818
-
2819
-        // ensure $post_type and $post are set
2820
-        // to prevent WooCommerce from blowing things up if not using CPT
2821
-        global $post_type, $post;
2822
-        $this->_template_args['post_type'] = $post_type ?? '';
2823
-        $this->_template_args['post']  = $post ?? new WP_Post((object) [ 'ID' => 0, 'filter' => 'raw' ]);
2824
-
2825
-        $this->_template_args['post_body_content'] = EEH_Template::display_template(
2826
-            EE_ADMIN_TEMPLATE . 'admin_details_wrapper_post_body_content.template.php',
2827
-            $this->_template_args,
2828
-            true
2829
-        );
2830
-
2831
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2832
-            $template_path,
2833
-            $this->_template_args,
2834
-            true
2835
-        );
2836
-        // the final template wrapper
2837
-        $this->admin_page_wrapper($about);
2838
-    }
2839
-
2840
-
2841
-    /**
2842
-     * This is used to display caf preview pages.
2843
-     *
2844
-     * @param string $utm_campaign_source what is the key used for Google Analytics link
2845
-     * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2846
-     *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2847
-     * @return void
2848
-     * @throws DomainException
2849
-     * @throws EE_Error
2850
-     * @throws InvalidArgumentException
2851
-     * @throws InvalidDataTypeException
2852
-     * @throws InvalidInterfaceException
2853
-     * @since 4.3.2
2854
-     */
2855
-    public function display_admin_caf_preview_page(string $utm_campaign_source = '', bool $display_sidebar = true)
2856
-    {
2857
-        // let's generate a default preview action button if there isn't one already present.
2858
-        $this->_labels['buttons']['buy_now']           = esc_html__(
2859
-            'Upgrade to Event Espresso 4 Right Now',
2860
-            'event_espresso'
2861
-        );
2862
-        $buy_now_url                                   = add_query_arg(
2863
-            [
2864
-                'ee_ver'       => 'ee4',
2865
-                'utm_source'   => 'ee4_plugin_admin',
2866
-                'utm_medium'   => 'link',
2867
-                'utm_campaign' => $utm_campaign_source,
2868
-                'utm_content'  => 'buy_now_button',
2869
-            ],
2870
-            'https://eventespresso.com/pricing/'
2871
-        );
2872
-        $this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2873
-            ? $this->get_action_link_or_button(
2874
-                '',
2875
-                'buy_now',
2876
-                [],
2877
-                'button button--primary button--big',
2878
-                esc_url_raw($buy_now_url),
2879
-                true
2880
-            )
2881
-            : $this->_template_args['preview_action_button'];
2882
-        $this->_template_args['admin_page_content']    = EEH_Template::display_template(
2883
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2884
-            $this->_template_args,
2885
-            true
2886
-        );
2887
-        $this->_display_admin_page($display_sidebar);
2888
-    }
2889
-
2890
-
2891
-    /**
2892
-     * display_admin_list_table_page_with_sidebar
2893
-     * generates HTML wrapper for an admin_page with list_table
2894
-     *
2895
-     * @return void
2896
-     * @throws DomainException
2897
-     * @throws EE_Error
2898
-     * @throws InvalidArgumentException
2899
-     * @throws InvalidDataTypeException
2900
-     * @throws InvalidInterfaceException
2901
-     */
2902
-    public function display_admin_list_table_page_with_sidebar()
2903
-    {
2904
-        $this->_display_admin_list_table_page(true);
2905
-    }
2906
-
2907
-
2908
-    /**
2909
-     * display_admin_list_table_page_with_no_sidebar
2910
-     * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2911
-     *
2912
-     * @return void
2913
-     * @throws DomainException
2914
-     * @throws EE_Error
2915
-     * @throws InvalidArgumentException
2916
-     * @throws InvalidDataTypeException
2917
-     * @throws InvalidInterfaceException
2918
-     */
2919
-    public function display_admin_list_table_page_with_no_sidebar()
2920
-    {
2921
-        $this->_display_admin_list_table_page();
2922
-    }
2923
-
2924
-
2925
-    /**
2926
-     * generates html wrapper for an admin_list_table page
2927
-     *
2928
-     * @param bool $sidebar whether to display with sidebar or not.
2929
-     * @return void
2930
-     * @throws DomainException
2931
-     * @throws EE_Error
2932
-     * @throws InvalidArgumentException
2933
-     * @throws InvalidDataTypeException
2934
-     * @throws InvalidInterfaceException
2935
-     */
2936
-    private function _display_admin_list_table_page(bool $sidebar = false)
2937
-    {
2938
-        // setup search attributes
2939
-        $this->_set_search_attributes();
2940
-        $this->_template_args['current_page']     = $this->_wp_page_slug;
2941
-        $template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2942
-        $this->_template_args['table_url']        = $this->request->isAjax()
2943
-            ? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2944
-            : add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
2945
-        $this->_template_args['list_table']       = $this->_list_table_object;
2946
-        $this->_template_args['current_route']    = $this->_req_action;
2947
-        $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2948
-        $ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2949
-        if (! empty($ajax_sorting_callback)) {
2950
-            $sortable_list_table_form_fields = wp_nonce_field(
2951
-                $ajax_sorting_callback . '_nonce',
2952
-                $ajax_sorting_callback . '_nonce',
2953
-                false,
2954
-                false
2955
-            );
2956
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2957
-                                                . $this->page_slug
2958
-                                                . '" />';
2959
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2960
-                                                . $ajax_sorting_callback
2961
-                                                . '" />';
2962
-        } else {
2963
-            $sortable_list_table_form_fields = '';
2964
-        }
2965
-        $this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2966
-
2967
-        $hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2968
-
2969
-        $nonce_ref          = $this->_req_action . '_nonce';
2970
-        $hidden_form_fields .= '
2643
+	}
2644
+
2645
+
2646
+	/**
2647
+	 * facade for $this->addMetaBox()
2648
+	 *
2649
+	 * @param string   $action        where the metabox gets displayed
2650
+	 * @param string   $title         Title of Metabox (output in metabox header)
2651
+	 * @param callable $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2652
+	 *                                instead of the one created in here.
2653
+	 * @param array    $callback_args an array of args supplied for the metabox
2654
+	 * @param string   $column        what metabox column
2655
+	 * @param string   $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2656
+	 * @param bool     $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2657
+	 *                                created but just set our own callback for wp's add_meta_box.
2658
+	 * @throws DomainException
2659
+	 */
2660
+	public function _add_admin_page_meta_box(
2661
+		string $action,
2662
+		string $title,
2663
+		callable $callback,
2664
+		array $callback_args,
2665
+		string $column = 'normal',
2666
+		string $priority = 'high',
2667
+		bool $create_func = true
2668
+	) {
2669
+		// 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.
2670
+		if (empty($callback_args) && $create_func) {
2671
+			$callback_args = [
2672
+				'template_path' => $this->_template_path,
2673
+				'template_args' => $this->_template_args,
2674
+			];
2675
+		}
2676
+		// 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)
2677
+		$call_back_func = $create_func
2678
+			? static function ($post, $metabox) {
2679
+				echo EEH_Template::display_template(
2680
+					$metabox['args']['template_path'],
2681
+					$metabox['args']['template_args'],
2682
+					true
2683
+				);
2684
+			}
2685
+			: $callback;
2686
+		$this->addMetaBox(
2687
+			str_replace('_', '-', $action) . '-mbox',
2688
+			$title,
2689
+			$call_back_func,
2690
+			$this->_wp_page_slug,
2691
+			$column,
2692
+			$priority,
2693
+			$callback_args
2694
+		);
2695
+	}
2696
+
2697
+
2698
+	/**
2699
+	 * generates HTML wrapper for and admin details page that contains metaboxes in columns
2700
+	 *
2701
+	 * @throws DomainException
2702
+	 * @throws EE_Error
2703
+	 * @throws InvalidArgumentException
2704
+	 * @throws InvalidDataTypeException
2705
+	 * @throws InvalidInterfaceException
2706
+	 */
2707
+	public function display_admin_page_with_metabox_columns()
2708
+	{
2709
+		$this->_template_args['post_body_content']  = $this->_template_args['admin_page_content'];
2710
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2711
+			$this->_column_template_path,
2712
+			$this->_template_args,
2713
+			true
2714
+		);
2715
+		// the final wrapper
2716
+		$this->admin_page_wrapper();
2717
+	}
2718
+
2719
+
2720
+	/**
2721
+	 * generates  HTML wrapper for an admin details page
2722
+	 *
2723
+	 * @return void
2724
+	 * @throws DomainException
2725
+	 * @throws EE_Error
2726
+	 * @throws InvalidArgumentException
2727
+	 * @throws InvalidDataTypeException
2728
+	 * @throws InvalidInterfaceException
2729
+	 */
2730
+	public function display_admin_page_with_sidebar()
2731
+	{
2732
+		$this->_display_admin_page(true);
2733
+	}
2734
+
2735
+
2736
+	/**
2737
+	 * generates  HTML wrapper for an admin details page (except no sidebar)
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_no_sidebar()
2747
+	{
2748
+		$this->_display_admin_page();
2749
+	}
2750
+
2751
+
2752
+	/**
2753
+	 * generates HTML wrapper for an EE about admin page (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_about_admin_page()
2763
+	{
2764
+		$this->_display_admin_page(false, true);
2765
+	}
2766
+
2767
+
2768
+	/**
2769
+	 * display_admin_page
2770
+	 * contains the code for actually displaying an admin page
2771
+	 *
2772
+	 * @param bool $sidebar true with sidebar, false without
2773
+	 * @param bool $about   use the About admin wrapper instead of the default.
2774
+	 * @return void
2775
+	 * @throws DomainException
2776
+	 * @throws EE_Error
2777
+	 * @throws InvalidArgumentException
2778
+	 * @throws InvalidDataTypeException
2779
+	 * @throws InvalidInterfaceException
2780
+	 */
2781
+	private function _display_admin_page(bool $sidebar = false, bool $about = false): void
2782
+	{
2783
+		// custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2784
+		do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2785
+
2786
+		// set current wp page slug - looks like: event-espresso_page_event_categories
2787
+		// keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2788
+		$post_body_content = $this->_template_args['before_admin_page_content'] ?? '';
2789
+
2790
+		$this->_template_args['add_page_frame'] = $this->_req_action !== 'system_status'
2791
+												  && $this->_req_action !== 'data_reset'
2792
+												  && $this->_wp_page_slug !== 'event-espresso_page_espresso_packages'
2793
+												  && strpos($post_body_content, 'wp-list-table') === false;
2794
+
2795
+		$this->_template_args['current_page']                 = $this->_wp_page_slug;
2796
+		$this->_template_args['admin_page_wrapper_div_id']    = $this->_cpt_route
2797
+			? 'poststuff'
2798
+			: 'espresso-default-admin';
2799
+		$this->_template_args['admin_page_wrapper_div_class'] = str_replace(
2800
+			'event-espresso_page_espresso_',
2801
+			'',
2802
+			$this->_wp_page_slug
2803
+		) . ' ' . $this->_req_action . '-route';
2804
+
2805
+		$template_path = $sidebar
2806
+			? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2807
+			: EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2808
+
2809
+		$this->_template_args['is_ajax'] = $this->request->isAjax();
2810
+		if ($this->request->isAjax()) {
2811
+			$template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2812
+		}
2813
+		$template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2814
+
2815
+		$this->_template_args['post_body_content']         = $this->_template_args['admin_page_content'] ?? '';
2816
+		$this->_template_args['before_admin_page_content'] = $post_body_content;
2817
+		$this->_template_args['after_admin_page_content']  = $this->_template_args['after_admin_page_content'] ?? '';
2818
+
2819
+		// ensure $post_type and $post are set
2820
+		// to prevent WooCommerce from blowing things up if not using CPT
2821
+		global $post_type, $post;
2822
+		$this->_template_args['post_type'] = $post_type ?? '';
2823
+		$this->_template_args['post']  = $post ?? new WP_Post((object) [ 'ID' => 0, 'filter' => 'raw' ]);
2824
+
2825
+		$this->_template_args['post_body_content'] = EEH_Template::display_template(
2826
+			EE_ADMIN_TEMPLATE . 'admin_details_wrapper_post_body_content.template.php',
2827
+			$this->_template_args,
2828
+			true
2829
+		);
2830
+
2831
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2832
+			$template_path,
2833
+			$this->_template_args,
2834
+			true
2835
+		);
2836
+		// the final template wrapper
2837
+		$this->admin_page_wrapper($about);
2838
+	}
2839
+
2840
+
2841
+	/**
2842
+	 * This is used to display caf preview pages.
2843
+	 *
2844
+	 * @param string $utm_campaign_source what is the key used for Google Analytics link
2845
+	 * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2846
+	 *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2847
+	 * @return void
2848
+	 * @throws DomainException
2849
+	 * @throws EE_Error
2850
+	 * @throws InvalidArgumentException
2851
+	 * @throws InvalidDataTypeException
2852
+	 * @throws InvalidInterfaceException
2853
+	 * @since 4.3.2
2854
+	 */
2855
+	public function display_admin_caf_preview_page(string $utm_campaign_source = '', bool $display_sidebar = true)
2856
+	{
2857
+		// let's generate a default preview action button if there isn't one already present.
2858
+		$this->_labels['buttons']['buy_now']           = esc_html__(
2859
+			'Upgrade to Event Espresso 4 Right Now',
2860
+			'event_espresso'
2861
+		);
2862
+		$buy_now_url                                   = add_query_arg(
2863
+			[
2864
+				'ee_ver'       => 'ee4',
2865
+				'utm_source'   => 'ee4_plugin_admin',
2866
+				'utm_medium'   => 'link',
2867
+				'utm_campaign' => $utm_campaign_source,
2868
+				'utm_content'  => 'buy_now_button',
2869
+			],
2870
+			'https://eventespresso.com/pricing/'
2871
+		);
2872
+		$this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2873
+			? $this->get_action_link_or_button(
2874
+				'',
2875
+				'buy_now',
2876
+				[],
2877
+				'button button--primary button--big',
2878
+				esc_url_raw($buy_now_url),
2879
+				true
2880
+			)
2881
+			: $this->_template_args['preview_action_button'];
2882
+		$this->_template_args['admin_page_content']    = EEH_Template::display_template(
2883
+			EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2884
+			$this->_template_args,
2885
+			true
2886
+		);
2887
+		$this->_display_admin_page($display_sidebar);
2888
+	}
2889
+
2890
+
2891
+	/**
2892
+	 * display_admin_list_table_page_with_sidebar
2893
+	 * generates HTML wrapper for an admin_page with list_table
2894
+	 *
2895
+	 * @return void
2896
+	 * @throws DomainException
2897
+	 * @throws EE_Error
2898
+	 * @throws InvalidArgumentException
2899
+	 * @throws InvalidDataTypeException
2900
+	 * @throws InvalidInterfaceException
2901
+	 */
2902
+	public function display_admin_list_table_page_with_sidebar()
2903
+	{
2904
+		$this->_display_admin_list_table_page(true);
2905
+	}
2906
+
2907
+
2908
+	/**
2909
+	 * display_admin_list_table_page_with_no_sidebar
2910
+	 * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2911
+	 *
2912
+	 * @return void
2913
+	 * @throws DomainException
2914
+	 * @throws EE_Error
2915
+	 * @throws InvalidArgumentException
2916
+	 * @throws InvalidDataTypeException
2917
+	 * @throws InvalidInterfaceException
2918
+	 */
2919
+	public function display_admin_list_table_page_with_no_sidebar()
2920
+	{
2921
+		$this->_display_admin_list_table_page();
2922
+	}
2923
+
2924
+
2925
+	/**
2926
+	 * generates html wrapper for an admin_list_table page
2927
+	 *
2928
+	 * @param bool $sidebar whether to display with sidebar or not.
2929
+	 * @return void
2930
+	 * @throws DomainException
2931
+	 * @throws EE_Error
2932
+	 * @throws InvalidArgumentException
2933
+	 * @throws InvalidDataTypeException
2934
+	 * @throws InvalidInterfaceException
2935
+	 */
2936
+	private function _display_admin_list_table_page(bool $sidebar = false)
2937
+	{
2938
+		// setup search attributes
2939
+		$this->_set_search_attributes();
2940
+		$this->_template_args['current_page']     = $this->_wp_page_slug;
2941
+		$template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2942
+		$this->_template_args['table_url']        = $this->request->isAjax()
2943
+			? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2944
+			: add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
2945
+		$this->_template_args['list_table']       = $this->_list_table_object;
2946
+		$this->_template_args['current_route']    = $this->_req_action;
2947
+		$this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2948
+		$ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2949
+		if (! empty($ajax_sorting_callback)) {
2950
+			$sortable_list_table_form_fields = wp_nonce_field(
2951
+				$ajax_sorting_callback . '_nonce',
2952
+				$ajax_sorting_callback . '_nonce',
2953
+				false,
2954
+				false
2955
+			);
2956
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2957
+												. $this->page_slug
2958
+												. '" />';
2959
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2960
+												. $ajax_sorting_callback
2961
+												. '" />';
2962
+		} else {
2963
+			$sortable_list_table_form_fields = '';
2964
+		}
2965
+		$this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2966
+
2967
+		$hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2968
+
2969
+		$nonce_ref          = $this->_req_action . '_nonce';
2970
+		$hidden_form_fields .= '
2971 2971
             <input type="hidden" name="' . $nonce_ref . '" value="' . wp_create_nonce($nonce_ref) . '">';
2972 2972
 
2973
-        $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2974
-        // display message about search results?
2975
-        $search                                    = $this->request->getRequestParam('s');
2976
-        $this->_template_args['before_list_table'] .= ! empty($search)
2977
-            ? '<p class="ee-search-results">' . sprintf(
2978
-                esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2979
-                trim($search, '%')
2980
-            ) . '</p>'
2981
-            : '';
2982
-        // filter before_list_table template arg
2983
-        $this->_template_args['before_list_table'] = apply_filters(
2984
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2985
-            $this->_template_args['before_list_table'],
2986
-            $this->page_slug,
2987
-            $this->request->requestParams(),
2988
-            $this->_req_action
2989
-        );
2990
-        // convert to array and filter again
2991
-        // arrays are easier to inject new items in a specific location,
2992
-        // but would not be backwards compatible, so we have to add a new filter
2993
-        $this->_template_args['before_list_table'] = implode(
2994
-            " \n",
2995
-            (array) apply_filters(
2996
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
2997
-                (array) $this->_template_args['before_list_table'],
2998
-                $this->page_slug,
2999
-                $this->request->requestParams(),
3000
-                $this->_req_action
3001
-            )
3002
-        );
3003
-        // filter after_list_table template arg
3004
-        $this->_template_args['after_list_table'] = apply_filters(
3005
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3006
-            $this->_template_args['after_list_table'],
3007
-            $this->page_slug,
3008
-            $this->request->requestParams(),
3009
-            $this->_req_action
3010
-        );
3011
-        // convert to array and filter again
3012
-        // arrays are easier to inject new items in a specific location,
3013
-        // but would not be backwards compatible, so we have to add a new filter
3014
-        $this->_template_args['after_list_table']   = implode(
3015
-            " \n",
3016
-            (array) apply_filters(
3017
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3018
-                (array) $this->_template_args['after_list_table'],
3019
-                $this->page_slug,
3020
-                $this->request->requestParams(),
3021
-                $this->_req_action
3022
-            )
3023
-        );
3024
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3025
-            $template_path,
3026
-            $this->_template_args,
3027
-            true
3028
-        );
3029
-        // the final template wrapper
3030
-        if ($sidebar) {
3031
-            $this->display_admin_page_with_sidebar();
3032
-        } else {
3033
-            $this->display_admin_page_with_no_sidebar();
3034
-        }
3035
-    }
3036
-
3037
-
3038
-    /**
3039
-     * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3040
-     * html string for the legend.
3041
-     * $items are expected in an array in the following format:
3042
-     * $legend_items = array(
3043
-     *        'item_id' => array(
3044
-     *            'icon' => 'http://url_to_icon_being_described.png',
3045
-     *            'desc' => esc_html__('localized description of item');
3046
-     *        )
3047
-     * );
3048
-     *
3049
-     * @param array $items see above for format of array
3050
-     * @return string html string of legend
3051
-     * @throws DomainException
3052
-     */
3053
-    protected function _display_legend(array $items): string
3054
-    {
3055
-        $this->_template_args['items'] = (array) apply_filters(
3056
-            'FHEE__EE_Admin_Page___display_legend__items',
3057
-            $items,
3058
-            $this
3059
-        );
3060
-        /** @var StatusChangeNotice $status_change_notice */
3061
-        $status_change_notice                         = $this->loader->getShared(
3062
-            'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
3063
-        );
3064
-        $this->_template_args['status_change_notice'] = $status_change_notice->display(
3065
-            '__admin-legend',
3066
-            $this->page_slug
3067
-        );
3068
-        return EEH_Template::display_template(
3069
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3070
-            $this->_template_args,
3071
-            true
3072
-        );
3073
-    }
3074
-
3075
-
3076
-    /**
3077
-     * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3078
-     * The returned json object is created from an array in the following format:
3079
-     * array(
3080
-     *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3081
-     *  'success' => FALSE, //(default FALSE) - contains any special success message.
3082
-     *  'notices' => '', // - contains any EE_Error formatted notices
3083
-     *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3084
-     *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3085
-     *  We're also going to include the template args with every package (so js can pick out any specific template args
3086
-     *  that might be included in here)
3087
-     * )
3088
-     * The json object is populated by whatever is set in the $_template_args property.
3089
-     *
3090
-     * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3091
-     *                                 instead of displayed.
3092
-     * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3093
-     * @return void
3094
-     * @throws EE_Error
3095
-     * @throws InvalidArgumentException
3096
-     * @throws InvalidDataTypeException
3097
-     * @throws InvalidInterfaceException
3098
-     */
3099
-    protected function _return_json(bool $sticky_notices = false, array $notices_arguments = [])
3100
-    {
3101
-        // make sure any EE_Error notices have been handled.
3102
-        $this->_process_notices($notices_arguments, true, $sticky_notices);
3103
-        $data = $this->_template_args['data'] ?? [];
3104
-        unset($this->_template_args['data']);
3105
-        $json = [
3106
-            'error'     => $this->_template_args['error'] ?? false,
3107
-            'success'   => $this->_template_args['success'] ?? false,
3108
-            'errors'    => $this->_template_args['errors'] ?? false,
3109
-            'attention' => $this->_template_args['attention'] ?? false,
3110
-            'notices'   => EE_Error::get_notices(),
3111
-            'content'   => $this->_template_args['admin_page_content'] ?? '',
3112
-            'data'      => array_merge($data, ['template_args' => $this->_template_args]),
3113
-            'isEEajax'  => true,
3114
-            // special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3115
-        ];
3116
-        // make sure there are no php errors or headers_sent.  Then we can set correct json header.
3117
-        if (null === error_get_last() || ! headers_sent()) {
3118
-            header('Content-Type: application/json; charset=UTF-8');
3119
-        }
3120
-        echo wp_json_encode($json);
3121
-        exit();
3122
-    }
3123
-
3124
-
3125
-    /**
3126
-     * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3127
-     *
3128
-     * @return void
3129
-     * @throws EE_Error
3130
-     * @throws InvalidArgumentException
3131
-     * @throws InvalidDataTypeException
3132
-     * @throws InvalidInterfaceException
3133
-     */
3134
-    public function return_json()
3135
-    {
3136
-        if ($this->request->isAjax()) {
3137
-            $this->_return_json();
3138
-        } else {
3139
-            throw new EE_Error(
3140
-                sprintf(
3141
-                    esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3142
-                    __FUNCTION__
3143
-                )
3144
-            );
3145
-        }
3146
-    }
3147
-
3148
-
3149
-    /**
3150
-     * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3151
-     * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3152
-     *
3153
-     * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3154
-     * @deprecated  5.0.8.p
3155
-     */
3156
-    public function set_hook_object(EE_Admin_Hooks $hook_obj)
3157
-    {
3158
-        $this->_hook_obj = $hook_obj;
3159
-    }
3160
-
3161
-
3162
-    /**
3163
-     *        generates  HTML wrapper with Tabbed nav for an admin page
3164
-     *
3165
-     * @param bool $about whether to use the special about page wrapper or default.
3166
-     * @return void
3167
-     * @throws DomainException
3168
-     * @throws EE_Error
3169
-     * @throws InvalidArgumentException
3170
-     * @throws InvalidDataTypeException
3171
-     * @throws InvalidInterfaceException
3172
-     */
3173
-    public function admin_page_wrapper(bool $about = false)
3174
-    {
3175
-        $this->_template_args['nav_tabs']         = $this->_get_main_nav_tabs();
3176
-        $this->_template_args['admin_page_title'] = $this->_admin_page_title;
3177
-
3178
-        $this->_template_args['before_admin_page_content'] = apply_filters(
3179
-            "FHEE_before_admin_page_content$this->_current_page$this->_current_view",
3180
-            $this->_template_args['before_admin_page_content'] ?? ''
3181
-        );
3182
-
3183
-        $this->_template_args['after_admin_page_content'] = apply_filters(
3184
-            "FHEE_after_admin_page_content$this->_current_page$this->_current_view",
3185
-            $this->_template_args['after_admin_page_content'] ?? ''
3186
-        );
3187
-        $this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3188
-
3189
-        if ($this->request->isAjax()) {
3190
-            $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3191
-            // $template_path,
3192
-                EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3193
-                $this->_template_args,
3194
-                true
3195
-            );
3196
-            $this->_return_json();
3197
-        }
3198
-        // load settings page wrapper template
3199
-        $template_path = $about
3200
-            ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3201
-            : EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3202
-
3203
-        EEH_Template::display_template($template_path, $this->_template_args);
3204
-    }
3205
-
3206
-
3207
-    /**
3208
-     * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3209
-     *
3210
-     * @return string html
3211
-     * @throws EE_Error
3212
-     */
3213
-    protected function _get_main_nav_tabs(): string
3214
-    {
3215
-        // let's generate the html using the EEH_Tabbed_Content helper.
3216
-        // We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3217
-        // (rather than setting in the page_routes array)
3218
-        return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs, $this->page_slug);
3219
-    }
3220
-
3221
-
3222
-    /**
3223
-     *        sort nav tabs
3224
-     *
3225
-     * @param array $a
3226
-     * @param array $b
3227
-     * @return int
3228
-     */
3229
-    private function _sort_nav_tabs(array $a, array $b): int
3230
-    {
3231
-        if ($a['order'] === $b['order']) {
3232
-            return 0;
3233
-        }
3234
-        return ($a['order'] < $b['order']) ? -1 : 1;
3235
-    }
3236
-
3237
-
3238
-    /**
3239
-     * generates HTML for the forms used on admin pages
3240
-     *
3241
-     * @param array  $input_vars - array of input field details
3242
-     * @param string $generator  indicates which generator to use: options are 'string' or 'array'
3243
-     * @param string $id
3244
-     * @return array|string
3245
-     * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3246
-     * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3247
-     */
3248
-    protected function _generate_admin_form_fields(
3249
-        array $input_vars = [],
3250
-        string $generator = 'string',
3251
-        string $id = ''
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.  Otherwise 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(bool $both = true, array $text = [], array $actions = [], $referrer = null)
3274
-    {
3275
-        $referrer_url  = ! empty($referrer) ? $referrer : $this->request->getServerParam('REQUEST_URI');
3276
-        $button_text   = ! empty($text)
3277
-            ? $text
3278
-            : [
3279
-                esc_html__('Save', 'event_espresso'),
3280
-                esc_html__('Save and Close', 'event_espresso'),
3281
-            ];
3282
-        $default_names = ['save', 'save_and_close'];
3283
-        $buttons       = '';
3284
-        foreach ($button_text as $key => $button) {
3285
-            $ref     = $default_names[ $key ];
3286
-            $name    = ! empty($actions) ? $actions[ $key ] : $ref;
3287
-            $buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3288
-                        . 'value="' . $button . '" name="' . $name . '" '
3289
-                        . 'id="' . $this->_current_view . '_' . $ref . '" />';
3290
-            if (! $both) {
3291
-                break;
3292
-            }
3293
-        }
3294
-        // add in a hidden index for the current page (so save and close redirects properly)
3295
-        $buttons .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3296
-                    . $referrer_url
3297
-                    . '" />';
3298
-
3299
-        $this->_template_args['save_buttons'] = $buttons;
3300
-    }
3301
-
3302
-
3303
-    /**
3304
-     * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3305
-     *
3306
-     * @param string $route
3307
-     * @param array  $additional_hidden_fields
3308
-     * @see   $this->_set_add_edit_form_tags() for details on params
3309
-     * @since 4.6.0
3310
-     */
3311
-    public function set_add_edit_form_tags(string $route = '', array $additional_hidden_fields = [])
3312
-    {
3313
-        $this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3314
-    }
3315
-
3316
-
3317
-    /**
3318
-     * set form open and close tags on add/edit pages.
3319
-     *
3320
-     * @param string $route                    the route you want the form to direct to
3321
-     * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3322
-     * @return void
3323
-     */
3324
-    protected function _set_add_edit_form_tags(string $route = '', array $additional_hidden_fields = [])
3325
-    {
3326
-        if (empty($route)) {
3327
-            $user_msg = esc_html__(
3328
-                'An error occurred. No action was set for this page\'s form.',
3329
-                'event_espresso'
3330
-            );
3331
-            $dev_msg  = $user_msg . "\n"
3332
-                        . sprintf(
3333
-                            esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3334
-                            __FUNCTION__,
3335
-                            __CLASS__
3336
-                        );
3337
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3338
-        }
3339
-        // open form
3340
-        $action                                            = $this->_admin_base_url;
3341
-        $this->_template_args['before_admin_page_content'] = "
2973
+		$this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2974
+		// display message about search results?
2975
+		$search                                    = $this->request->getRequestParam('s');
2976
+		$this->_template_args['before_list_table'] .= ! empty($search)
2977
+			? '<p class="ee-search-results">' . sprintf(
2978
+				esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2979
+				trim($search, '%')
2980
+			) . '</p>'
2981
+			: '';
2982
+		// filter before_list_table template arg
2983
+		$this->_template_args['before_list_table'] = apply_filters(
2984
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2985
+			$this->_template_args['before_list_table'],
2986
+			$this->page_slug,
2987
+			$this->request->requestParams(),
2988
+			$this->_req_action
2989
+		);
2990
+		// convert to array and filter again
2991
+		// arrays are easier to inject new items in a specific location,
2992
+		// but would not be backwards compatible, so we have to add a new filter
2993
+		$this->_template_args['before_list_table'] = implode(
2994
+			" \n",
2995
+			(array) apply_filters(
2996
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
2997
+				(array) $this->_template_args['before_list_table'],
2998
+				$this->page_slug,
2999
+				$this->request->requestParams(),
3000
+				$this->_req_action
3001
+			)
3002
+		);
3003
+		// filter after_list_table template arg
3004
+		$this->_template_args['after_list_table'] = apply_filters(
3005
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3006
+			$this->_template_args['after_list_table'],
3007
+			$this->page_slug,
3008
+			$this->request->requestParams(),
3009
+			$this->_req_action
3010
+		);
3011
+		// convert to array and filter again
3012
+		// arrays are easier to inject new items in a specific location,
3013
+		// but would not be backwards compatible, so we have to add a new filter
3014
+		$this->_template_args['after_list_table']   = implode(
3015
+			" \n",
3016
+			(array) apply_filters(
3017
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3018
+				(array) $this->_template_args['after_list_table'],
3019
+				$this->page_slug,
3020
+				$this->request->requestParams(),
3021
+				$this->_req_action
3022
+			)
3023
+		);
3024
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3025
+			$template_path,
3026
+			$this->_template_args,
3027
+			true
3028
+		);
3029
+		// the final template wrapper
3030
+		if ($sidebar) {
3031
+			$this->display_admin_page_with_sidebar();
3032
+		} else {
3033
+			$this->display_admin_page_with_no_sidebar();
3034
+		}
3035
+	}
3036
+
3037
+
3038
+	/**
3039
+	 * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3040
+	 * html string for the legend.
3041
+	 * $items are expected in an array in the following format:
3042
+	 * $legend_items = array(
3043
+	 *        'item_id' => array(
3044
+	 *            'icon' => 'http://url_to_icon_being_described.png',
3045
+	 *            'desc' => esc_html__('localized description of item');
3046
+	 *        )
3047
+	 * );
3048
+	 *
3049
+	 * @param array $items see above for format of array
3050
+	 * @return string html string of legend
3051
+	 * @throws DomainException
3052
+	 */
3053
+	protected function _display_legend(array $items): string
3054
+	{
3055
+		$this->_template_args['items'] = (array) apply_filters(
3056
+			'FHEE__EE_Admin_Page___display_legend__items',
3057
+			$items,
3058
+			$this
3059
+		);
3060
+		/** @var StatusChangeNotice $status_change_notice */
3061
+		$status_change_notice                         = $this->loader->getShared(
3062
+			'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
3063
+		);
3064
+		$this->_template_args['status_change_notice'] = $status_change_notice->display(
3065
+			'__admin-legend',
3066
+			$this->page_slug
3067
+		);
3068
+		return EEH_Template::display_template(
3069
+			EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3070
+			$this->_template_args,
3071
+			true
3072
+		);
3073
+	}
3074
+
3075
+
3076
+	/**
3077
+	 * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3078
+	 * The returned json object is created from an array in the following format:
3079
+	 * array(
3080
+	 *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3081
+	 *  'success' => FALSE, //(default FALSE) - contains any special success message.
3082
+	 *  'notices' => '', // - contains any EE_Error formatted notices
3083
+	 *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3084
+	 *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3085
+	 *  We're also going to include the template args with every package (so js can pick out any specific template args
3086
+	 *  that might be included in here)
3087
+	 * )
3088
+	 * The json object is populated by whatever is set in the $_template_args property.
3089
+	 *
3090
+	 * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3091
+	 *                                 instead of displayed.
3092
+	 * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3093
+	 * @return void
3094
+	 * @throws EE_Error
3095
+	 * @throws InvalidArgumentException
3096
+	 * @throws InvalidDataTypeException
3097
+	 * @throws InvalidInterfaceException
3098
+	 */
3099
+	protected function _return_json(bool $sticky_notices = false, array $notices_arguments = [])
3100
+	{
3101
+		// make sure any EE_Error notices have been handled.
3102
+		$this->_process_notices($notices_arguments, true, $sticky_notices);
3103
+		$data = $this->_template_args['data'] ?? [];
3104
+		unset($this->_template_args['data']);
3105
+		$json = [
3106
+			'error'     => $this->_template_args['error'] ?? false,
3107
+			'success'   => $this->_template_args['success'] ?? false,
3108
+			'errors'    => $this->_template_args['errors'] ?? false,
3109
+			'attention' => $this->_template_args['attention'] ?? false,
3110
+			'notices'   => EE_Error::get_notices(),
3111
+			'content'   => $this->_template_args['admin_page_content'] ?? '',
3112
+			'data'      => array_merge($data, ['template_args' => $this->_template_args]),
3113
+			'isEEajax'  => true,
3114
+			// special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3115
+		];
3116
+		// make sure there are no php errors or headers_sent.  Then we can set correct json header.
3117
+		if (null === error_get_last() || ! headers_sent()) {
3118
+			header('Content-Type: application/json; charset=UTF-8');
3119
+		}
3120
+		echo wp_json_encode($json);
3121
+		exit();
3122
+	}
3123
+
3124
+
3125
+	/**
3126
+	 * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3127
+	 *
3128
+	 * @return void
3129
+	 * @throws EE_Error
3130
+	 * @throws InvalidArgumentException
3131
+	 * @throws InvalidDataTypeException
3132
+	 * @throws InvalidInterfaceException
3133
+	 */
3134
+	public function return_json()
3135
+	{
3136
+		if ($this->request->isAjax()) {
3137
+			$this->_return_json();
3138
+		} else {
3139
+			throw new EE_Error(
3140
+				sprintf(
3141
+					esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3142
+					__FUNCTION__
3143
+				)
3144
+			);
3145
+		}
3146
+	}
3147
+
3148
+
3149
+	/**
3150
+	 * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3151
+	 * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3152
+	 *
3153
+	 * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3154
+	 * @deprecated  5.0.8.p
3155
+	 */
3156
+	public function set_hook_object(EE_Admin_Hooks $hook_obj)
3157
+	{
3158
+		$this->_hook_obj = $hook_obj;
3159
+	}
3160
+
3161
+
3162
+	/**
3163
+	 *        generates  HTML wrapper with Tabbed nav for an admin page
3164
+	 *
3165
+	 * @param bool $about whether to use the special about page wrapper or default.
3166
+	 * @return void
3167
+	 * @throws DomainException
3168
+	 * @throws EE_Error
3169
+	 * @throws InvalidArgumentException
3170
+	 * @throws InvalidDataTypeException
3171
+	 * @throws InvalidInterfaceException
3172
+	 */
3173
+	public function admin_page_wrapper(bool $about = false)
3174
+	{
3175
+		$this->_template_args['nav_tabs']         = $this->_get_main_nav_tabs();
3176
+		$this->_template_args['admin_page_title'] = $this->_admin_page_title;
3177
+
3178
+		$this->_template_args['before_admin_page_content'] = apply_filters(
3179
+			"FHEE_before_admin_page_content$this->_current_page$this->_current_view",
3180
+			$this->_template_args['before_admin_page_content'] ?? ''
3181
+		);
3182
+
3183
+		$this->_template_args['after_admin_page_content'] = apply_filters(
3184
+			"FHEE_after_admin_page_content$this->_current_page$this->_current_view",
3185
+			$this->_template_args['after_admin_page_content'] ?? ''
3186
+		);
3187
+		$this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3188
+
3189
+		if ($this->request->isAjax()) {
3190
+			$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3191
+			// $template_path,
3192
+				EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3193
+				$this->_template_args,
3194
+				true
3195
+			);
3196
+			$this->_return_json();
3197
+		}
3198
+		// load settings page wrapper template
3199
+		$template_path = $about
3200
+			? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3201
+			: EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3202
+
3203
+		EEH_Template::display_template($template_path, $this->_template_args);
3204
+	}
3205
+
3206
+
3207
+	/**
3208
+	 * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3209
+	 *
3210
+	 * @return string html
3211
+	 * @throws EE_Error
3212
+	 */
3213
+	protected function _get_main_nav_tabs(): string
3214
+	{
3215
+		// let's generate the html using the EEH_Tabbed_Content helper.
3216
+		// We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3217
+		// (rather than setting in the page_routes array)
3218
+		return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs, $this->page_slug);
3219
+	}
3220
+
3221
+
3222
+	/**
3223
+	 *        sort nav tabs
3224
+	 *
3225
+	 * @param array $a
3226
+	 * @param array $b
3227
+	 * @return int
3228
+	 */
3229
+	private function _sort_nav_tabs(array $a, array $b): int
3230
+	{
3231
+		if ($a['order'] === $b['order']) {
3232
+			return 0;
3233
+		}
3234
+		return ($a['order'] < $b['order']) ? -1 : 1;
3235
+	}
3236
+
3237
+
3238
+	/**
3239
+	 * generates HTML for the forms used on admin pages
3240
+	 *
3241
+	 * @param array  $input_vars - array of input field details
3242
+	 * @param string $generator  indicates which generator to use: options are 'string' or 'array'
3243
+	 * @param string $id
3244
+	 * @return array|string
3245
+	 * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3246
+	 * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3247
+	 */
3248
+	protected function _generate_admin_form_fields(
3249
+		array $input_vars = [],
3250
+		string $generator = 'string',
3251
+		string $id = ''
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.  Otherwise 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(bool $both = true, array $text = [], array $actions = [], $referrer = null)
3274
+	{
3275
+		$referrer_url  = ! empty($referrer) ? $referrer : $this->request->getServerParam('REQUEST_URI');
3276
+		$button_text   = ! empty($text)
3277
+			? $text
3278
+			: [
3279
+				esc_html__('Save', 'event_espresso'),
3280
+				esc_html__('Save and Close', 'event_espresso'),
3281
+			];
3282
+		$default_names = ['save', 'save_and_close'];
3283
+		$buttons       = '';
3284
+		foreach ($button_text as $key => $button) {
3285
+			$ref     = $default_names[ $key ];
3286
+			$name    = ! empty($actions) ? $actions[ $key ] : $ref;
3287
+			$buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3288
+						. 'value="' . $button . '" name="' . $name . '" '
3289
+						. 'id="' . $this->_current_view . '_' . $ref . '" />';
3290
+			if (! $both) {
3291
+				break;
3292
+			}
3293
+		}
3294
+		// add in a hidden index for the current page (so save and close redirects properly)
3295
+		$buttons .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3296
+					. $referrer_url
3297
+					. '" />';
3298
+
3299
+		$this->_template_args['save_buttons'] = $buttons;
3300
+	}
3301
+
3302
+
3303
+	/**
3304
+	 * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3305
+	 *
3306
+	 * @param string $route
3307
+	 * @param array  $additional_hidden_fields
3308
+	 * @see   $this->_set_add_edit_form_tags() for details on params
3309
+	 * @since 4.6.0
3310
+	 */
3311
+	public function set_add_edit_form_tags(string $route = '', array $additional_hidden_fields = [])
3312
+	{
3313
+		$this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3314
+	}
3315
+
3316
+
3317
+	/**
3318
+	 * set form open and close tags on add/edit pages.
3319
+	 *
3320
+	 * @param string $route                    the route you want the form to direct to
3321
+	 * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3322
+	 * @return void
3323
+	 */
3324
+	protected function _set_add_edit_form_tags(string $route = '', array $additional_hidden_fields = [])
3325
+	{
3326
+		if (empty($route)) {
3327
+			$user_msg = esc_html__(
3328
+				'An error occurred. No action was set for this page\'s form.',
3329
+				'event_espresso'
3330
+			);
3331
+			$dev_msg  = $user_msg . "\n"
3332
+						. sprintf(
3333
+							esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3334
+							__FUNCTION__,
3335
+							__CLASS__
3336
+						);
3337
+			EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3338
+		}
3339
+		// open form
3340
+		$action                                            = $this->_admin_base_url;
3341
+		$this->_template_args['before_admin_page_content'] = "
3342 3342
             <form name='form' method='post' action='$action' id='{$route}_event_form' class='ee-admin-page-form' >
3343 3343
             ";
3344
-        // add nonce
3345
-        $nonce                                             =
3346
-            wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3347
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3348
-        // add REQUIRED form action
3349
-        $hidden_fields = [
3350
-            'action' => ['type' => 'hidden', 'value' => $route],
3351
-        ];
3352
-        // merge arrays
3353
-        $hidden_fields = is_array($additional_hidden_fields)
3354
-            ? array_merge($hidden_fields, $additional_hidden_fields)
3355
-            : $hidden_fields;
3356
-        // generate form fields
3357
-        $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3358
-        // add fields to form
3359
-        foreach ((array) $form_fields as $form_field) {
3360
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3361
-        }
3362
-        // close form
3363
-        $this->_template_args['after_admin_page_content'] = '</form>';
3364
-    }
3365
-
3366
-
3367
-    /**
3368
-     * Public Wrapper for _redirect_after_action() method since its
3369
-     * discovered it would be useful for external code to have access.
3370
-     *
3371
-     * @param bool|int $success
3372
-     * @param string   $what
3373
-     * @param string   $action_desc
3374
-     * @param array    $query_args
3375
-     * @param bool     $override_overwrite
3376
-     * @throws EE_Error
3377
-     * @see   EE_Admin_Page::_redirect_after_action() for params.
3378
-     * @since 4.5.0
3379
-     */
3380
-    public function redirect_after_action(
3381
-        $success = false,
3382
-        string $what = 'item',
3383
-        string $action_desc = 'processed',
3384
-        array $query_args = [],
3385
-        bool $override_overwrite = false
3386
-    ) {
3387
-        $this->_redirect_after_action(
3388
-            $success,
3389
-            $what,
3390
-            $action_desc,
3391
-            $query_args,
3392
-            $override_overwrite
3393
-        );
3394
-    }
3395
-
3396
-
3397
-    /**
3398
-     * Helper method for merging existing request data with the returned redirect url.
3399
-     * This is typically used for redirects after an action so that if the original view was a filtered view those
3400
-     * filters are still applied.
3401
-     *
3402
-     * @param array $new_route_data
3403
-     * @return array
3404
-     */
3405
-    protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data): array
3406
-    {
3407
-        foreach ($this->request->requestParams() as $ref => $value) {
3408
-            // unset nonces
3409
-            if (strpos($ref, 'nonce') !== false) {
3410
-                $this->request->unSetRequestParam($ref);
3411
-                continue;
3412
-            }
3413
-            // urlencode values.
3414
-            $value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3415
-            $this->request->setRequestParam($ref, $value);
3416
-        }
3417
-        return array_merge($this->request->requestParams(), $new_route_data);
3418
-    }
3419
-
3420
-
3421
-    /**
3422
-     * @param int|float|string $success            - whether success was for two or more records, or just one, or none
3423
-     * @param string           $what               - what the action was performed on
3424
-     * @param string           $action_desc        - what was done ie: updated, deleted, etc
3425
-     * @param array            $query_args         - an array of query_args to be added to the URL to redirect to
3426
-     * @param BOOL             $override_overwrite - by default all EE_Error::success messages are overwritten,
3427
-     *                                             this allows you to override this so that they show.
3428
-     * @return void
3429
-     * @throws EE_Error
3430
-     * @throws InvalidArgumentException
3431
-     * @throws InvalidDataTypeException
3432
-     * @throws InvalidInterfaceException
3433
-     */
3434
-    protected function _redirect_after_action(
3435
-        $success = 0,
3436
-        string $what = 'item',
3437
-        string $action_desc = 'processed',
3438
-        array $query_args = [],
3439
-        bool $override_overwrite = false
3440
-    ) {
3441
-        $notices = EE_Error::get_notices(false);
3442
-        // overwrite default success messages //BUT ONLY if overwrite not overridden
3443
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3444
-            EE_Error::overwrite_success();
3445
-        }
3446
-        if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3447
-            // how many records affected ? more than one record ? or just one ?
3448
-            EE_Error::add_success(
3449
-                sprintf(
3450
-                    esc_html(
3451
-                        _n(
3452
-                            'The "%1$s" has been successfully %2$s.',
3453
-                            'The "%1$s" have been successfully %2$s.',
3454
-                            $success,
3455
-                            'event_espresso'
3456
-                        )
3457
-                    ),
3458
-                    $what,
3459
-                    $action_desc
3460
-                ),
3461
-                __FILE__,
3462
-                __FUNCTION__,
3463
-                __LINE__
3464
-            );
3465
-        }
3466
-        // check that $query_args isn't something crazy
3467
-        $query_args = is_array($query_args) ? $query_args : [];
3468
-        /**
3469
-         * Allow injecting actions before the query_args are modified for possible different
3470
-         * redirections on save and close actions
3471
-         *
3472
-         * @param array $query_args       The original query_args array coming into the
3473
-         *                                method.
3474
-         * @since 4.2.0
3475
-         */
3476
-        do_action(
3477
-            "AHEE__{$this->class_name}___redirect_after_action__before_redirect_modification_$this->_req_action",
3478
-            $query_args
3479
-        );
3480
-        // set redirect url.
3481
-        // Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3482
-        // otherwise we go with whatever is set as the _admin_base_url
3483
-        $redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3484
-        // calculate where we're going (if we have a "save and close" button pushed)
3485
-        if (
3486
-            $this->request->requestParamIsSet('save_and_close')
3487
-            && $this->request->requestParamIsSet('save_and_close_referrer')
3488
-        ) {
3489
-            // even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3490
-            $parsed_url = parse_url($this->request->getRequestParam('save_and_close_referrer', '', DataType::URL));
3491
-            // regenerate query args array from referrer URL
3492
-            parse_str($parsed_url['query'], $query_args);
3493
-            // correct page and action will be in the query args now
3494
-            $redirect_url = admin_url('admin.php');
3495
-        }
3496
-        // merge any default query_args set in _default_route_query_args property
3497
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3498
-            $args_to_merge = [];
3499
-            foreach ($this->_default_route_query_args as $query_param => $query_value) {
3500
-                // is there a wp_referer array in our _default_route_query_args property?
3501
-                if ($query_param === 'wp_referer') {
3502
-                    $query_value = (array) $query_value;
3503
-                    foreach ($query_value as $reference => $value) {
3504
-                        if (strpos($reference, 'nonce') !== false) {
3505
-                            continue;
3506
-                        }
3507
-                        // finally we will override any arguments in the referer with
3508
-                        // what might be set on the _default_route_query_args array.
3509
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3510
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3511
-                        } else {
3512
-                            $args_to_merge[ $reference ] = urlencode($value);
3513
-                        }
3514
-                    }
3515
-                    continue;
3516
-                }
3517
-                $args_to_merge[ $query_param ] = $query_value;
3518
-            }
3519
-            // now let's merge these arguments but override with what was specifically sent in to the
3520
-            // redirect.
3521
-            $query_args = array_merge($args_to_merge, $query_args);
3522
-        }
3523
-        $this->_process_notices($query_args);
3524
-        // generate redirect url
3525
-        // if redirecting to anything other than the main page, add a nonce
3526
-        if (isset($query_args['action'])) {
3527
-            // manually generate wp_nonce and merge that with the query vars
3528
-            // becuz the wp_nonce_url function wrecks havoc on some vars
3529
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3530
-        }
3531
-        // we're adding some hooks and filters in here for processing any things just before redirects
3532
-        // (example: an admin page has done an insert or update and we want to run something after that).
3533
-        do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3534
-        $redirect_url = apply_filters(
3535
-            'FHEE_redirect_' . $this->class_name . $this->_req_action,
3536
-            EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3537
-            $query_args
3538
-        );
3539
-        // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3540
-        if ($this->request->isAjax()) {
3541
-            $default_data                    = [
3542
-                'close'        => true,
3543
-                'redirect_url' => $redirect_url,
3544
-                'where'        => 'main',
3545
-                'what'         => 'append',
3546
-            ];
3547
-            $this->_template_args['success'] = $success;
3548
-            $this->_template_args['data']    = ! empty($this->_template_args['data']) ? array_merge(
3549
-                $default_data,
3550
-                $this->_template_args['data']
3551
-            ) : $default_data;
3552
-            $this->_return_json();
3553
-        }
3554
-        wp_safe_redirect($redirect_url);
3555
-        exit();
3556
-    }
3557
-
3558
-
3559
-    /**
3560
-     * process any notices before redirecting (or returning ajax request)
3561
-     * This method sets the $this->_template_args['notices'] attribute;
3562
-     *
3563
-     * @param array $query_args         any query args that need to be used for notice transient ('action')
3564
-     * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3565
-     *                                  page_routes haven't been defined yet.
3566
-     * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3567
-     *                                  still save a transient for the notice.
3568
-     * @return void
3569
-     * @throws EE_Error
3570
-     * @throws InvalidArgumentException
3571
-     * @throws InvalidDataTypeException
3572
-     * @throws InvalidInterfaceException
3573
-     */
3574
-    protected function _process_notices(
3575
-        array $query_args = [],
3576
-        bool $skip_route_verify = false,
3577
-        bool $sticky_notices = true
3578
-    ) {
3579
-        // first let's set individual error properties if doing_ajax and the properties aren't already set.
3580
-        if ($this->request->isAjax()) {
3581
-            $notices = EE_Error::get_notices(false);
3582
-            if (empty($this->_template_args['success'])) {
3583
-                $this->_template_args['success'] = $notices['success'] ?? false;
3584
-            }
3585
-            if (empty($this->_template_args['errors'])) {
3586
-                $this->_template_args['errors'] = $notices['errors'] ?? false;
3587
-            }
3588
-            if (empty($this->_template_args['attention'])) {
3589
-                $this->_template_args['attention'] = $notices['attention'] ?? false;
3590
-            }
3591
-        }
3592
-        $this->_template_args['notices'] = EE_Error::get_notices();
3593
-        // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3594
-        if (! $this->request->isAjax() || $sticky_notices) {
3595
-            $route = $query_args['action'] ?? 'default';
3596
-            $this->_add_transient(
3597
-                $route,
3598
-                (array) $this->_template_args['notices'],
3599
-                true,
3600
-                $skip_route_verify
3601
-            );
3602
-        }
3603
-    }
3604
-
3605
-
3606
-    /**
3607
-     * get_action_link_or_button
3608
-     * returns the button html for adding, editing, or deleting an item (depending on given type)
3609
-     *
3610
-     * @param string $action        use this to indicate which action the url is generated with.
3611
-     * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3612
-     *                              property.
3613
-     * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3614
-     * @param string $class         Use this to give the class for the button. Defaults to 'button--primary'
3615
-     * @param string $base_url      If this is not provided
3616
-     *                              the _admin_base_url will be used as the default for the button base_url.
3617
-     *                              Otherwise this value will be used.
3618
-     * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3619
-     * @return string
3620
-     * @throws InvalidArgumentException
3621
-     * @throws InvalidInterfaceException
3622
-     * @throws InvalidDataTypeException
3623
-     * @throws EE_Error
3624
-     */
3625
-    public function get_action_link_or_button(
3626
-        string $action,
3627
-        string $type = 'add',
3628
-        array $extra_request = [],
3629
-        string $class = 'button button--primary',
3630
-        string $base_url = '',
3631
-        bool $exclude_nonce = false
3632
-    ): string {
3633
-        // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3634
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3635
-            throw new EE_Error(
3636
-                sprintf(
3637
-                    esc_html__(
3638
-                        'There is no page route for given action for the button.  This action was given: %s',
3639
-                        'event_espresso'
3640
-                    ),
3641
-                    $action
3642
-                )
3643
-            );
3644
-        }
3645
-        if (! isset($this->_labels['buttons'][ $type ])) {
3646
-            throw new EE_Error(
3647
-                sprintf(
3648
-                    esc_html__(
3649
-                        'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3650
-                        'event_espresso'
3651
-                    ),
3652
-                    $type
3653
-                )
3654
-            );
3655
-        }
3656
-        // finally check user access for this button.
3657
-        $has_access = $this->check_user_access($action, true);
3658
-        if (! $has_access) {
3659
-            return '';
3660
-        }
3661
-        $_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
3662
-        $query_args = [
3663
-            'action' => $action,
3664
-        ];
3665
-        // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3666
-        if (! empty($extra_request)) {
3667
-            $query_args = array_merge($extra_request, $query_args);
3668
-        }
3669
-        $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3670
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3671
-    }
3672
-
3673
-
3674
-    /**
3675
-     * _per_page_screen_option
3676
-     * Utility function for adding in a per_page_option in the screen_options_dropdown.
3677
-     *
3678
-     * @return void
3679
-     * @throws InvalidArgumentException
3680
-     * @throws InvalidInterfaceException
3681
-     * @throws InvalidDataTypeException
3682
-     */
3683
-    protected function _per_page_screen_option()
3684
-    {
3685
-        $option = 'per_page';
3686
-        $args   = [
3687
-            'label'   => apply_filters(
3688
-                'FHEE__EE_Admin_Page___per_page_screen_options___label',
3689
-                $this->_admin_page_title,
3690
-                $this
3691
-            ),
3692
-            'default' => (int) apply_filters(
3693
-                'FHEE__EE_Admin_Page___per_page_screen_options__default',
3694
-                20
3695
-            ),
3696
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3697
-        ];
3698
-        // ONLY add the screen option if the user has access to it.
3699
-        if ($this->check_user_access($this->_current_view, true)) {
3700
-            add_screen_option($option, $args);
3701
-        }
3702
-    }
3703
-
3704
-
3705
-    /**
3706
-     * set_per_page_screen_option
3707
-     * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3708
-     * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3709
-     * admin_menu.
3710
-     *
3711
-     * @return void
3712
-     */
3713
-    private function _set_per_page_screen_options()
3714
-    {
3715
-        if ($this->request->requestParamIsSet('wp_screen_options')) {
3716
-            check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3717
-            if (! $user = wp_get_current_user()) {
3718
-                return;
3719
-            }
3720
-            $option = $this->request->getRequestParam('wp_screen_options[option]', '', DataType::KEY);
3721
-            if (! $option) {
3722
-                return;
3723
-            }
3724
-            $value      = $this->request->getRequestParam('wp_screen_options[value]', 0, DataType::INT);
3725
-            $map_option = $option;
3726
-            $option     = str_replace('-', '_', $option);
3727
-            switch ($map_option) {
3728
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3729
-                    $max_value = apply_filters(
3730
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3731
-                        999,
3732
-                        $this->_current_page,
3733
-                        $this->_current_view
3734
-                    );
3735
-                    if ($value < 1) {
3736
-                        return;
3737
-                    }
3738
-                    $value = min($value, $max_value);
3739
-                    break;
3740
-                default:
3741
-                    $value = apply_filters(
3742
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3743
-                        false,
3744
-                        $option,
3745
-                        $value
3746
-                    );
3747
-                    if (false === $value) {
3748
-                        return;
3749
-                    }
3750
-                    break;
3751
-            }
3752
-            update_user_meta($user->ID, $option, $value);
3753
-            wp_safe_redirect(remove_query_arg(['pagenum', 'apage', 'paged'], wp_get_referer()));
3754
-            exit;
3755
-        }
3756
-    }
3757
-
3758
-
3759
-    /**
3760
-     * This just allows for setting the $_template_args property if it needs to be set outside the object
3761
-     *
3762
-     * @param array $data array that will be assigned to template args.
3763
-     */
3764
-    public function set_template_args(array $data)
3765
-    {
3766
-        $this->_template_args = array_merge($this->_template_args, $data);
3767
-    }
3768
-
3769
-
3770
-    public function setAdminPageTitle(string $title)
3771
-    {
3772
-        $this->_admin_page_title = sanitize_text_field($title);
3773
-    }
3774
-
3775
-
3776
-    /**
3777
-     * This makes available the WP transient system for temporarily moving data between routes
3778
-     *
3779
-     * @param string $route             the route that should receive the transient
3780
-     * @param array  $data              the data that gets sent
3781
-     * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise it's just a
3782
-     *                                  normal route transient.
3783
-     * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3784
-     *                                  when we are adding a transient before page_routes have been defined.
3785
-     * @return void
3786
-     * @throws EE_Error
3787
-     */
3788
-    protected function _add_transient(
3789
-        string $route,
3790
-        array $data,
3791
-        bool $notices = false,
3792
-        bool $skip_route_verify = false
3793
-    ) {
3794
-        $user_id = get_current_user_id();
3795
-        if (! $skip_route_verify) {
3796
-            $this->_verify_route($route);
3797
-        }
3798
-        // now let's set the string for what kind of transient we're setting
3799
-        $transient = $notices ? "ee_rte_n_tx_{$route}_$user_id" : "rte_tx_{$route}_$user_id";
3800
-        $data      = $notices ? ['notices' => $data] : $data;
3801
-        // is there already a transient for this route?  If there is then let's ADD to that transient
3802
-        $existing = is_multisite() && is_network_admin()
3803
-            ? get_site_transient($transient)
3804
-            : get_transient($transient);
3805
-        if ($existing) {
3806
-            $data = array_merge($data, (array) $existing);
3807
-        }
3808
-        if (is_multisite() && is_network_admin()) {
3809
-            set_site_transient($transient, $data, 8);
3810
-        } else {
3811
-            set_transient($transient, $data, 8);
3812
-        }
3813
-    }
3814
-
3815
-
3816
-    /**
3817
-     * this retrieves the temporary transient that has been set for moving data between routes.
3818
-     *
3819
-     * @param bool   $notices true we get notices transient. False we just return normal route transient
3820
-     * @param string $route
3821
-     * @return mixed data
3822
-     */
3823
-    protected function _get_transient(bool $notices = false, string $route = '')
3824
-    {
3825
-        $user_id   = get_current_user_id();
3826
-        $route     = ! $route ? $this->_req_action : $route;
3827
-        $transient = $notices
3828
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3829
-            : 'rte_tx_' . $route . '_' . $user_id;
3830
-        $data      = is_multisite() && is_network_admin()
3831
-            ? get_site_transient($transient)
3832
-            : get_transient($transient);
3833
-        // delete transient after retrieval (just in case it hasn't expired);
3834
-        if (is_multisite() && is_network_admin()) {
3835
-            delete_site_transient($transient);
3836
-        } else {
3837
-            delete_transient($transient);
3838
-        }
3839
-        return $notices && isset($data['notices']) ? $data['notices'] : $data;
3840
-    }
3841
-
3842
-
3843
-    /**
3844
-     * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3845
-     * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3846
-     * default route callback on the EE_Admin page you want it run.)
3847
-     *
3848
-     * @return void
3849
-     */
3850
-    protected function _transient_garbage_collection()
3851
-    {
3852
-        global $wpdb;
3853
-        // retrieve all existing transients
3854
-        $query =
3855
-            "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3856
-        if ($results = $wpdb->get_results($query)) {
3857
-            foreach ($results as $result) {
3858
-                $transient = str_replace('_transient_', '', $result->option_name);
3859
-                get_transient($transient);
3860
-                if (is_multisite() && is_network_admin()) {
3861
-                    get_site_transient($transient);
3862
-                }
3863
-            }
3864
-        }
3865
-    }
3866
-
3867
-
3868
-    /**
3869
-     * get_view
3870
-     *
3871
-     * @return string content of _view property
3872
-     */
3873
-    public function get_view(): string
3874
-    {
3875
-        return $this->_view;
3876
-    }
3877
-
3878
-
3879
-    /**
3880
-     * getter for the protected $_views property
3881
-     *
3882
-     * @return array
3883
-     */
3884
-    public function get_views(): array
3885
-    {
3886
-        return $this->_views;
3887
-    }
3888
-
3889
-
3890
-    /**
3891
-     * @param array $views
3892
-     * @return void
3893
-     * @since 5.0.13.p
3894
-     */
3895
-    public function updateViews(array $views)
3896
-    {
3897
-        $this->_views = array_merge($this->_views, $views);
3898
-    }
3899
-
3900
-
3901
-    /**
3902
-    /**
3903
-     * get_current_page
3904
-     *
3905
-     * @return string _current_page property value
3906
-     */
3907
-    public function get_current_page(): string
3908
-    {
3909
-        return $this->_current_page;
3910
-    }
3911
-
3912
-
3913
-    /**
3914
-     * get_current_view
3915
-     *
3916
-     * @return string _current_view property value
3917
-     */
3918
-    public function get_current_view(): string
3919
-    {
3920
-        return $this->_current_view;
3921
-    }
3922
-
3923
-
3924
-    /**
3925
-     * get_current_screen
3926
-     *
3927
-     * @return object The current WP_Screen object
3928
-     */
3929
-    public function get_current_screen()
3930
-    {
3931
-        return $this->_current_screen;
3932
-    }
3933
-
3934
-
3935
-    /**
3936
-     * get_current_page_view_url
3937
-     *
3938
-     * @return string This returns the url for the current_page_view.
3939
-     */
3940
-    public function get_current_page_view_url(): string
3941
-    {
3942
-        return $this->_current_page_view_url;
3943
-    }
3944
-
3945
-
3946
-    /**
3947
-     * just returns the Request
3948
-     *
3949
-     * @return RequestInterface
3950
-     */
3951
-    public function get_request(): ?RequestInterface
3952
-    {
3953
-        return $this->request;
3954
-    }
3955
-
3956
-
3957
-    /**
3958
-     * just returns the _req_data property
3959
-     *
3960
-     * @return array
3961
-     */
3962
-    public function get_request_data(): array
3963
-    {
3964
-        return $this->request->requestParams();
3965
-    }
3966
-
3967
-
3968
-    /**
3969
-     * returns the _req_data protected property
3970
-     *
3971
-     * @return string
3972
-     */
3973
-    public function get_req_action(): string
3974
-    {
3975
-        return $this->_req_action;
3976
-    }
3977
-
3978
-
3979
-    /**
3980
-     * @return bool  value of $_is_caf property
3981
-     */
3982
-    public function is_caf(): bool
3983
-    {
3984
-        return $this->_is_caf;
3985
-    }
3986
-
3987
-
3988
-    /**
3989
-     * @return array
3990
-     */
3991
-    public function default_espresso_metaboxes(): array
3992
-    {
3993
-        return $this->_default_espresso_metaboxes;
3994
-    }
3995
-
3996
-
3997
-    /**
3998
-     * @return string
3999
-     */
4000
-    public function admin_base_url(): string
4001
-    {
4002
-        return $this->_admin_base_url;
4003
-    }
4004
-
4005
-
4006
-    /**
4007
-     * @return string
4008
-     */
4009
-    public function wp_page_slug(): string
4010
-    {
4011
-        return $this->_wp_page_slug;
4012
-    }
4013
-
4014
-
4015
-    /**
4016
-     * updates  espresso configuration settings
4017
-     *
4018
-     * @param string                   $tab
4019
-     * @param EE_Config_Base|EE_Config $config
4020
-     * @param string                   $file file where error occurred
4021
-     * @param string                   $func function  where error occurred
4022
-     * @param string                   $line line no where error occurred
4023
-     * @return bool
4024
-     * @throws EE_Error
4025
-     * @throws ReflectionException
4026
-     */
4027
-    protected function _update_espresso_configuration(
4028
-        string $tab,
4029
-        $config,
4030
-        string $file = '',
4031
-        string $func = '',
4032
-        string $line = ''
4033
-    ): bool {
4034
-        // remove any options that are NOT going to be saved with the config settings.
4035
-        if (isset($config->core->ee_ueip_optin)) {
4036
-            // TODO: remove the following two lines and make sure values are migrated from 3.1
4037
-            update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4038
-            update_option('ee_ueip_has_notified', true);
4039
-        }
4040
-        // and save it (note we're also doing the network save here)
4041
-        $net_saved    = ! is_main_site() || EE_Network_Config::instance()->update_config(false, false);
4042
-        $config_saved = EE_Config::instance()->update_espresso_config(false, false);
4043
-        if ($config_saved && $net_saved) {
4044
-            EE_Error::add_success(sprintf(esc_html__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4045
-            return true;
4046
-        }
4047
-        EE_Error::add_error(
4048
-            sprintf(esc_html__('The "%s" were not updated.', 'event_espresso'), $tab),
4049
-            $file,
4050
-            $func,
4051
-            $line
4052
-        );
4053
-        return false;
4054
-    }
4055
-
4056
-
4057
-    /**
4058
-     * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4059
-     *
4060
-     * @return array
4061
-     */
4062
-    public function get_yes_no_values(): array
4063
-    {
4064
-        return $this->_yes_no_values;
4065
-    }
4066
-
4067
-
4068
-    /**
4069
-     * @return string
4070
-     * @throws ReflectionException
4071
-     * @since 5.0.0.p
4072
-     */
4073
-    protected function _get_dir(): string
4074
-    {
4075
-        $reflector = new ReflectionClass($this->class_name);
4076
-        return dirname($reflector->getFileName());
4077
-    }
4078
-
4079
-
4080
-    /**
4081
-     * A helper for getting a "next link".
4082
-     *
4083
-     * @param string $url   The url to link to
4084
-     * @param string $class The class to use.
4085
-     * @return string
4086
-     */
4087
-    protected function _next_link(string $url, string $class = 'dashicons dashicons-arrow-right'): string
4088
-    {
4089
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4090
-    }
4091
-
4092
-
4093
-    /**
4094
-     * A helper for getting a "previous link".
4095
-     *
4096
-     * @param string $url   The url to link to
4097
-     * @param string $class The class to use.
4098
-     * @return string
4099
-     */
4100
-    protected function _previous_link(string $url, string $class = 'dashicons dashicons-arrow-left'): string
4101
-    {
4102
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4103
-    }
4104
-
4105
-
4106
-
4107
-
4108
-
4109
-
4110
-
4111
-    // below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4112
-
4113
-
4114
-    /**
4115
-     * This processes a request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4116
-     * 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
4117
-     * _req_data array.
4118
-     *
4119
-     * @return bool success/fail
4120
-     * @throws EE_Error
4121
-     * @throws InvalidArgumentException
4122
-     * @throws ReflectionException
4123
-     * @throws InvalidDataTypeException
4124
-     * @throws InvalidInterfaceException
4125
-     */
4126
-    protected function _process_resend_registration(): bool
4127
-    {
4128
-        $this->_template_args['success'] = EED_Messages::process_resend($this->request->requestParams());
4129
-        do_action(
4130
-            'AHEE__EE_Admin_Page___process_resend_registration',
4131
-            $this->_template_args['success'],
4132
-            $this->request->requestParams()
4133
-        );
4134
-        return $this->_template_args['success'];
4135
-    }
4136
-
4137
-
4138
-    /**
4139
-     * This automatically processes any payment message notifications when manual payment has been applied.
4140
-     *
4141
-     * @param EE_Payment $payment
4142
-     * @return bool success/fail
4143
-     */
4144
-    protected function _process_payment_notification(EE_Payment $payment): bool
4145
-    {
4146
-        add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4147
-        do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4148
-        $this->_template_args['success'] = apply_filters(
4149
-            'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4150
-            false,
4151
-            $payment
4152
-        );
4153
-        return $this->_template_args['success'];
4154
-    }
4155
-
4156
-
4157
-    /**
4158
-     * @param EEM_Base      $entity_model
4159
-     * @param string        $entity_PK_name name of the primary key field used as a request param, ie: id, ID, etc
4160
-     * @param string        $action         one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4161
-     * @param string        $delete_column  name of the field that denotes whether entity is trashed
4162
-     * @param callable|null $callback       called after entity is trashed, restored, or deleted
4163
-     * @return int|float
4164
-     * @throws EE_Error
4165
-     */
4166
-    protected function trashRestoreDeleteEntities(
4167
-        EEM_Base $entity_model,
4168
-        string $entity_PK_name,
4169
-        string $action = EE_Admin_List_Table::ACTION_DELETE,
4170
-        string $delete_column = '',
4171
-        ?callable $callback = null
4172
-    ) {
4173
-        $entity_PK      = $entity_model->get_primary_key_field();
4174
-        $entity_PK_name = $entity_PK_name ?: $entity_PK->get_name();
4175
-        $entity_PK_type = $this->resolveEntityFieldDataType($entity_PK);
4176
-        // grab ID if deleting a single entity
4177
-        if ($this->request->requestParamIsSet($entity_PK_name)) {
4178
-            $ID = $this->request->getRequestParam($entity_PK_name, 0, $entity_PK_type);
4179
-            return $this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback) ? 1 : 0;
4180
-        }
4181
-        // or grab checkbox array if bulk deleting
4182
-        $checkboxes = $this->request->getRequestParam('checkbox', [], $entity_PK_type, true);
4183
-        if (empty($checkboxes)) {
4184
-            return 0;
4185
-        }
4186
-        $success = 0;
4187
-        $IDs     = array_keys($checkboxes);
4188
-        // cycle thru bulk action checkboxes
4189
-        foreach ($IDs as $ID) {
4190
-            // increment $success
4191
-            if ($this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback)) {
4192
-                $success++;
4193
-            }
4194
-        }
4195
-        $count = (int) count($checkboxes);
4196
-        // if multiple entities were deleted successfully, then $deleted will be full count of deletions,
4197
-        // otherwise it will be a fraction of ( actual deletions / total entities to be deleted )
4198
-        return $success === $count ? $count : $success / $count;
4199
-    }
4200
-
4201
-
4202
-    /**
4203
-     * @param EE_Primary_Key_Field_Base $entity_PK
4204
-     * @return string
4205
-     * @throws EE_Error
4206
-     * @since   4.10.30.p
4207
-     */
4208
-    private function resolveEntityFieldDataType(EE_Primary_Key_Field_Base $entity_PK): string
4209
-    {
4210
-        $entity_PK_type = $entity_PK->getSchemaType();
4211
-        switch ($entity_PK_type) {
4212
-            case 'boolean':
4213
-                return DataType::BOOL;
4214
-            case 'integer':
4215
-                return DataType::INT;
4216
-            case 'number':
4217
-                return DataType::FLOAT;
4218
-            case 'string':
4219
-                return DataType::STRING;
4220
-        }
4221
-        throw new RuntimeException(
4222
-            sprintf(
4223
-                esc_html__(
4224
-                    '"%1$s" is an invalid schema type for the %2$s primary key.',
4225
-                    'event_espresso'
4226
-                ),
4227
-                $entity_PK_type,
4228
-                $entity_PK->get_name()
4229
-            )
4230
-        );
4231
-    }
4232
-
4233
-
4234
-    /**
4235
-     * @param EEM_Base      $entity_model
4236
-     * @param int|string    $entity_ID
4237
-     * @param string        $action        one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4238
-     * @param string        $delete_column name of the field that denotes whether entity is trashed
4239
-     * @param callable|null $callback      called after entity is trashed, restored, or deleted
4240
-     * @return bool
4241
-     */
4242
-    protected function trashRestoreDeleteEntity(
4243
-        EEM_Base $entity_model,
4244
-        $entity_ID,
4245
-        string $action,
4246
-        string $delete_column,
4247
-        ?callable $callback = null
4248
-    ): bool {
4249
-        $entity_ID = absint($entity_ID);
4250
-        if (! $entity_ID) {
4251
-            $this->trashRestoreDeleteError($action, $entity_model);
4252
-        }
4253
-        $result = 0;
4254
-        try {
4255
-            $entity = $entity_model->get_one_by_ID($entity_ID);
4256
-            if (! $entity instanceof EE_Base_Class) {
4257
-                throw new DomainException(
4258
-                    sprintf(
4259
-                        esc_html__(
4260
-                            'Missing or invalid %1$s entity with ID of "%2$s" returned from db.',
4261
-                            'event_espresso'
4262
-                        ),
4263
-                        str_replace('EEM_', '', $entity_model->get_this_model_name()),
4264
-                        $entity_ID
4265
-                    )
4266
-                );
4267
-            }
4268
-            switch ($action) {
4269
-                case EE_Admin_List_Table::ACTION_DELETE:
4270
-                    $result = (bool) $entity->delete_permanently();
4271
-                    break;
4272
-                case EE_Admin_List_Table::ACTION_RESTORE:
4273
-                    $result = $entity->delete_or_restore(false);
4274
-                    break;
4275
-                case EE_Admin_List_Table::ACTION_TRASH:
4276
-                    $result = $entity->delete_or_restore();
4277
-                    break;
4278
-            }
4279
-        } catch (Exception $exception) {
4280
-            $this->trashRestoreDeleteError($action, $entity_model, $exception);
4281
-        }
4282
-        if (is_callable($callback)) {
4283
-            call_user_func_array($callback, [$entity_model, $entity_ID, $action, $result, $delete_column]);
4284
-        }
4285
-        return $result;
4286
-    }
4287
-
4288
-
4289
-    /**
4290
-     * @param EEM_Base $entity_model
4291
-     * @param string   $delete_column
4292
-     * @since 4.10.30.p
4293
-     */
4294
-    private function validateDeleteColumn(EEM_Base $entity_model, string $delete_column)
4295
-    {
4296
-        if (empty($delete_column)) {
4297
-            throw new DomainException(
4298
-                sprintf(
4299
-                    esc_html__(
4300
-                        'You need to specify the name of the "delete column" on the %2$s model, in order to trash or restore an entity.',
4301
-                        'event_espresso'
4302
-                    ),
4303
-                    $entity_model->get_this_model_name()
4304
-                )
4305
-            );
4306
-        }
4307
-        if (! $entity_model->has_field($delete_column)) {
4308
-            throw new DomainException(
4309
-                sprintf(
4310
-                    esc_html__(
4311
-                        'The %1$s field does not exist on the %2$s model.',
4312
-                        'event_espresso'
4313
-                    ),
4314
-                    $delete_column,
4315
-                    $entity_model->get_this_model_name()
4316
-                )
4317
-            );
4318
-        }
4319
-    }
4320
-
4321
-
4322
-    /**
4323
-     * @param EEM_Base       $entity_model
4324
-     * @param Exception|null $exception
4325
-     * @param string         $action
4326
-     * @since 4.10.30.p
4327
-     */
4328
-    private function trashRestoreDeleteError(string $action, EEM_Base $entity_model, Exception $exception = null)
4329
-    {
4330
-        if ($exception instanceof Exception) {
4331
-            throw new RuntimeException(
4332
-                sprintf(
4333
-                    esc_html__(
4334
-                        'Could not %1$s the %2$s because the following error occurred: %3$s',
4335
-                        'event_espresso'
4336
-                    ),
4337
-                    $action,
4338
-                    $entity_model->get_this_model_name(),
4339
-                    $exception->getMessage()
4340
-                )
4341
-            );
4342
-        }
4343
-        throw new RuntimeException(
4344
-            sprintf(
4345
-                esc_html__(
4346
-                    'Could not %1$s the %2$s because an invalid ID was received.',
4347
-                    'event_espresso'
4348
-                ),
4349
-                $action,
4350
-                $entity_model->get_this_model_name()
4351
-            )
4352
-        );
4353
-    }
3344
+		// add nonce
3345
+		$nonce                                             =
3346
+			wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3347
+		$this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3348
+		// add REQUIRED form action
3349
+		$hidden_fields = [
3350
+			'action' => ['type' => 'hidden', 'value' => $route],
3351
+		];
3352
+		// merge arrays
3353
+		$hidden_fields = is_array($additional_hidden_fields)
3354
+			? array_merge($hidden_fields, $additional_hidden_fields)
3355
+			: $hidden_fields;
3356
+		// generate form fields
3357
+		$form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3358
+		// add fields to form
3359
+		foreach ((array) $form_fields as $form_field) {
3360
+			$this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3361
+		}
3362
+		// close form
3363
+		$this->_template_args['after_admin_page_content'] = '</form>';
3364
+	}
3365
+
3366
+
3367
+	/**
3368
+	 * Public Wrapper for _redirect_after_action() method since its
3369
+	 * discovered it would be useful for external code to have access.
3370
+	 *
3371
+	 * @param bool|int $success
3372
+	 * @param string   $what
3373
+	 * @param string   $action_desc
3374
+	 * @param array    $query_args
3375
+	 * @param bool     $override_overwrite
3376
+	 * @throws EE_Error
3377
+	 * @see   EE_Admin_Page::_redirect_after_action() for params.
3378
+	 * @since 4.5.0
3379
+	 */
3380
+	public function redirect_after_action(
3381
+		$success = false,
3382
+		string $what = 'item',
3383
+		string $action_desc = 'processed',
3384
+		array $query_args = [],
3385
+		bool $override_overwrite = false
3386
+	) {
3387
+		$this->_redirect_after_action(
3388
+			$success,
3389
+			$what,
3390
+			$action_desc,
3391
+			$query_args,
3392
+			$override_overwrite
3393
+		);
3394
+	}
3395
+
3396
+
3397
+	/**
3398
+	 * Helper method for merging existing request data with the returned redirect url.
3399
+	 * This is typically used for redirects after an action so that if the original view was a filtered view those
3400
+	 * filters are still applied.
3401
+	 *
3402
+	 * @param array $new_route_data
3403
+	 * @return array
3404
+	 */
3405
+	protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data): array
3406
+	{
3407
+		foreach ($this->request->requestParams() as $ref => $value) {
3408
+			// unset nonces
3409
+			if (strpos($ref, 'nonce') !== false) {
3410
+				$this->request->unSetRequestParam($ref);
3411
+				continue;
3412
+			}
3413
+			// urlencode values.
3414
+			$value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3415
+			$this->request->setRequestParam($ref, $value);
3416
+		}
3417
+		return array_merge($this->request->requestParams(), $new_route_data);
3418
+	}
3419
+
3420
+
3421
+	/**
3422
+	 * @param int|float|string $success            - whether success was for two or more records, or just one, or none
3423
+	 * @param string           $what               - what the action was performed on
3424
+	 * @param string           $action_desc        - what was done ie: updated, deleted, etc
3425
+	 * @param array            $query_args         - an array of query_args to be added to the URL to redirect to
3426
+	 * @param BOOL             $override_overwrite - by default all EE_Error::success messages are overwritten,
3427
+	 *                                             this allows you to override this so that they show.
3428
+	 * @return void
3429
+	 * @throws EE_Error
3430
+	 * @throws InvalidArgumentException
3431
+	 * @throws InvalidDataTypeException
3432
+	 * @throws InvalidInterfaceException
3433
+	 */
3434
+	protected function _redirect_after_action(
3435
+		$success = 0,
3436
+		string $what = 'item',
3437
+		string $action_desc = 'processed',
3438
+		array $query_args = [],
3439
+		bool $override_overwrite = false
3440
+	) {
3441
+		$notices = EE_Error::get_notices(false);
3442
+		// overwrite default success messages //BUT ONLY if overwrite not overridden
3443
+		if (! $override_overwrite || ! empty($notices['errors'])) {
3444
+			EE_Error::overwrite_success();
3445
+		}
3446
+		if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3447
+			// how many records affected ? more than one record ? or just one ?
3448
+			EE_Error::add_success(
3449
+				sprintf(
3450
+					esc_html(
3451
+						_n(
3452
+							'The "%1$s" has been successfully %2$s.',
3453
+							'The "%1$s" have been successfully %2$s.',
3454
+							$success,
3455
+							'event_espresso'
3456
+						)
3457
+					),
3458
+					$what,
3459
+					$action_desc
3460
+				),
3461
+				__FILE__,
3462
+				__FUNCTION__,
3463
+				__LINE__
3464
+			);
3465
+		}
3466
+		// check that $query_args isn't something crazy
3467
+		$query_args = is_array($query_args) ? $query_args : [];
3468
+		/**
3469
+		 * Allow injecting actions before the query_args are modified for possible different
3470
+		 * redirections on save and close actions
3471
+		 *
3472
+		 * @param array $query_args       The original query_args array coming into the
3473
+		 *                                method.
3474
+		 * @since 4.2.0
3475
+		 */
3476
+		do_action(
3477
+			"AHEE__{$this->class_name}___redirect_after_action__before_redirect_modification_$this->_req_action",
3478
+			$query_args
3479
+		);
3480
+		// set redirect url.
3481
+		// Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3482
+		// otherwise we go with whatever is set as the _admin_base_url
3483
+		$redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3484
+		// calculate where we're going (if we have a "save and close" button pushed)
3485
+		if (
3486
+			$this->request->requestParamIsSet('save_and_close')
3487
+			&& $this->request->requestParamIsSet('save_and_close_referrer')
3488
+		) {
3489
+			// even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3490
+			$parsed_url = parse_url($this->request->getRequestParam('save_and_close_referrer', '', DataType::URL));
3491
+			// regenerate query args array from referrer URL
3492
+			parse_str($parsed_url['query'], $query_args);
3493
+			// correct page and action will be in the query args now
3494
+			$redirect_url = admin_url('admin.php');
3495
+		}
3496
+		// merge any default query_args set in _default_route_query_args property
3497
+		if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3498
+			$args_to_merge = [];
3499
+			foreach ($this->_default_route_query_args as $query_param => $query_value) {
3500
+				// is there a wp_referer array in our _default_route_query_args property?
3501
+				if ($query_param === 'wp_referer') {
3502
+					$query_value = (array) $query_value;
3503
+					foreach ($query_value as $reference => $value) {
3504
+						if (strpos($reference, 'nonce') !== false) {
3505
+							continue;
3506
+						}
3507
+						// finally we will override any arguments in the referer with
3508
+						// what might be set on the _default_route_query_args array.
3509
+						if (isset($this->_default_route_query_args[ $reference ])) {
3510
+							$args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3511
+						} else {
3512
+							$args_to_merge[ $reference ] = urlencode($value);
3513
+						}
3514
+					}
3515
+					continue;
3516
+				}
3517
+				$args_to_merge[ $query_param ] = $query_value;
3518
+			}
3519
+			// now let's merge these arguments but override with what was specifically sent in to the
3520
+			// redirect.
3521
+			$query_args = array_merge($args_to_merge, $query_args);
3522
+		}
3523
+		$this->_process_notices($query_args);
3524
+		// generate redirect url
3525
+		// if redirecting to anything other than the main page, add a nonce
3526
+		if (isset($query_args['action'])) {
3527
+			// manually generate wp_nonce and merge that with the query vars
3528
+			// becuz the wp_nonce_url function wrecks havoc on some vars
3529
+			$query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3530
+		}
3531
+		// we're adding some hooks and filters in here for processing any things just before redirects
3532
+		// (example: an admin page has done an insert or update and we want to run something after that).
3533
+		do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3534
+		$redirect_url = apply_filters(
3535
+			'FHEE_redirect_' . $this->class_name . $this->_req_action,
3536
+			EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3537
+			$query_args
3538
+		);
3539
+		// check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3540
+		if ($this->request->isAjax()) {
3541
+			$default_data                    = [
3542
+				'close'        => true,
3543
+				'redirect_url' => $redirect_url,
3544
+				'where'        => 'main',
3545
+				'what'         => 'append',
3546
+			];
3547
+			$this->_template_args['success'] = $success;
3548
+			$this->_template_args['data']    = ! empty($this->_template_args['data']) ? array_merge(
3549
+				$default_data,
3550
+				$this->_template_args['data']
3551
+			) : $default_data;
3552
+			$this->_return_json();
3553
+		}
3554
+		wp_safe_redirect($redirect_url);
3555
+		exit();
3556
+	}
3557
+
3558
+
3559
+	/**
3560
+	 * process any notices before redirecting (or returning ajax request)
3561
+	 * This method sets the $this->_template_args['notices'] attribute;
3562
+	 *
3563
+	 * @param array $query_args         any query args that need to be used for notice transient ('action')
3564
+	 * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3565
+	 *                                  page_routes haven't been defined yet.
3566
+	 * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3567
+	 *                                  still save a transient for the notice.
3568
+	 * @return void
3569
+	 * @throws EE_Error
3570
+	 * @throws InvalidArgumentException
3571
+	 * @throws InvalidDataTypeException
3572
+	 * @throws InvalidInterfaceException
3573
+	 */
3574
+	protected function _process_notices(
3575
+		array $query_args = [],
3576
+		bool $skip_route_verify = false,
3577
+		bool $sticky_notices = true
3578
+	) {
3579
+		// first let's set individual error properties if doing_ajax and the properties aren't already set.
3580
+		if ($this->request->isAjax()) {
3581
+			$notices = EE_Error::get_notices(false);
3582
+			if (empty($this->_template_args['success'])) {
3583
+				$this->_template_args['success'] = $notices['success'] ?? false;
3584
+			}
3585
+			if (empty($this->_template_args['errors'])) {
3586
+				$this->_template_args['errors'] = $notices['errors'] ?? false;
3587
+			}
3588
+			if (empty($this->_template_args['attention'])) {
3589
+				$this->_template_args['attention'] = $notices['attention'] ?? false;
3590
+			}
3591
+		}
3592
+		$this->_template_args['notices'] = EE_Error::get_notices();
3593
+		// IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3594
+		if (! $this->request->isAjax() || $sticky_notices) {
3595
+			$route = $query_args['action'] ?? 'default';
3596
+			$this->_add_transient(
3597
+				$route,
3598
+				(array) $this->_template_args['notices'],
3599
+				true,
3600
+				$skip_route_verify
3601
+			);
3602
+		}
3603
+	}
3604
+
3605
+
3606
+	/**
3607
+	 * get_action_link_or_button
3608
+	 * returns the button html for adding, editing, or deleting an item (depending on given type)
3609
+	 *
3610
+	 * @param string $action        use this to indicate which action the url is generated with.
3611
+	 * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3612
+	 *                              property.
3613
+	 * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3614
+	 * @param string $class         Use this to give the class for the button. Defaults to 'button--primary'
3615
+	 * @param string $base_url      If this is not provided
3616
+	 *                              the _admin_base_url will be used as the default for the button base_url.
3617
+	 *                              Otherwise this value will be used.
3618
+	 * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3619
+	 * @return string
3620
+	 * @throws InvalidArgumentException
3621
+	 * @throws InvalidInterfaceException
3622
+	 * @throws InvalidDataTypeException
3623
+	 * @throws EE_Error
3624
+	 */
3625
+	public function get_action_link_or_button(
3626
+		string $action,
3627
+		string $type = 'add',
3628
+		array $extra_request = [],
3629
+		string $class = 'button button--primary',
3630
+		string $base_url = '',
3631
+		bool $exclude_nonce = false
3632
+	): string {
3633
+		// first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3634
+		if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3635
+			throw new EE_Error(
3636
+				sprintf(
3637
+					esc_html__(
3638
+						'There is no page route for given action for the button.  This action was given: %s',
3639
+						'event_espresso'
3640
+					),
3641
+					$action
3642
+				)
3643
+			);
3644
+		}
3645
+		if (! isset($this->_labels['buttons'][ $type ])) {
3646
+			throw new EE_Error(
3647
+				sprintf(
3648
+					esc_html__(
3649
+						'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3650
+						'event_espresso'
3651
+					),
3652
+					$type
3653
+				)
3654
+			);
3655
+		}
3656
+		// finally check user access for this button.
3657
+		$has_access = $this->check_user_access($action, true);
3658
+		if (! $has_access) {
3659
+			return '';
3660
+		}
3661
+		$_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
3662
+		$query_args = [
3663
+			'action' => $action,
3664
+		];
3665
+		// merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3666
+		if (! empty($extra_request)) {
3667
+			$query_args = array_merge($extra_request, $query_args);
3668
+		}
3669
+		$url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3670
+		return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3671
+	}
3672
+
3673
+
3674
+	/**
3675
+	 * _per_page_screen_option
3676
+	 * Utility function for adding in a per_page_option in the screen_options_dropdown.
3677
+	 *
3678
+	 * @return void
3679
+	 * @throws InvalidArgumentException
3680
+	 * @throws InvalidInterfaceException
3681
+	 * @throws InvalidDataTypeException
3682
+	 */
3683
+	protected function _per_page_screen_option()
3684
+	{
3685
+		$option = 'per_page';
3686
+		$args   = [
3687
+			'label'   => apply_filters(
3688
+				'FHEE__EE_Admin_Page___per_page_screen_options___label',
3689
+				$this->_admin_page_title,
3690
+				$this
3691
+			),
3692
+			'default' => (int) apply_filters(
3693
+				'FHEE__EE_Admin_Page___per_page_screen_options__default',
3694
+				20
3695
+			),
3696
+			'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3697
+		];
3698
+		// ONLY add the screen option if the user has access to it.
3699
+		if ($this->check_user_access($this->_current_view, true)) {
3700
+			add_screen_option($option, $args);
3701
+		}
3702
+	}
3703
+
3704
+
3705
+	/**
3706
+	 * set_per_page_screen_option
3707
+	 * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3708
+	 * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3709
+	 * admin_menu.
3710
+	 *
3711
+	 * @return void
3712
+	 */
3713
+	private function _set_per_page_screen_options()
3714
+	{
3715
+		if ($this->request->requestParamIsSet('wp_screen_options')) {
3716
+			check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3717
+			if (! $user = wp_get_current_user()) {
3718
+				return;
3719
+			}
3720
+			$option = $this->request->getRequestParam('wp_screen_options[option]', '', DataType::KEY);
3721
+			if (! $option) {
3722
+				return;
3723
+			}
3724
+			$value      = $this->request->getRequestParam('wp_screen_options[value]', 0, DataType::INT);
3725
+			$map_option = $option;
3726
+			$option     = str_replace('-', '_', $option);
3727
+			switch ($map_option) {
3728
+				case $this->_current_page . '_' . $this->_current_view . '_per_page':
3729
+					$max_value = apply_filters(
3730
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3731
+						999,
3732
+						$this->_current_page,
3733
+						$this->_current_view
3734
+					);
3735
+					if ($value < 1) {
3736
+						return;
3737
+					}
3738
+					$value = min($value, $max_value);
3739
+					break;
3740
+				default:
3741
+					$value = apply_filters(
3742
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3743
+						false,
3744
+						$option,
3745
+						$value
3746
+					);
3747
+					if (false === $value) {
3748
+						return;
3749
+					}
3750
+					break;
3751
+			}
3752
+			update_user_meta($user->ID, $option, $value);
3753
+			wp_safe_redirect(remove_query_arg(['pagenum', 'apage', 'paged'], wp_get_referer()));
3754
+			exit;
3755
+		}
3756
+	}
3757
+
3758
+
3759
+	/**
3760
+	 * This just allows for setting the $_template_args property if it needs to be set outside the object
3761
+	 *
3762
+	 * @param array $data array that will be assigned to template args.
3763
+	 */
3764
+	public function set_template_args(array $data)
3765
+	{
3766
+		$this->_template_args = array_merge($this->_template_args, $data);
3767
+	}
3768
+
3769
+
3770
+	public function setAdminPageTitle(string $title)
3771
+	{
3772
+		$this->_admin_page_title = sanitize_text_field($title);
3773
+	}
3774
+
3775
+
3776
+	/**
3777
+	 * This makes available the WP transient system for temporarily moving data between routes
3778
+	 *
3779
+	 * @param string $route             the route that should receive the transient
3780
+	 * @param array  $data              the data that gets sent
3781
+	 * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise it's just a
3782
+	 *                                  normal route transient.
3783
+	 * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3784
+	 *                                  when we are adding a transient before page_routes have been defined.
3785
+	 * @return void
3786
+	 * @throws EE_Error
3787
+	 */
3788
+	protected function _add_transient(
3789
+		string $route,
3790
+		array $data,
3791
+		bool $notices = false,
3792
+		bool $skip_route_verify = false
3793
+	) {
3794
+		$user_id = get_current_user_id();
3795
+		if (! $skip_route_verify) {
3796
+			$this->_verify_route($route);
3797
+		}
3798
+		// now let's set the string for what kind of transient we're setting
3799
+		$transient = $notices ? "ee_rte_n_tx_{$route}_$user_id" : "rte_tx_{$route}_$user_id";
3800
+		$data      = $notices ? ['notices' => $data] : $data;
3801
+		// is there already a transient for this route?  If there is then let's ADD to that transient
3802
+		$existing = is_multisite() && is_network_admin()
3803
+			? get_site_transient($transient)
3804
+			: get_transient($transient);
3805
+		if ($existing) {
3806
+			$data = array_merge($data, (array) $existing);
3807
+		}
3808
+		if (is_multisite() && is_network_admin()) {
3809
+			set_site_transient($transient, $data, 8);
3810
+		} else {
3811
+			set_transient($transient, $data, 8);
3812
+		}
3813
+	}
3814
+
3815
+
3816
+	/**
3817
+	 * this retrieves the temporary transient that has been set for moving data between routes.
3818
+	 *
3819
+	 * @param bool   $notices true we get notices transient. False we just return normal route transient
3820
+	 * @param string $route
3821
+	 * @return mixed data
3822
+	 */
3823
+	protected function _get_transient(bool $notices = false, string $route = '')
3824
+	{
3825
+		$user_id   = get_current_user_id();
3826
+		$route     = ! $route ? $this->_req_action : $route;
3827
+		$transient = $notices
3828
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3829
+			: 'rte_tx_' . $route . '_' . $user_id;
3830
+		$data      = is_multisite() && is_network_admin()
3831
+			? get_site_transient($transient)
3832
+			: get_transient($transient);
3833
+		// delete transient after retrieval (just in case it hasn't expired);
3834
+		if (is_multisite() && is_network_admin()) {
3835
+			delete_site_transient($transient);
3836
+		} else {
3837
+			delete_transient($transient);
3838
+		}
3839
+		return $notices && isset($data['notices']) ? $data['notices'] : $data;
3840
+	}
3841
+
3842
+
3843
+	/**
3844
+	 * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3845
+	 * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3846
+	 * default route callback on the EE_Admin page you want it run.)
3847
+	 *
3848
+	 * @return void
3849
+	 */
3850
+	protected function _transient_garbage_collection()
3851
+	{
3852
+		global $wpdb;
3853
+		// retrieve all existing transients
3854
+		$query =
3855
+			"SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3856
+		if ($results = $wpdb->get_results($query)) {
3857
+			foreach ($results as $result) {
3858
+				$transient = str_replace('_transient_', '', $result->option_name);
3859
+				get_transient($transient);
3860
+				if (is_multisite() && is_network_admin()) {
3861
+					get_site_transient($transient);
3862
+				}
3863
+			}
3864
+		}
3865
+	}
3866
+
3867
+
3868
+	/**
3869
+	 * get_view
3870
+	 *
3871
+	 * @return string content of _view property
3872
+	 */
3873
+	public function get_view(): string
3874
+	{
3875
+		return $this->_view;
3876
+	}
3877
+
3878
+
3879
+	/**
3880
+	 * getter for the protected $_views property
3881
+	 *
3882
+	 * @return array
3883
+	 */
3884
+	public function get_views(): array
3885
+	{
3886
+		return $this->_views;
3887
+	}
3888
+
3889
+
3890
+	/**
3891
+	 * @param array $views
3892
+	 * @return void
3893
+	 * @since 5.0.13.p
3894
+	 */
3895
+	public function updateViews(array $views)
3896
+	{
3897
+		$this->_views = array_merge($this->_views, $views);
3898
+	}
3899
+
3900
+
3901
+	/**
3902
+    /**
3903
+	 * get_current_page
3904
+	 *
3905
+	 * @return string _current_page property value
3906
+	 */
3907
+	public function get_current_page(): string
3908
+	{
3909
+		return $this->_current_page;
3910
+	}
3911
+
3912
+
3913
+	/**
3914
+	 * get_current_view
3915
+	 *
3916
+	 * @return string _current_view property value
3917
+	 */
3918
+	public function get_current_view(): string
3919
+	{
3920
+		return $this->_current_view;
3921
+	}
3922
+
3923
+
3924
+	/**
3925
+	 * get_current_screen
3926
+	 *
3927
+	 * @return object The current WP_Screen object
3928
+	 */
3929
+	public function get_current_screen()
3930
+	{
3931
+		return $this->_current_screen;
3932
+	}
3933
+
3934
+
3935
+	/**
3936
+	 * get_current_page_view_url
3937
+	 *
3938
+	 * @return string This returns the url for the current_page_view.
3939
+	 */
3940
+	public function get_current_page_view_url(): string
3941
+	{
3942
+		return $this->_current_page_view_url;
3943
+	}
3944
+
3945
+
3946
+	/**
3947
+	 * just returns the Request
3948
+	 *
3949
+	 * @return RequestInterface
3950
+	 */
3951
+	public function get_request(): ?RequestInterface
3952
+	{
3953
+		return $this->request;
3954
+	}
3955
+
3956
+
3957
+	/**
3958
+	 * just returns the _req_data property
3959
+	 *
3960
+	 * @return array
3961
+	 */
3962
+	public function get_request_data(): array
3963
+	{
3964
+		return $this->request->requestParams();
3965
+	}
3966
+
3967
+
3968
+	/**
3969
+	 * returns the _req_data protected property
3970
+	 *
3971
+	 * @return string
3972
+	 */
3973
+	public function get_req_action(): string
3974
+	{
3975
+		return $this->_req_action;
3976
+	}
3977
+
3978
+
3979
+	/**
3980
+	 * @return bool  value of $_is_caf property
3981
+	 */
3982
+	public function is_caf(): bool
3983
+	{
3984
+		return $this->_is_caf;
3985
+	}
3986
+
3987
+
3988
+	/**
3989
+	 * @return array
3990
+	 */
3991
+	public function default_espresso_metaboxes(): array
3992
+	{
3993
+		return $this->_default_espresso_metaboxes;
3994
+	}
3995
+
3996
+
3997
+	/**
3998
+	 * @return string
3999
+	 */
4000
+	public function admin_base_url(): string
4001
+	{
4002
+		return $this->_admin_base_url;
4003
+	}
4004
+
4005
+
4006
+	/**
4007
+	 * @return string
4008
+	 */
4009
+	public function wp_page_slug(): string
4010
+	{
4011
+		return $this->_wp_page_slug;
4012
+	}
4013
+
4014
+
4015
+	/**
4016
+	 * updates  espresso configuration settings
4017
+	 *
4018
+	 * @param string                   $tab
4019
+	 * @param EE_Config_Base|EE_Config $config
4020
+	 * @param string                   $file file where error occurred
4021
+	 * @param string                   $func function  where error occurred
4022
+	 * @param string                   $line line no where error occurred
4023
+	 * @return bool
4024
+	 * @throws EE_Error
4025
+	 * @throws ReflectionException
4026
+	 */
4027
+	protected function _update_espresso_configuration(
4028
+		string $tab,
4029
+		$config,
4030
+		string $file = '',
4031
+		string $func = '',
4032
+		string $line = ''
4033
+	): bool {
4034
+		// remove any options that are NOT going to be saved with the config settings.
4035
+		if (isset($config->core->ee_ueip_optin)) {
4036
+			// TODO: remove the following two lines and make sure values are migrated from 3.1
4037
+			update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4038
+			update_option('ee_ueip_has_notified', true);
4039
+		}
4040
+		// and save it (note we're also doing the network save here)
4041
+		$net_saved    = ! is_main_site() || EE_Network_Config::instance()->update_config(false, false);
4042
+		$config_saved = EE_Config::instance()->update_espresso_config(false, false);
4043
+		if ($config_saved && $net_saved) {
4044
+			EE_Error::add_success(sprintf(esc_html__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4045
+			return true;
4046
+		}
4047
+		EE_Error::add_error(
4048
+			sprintf(esc_html__('The "%s" were not updated.', 'event_espresso'), $tab),
4049
+			$file,
4050
+			$func,
4051
+			$line
4052
+		);
4053
+		return false;
4054
+	}
4055
+
4056
+
4057
+	/**
4058
+	 * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4059
+	 *
4060
+	 * @return array
4061
+	 */
4062
+	public function get_yes_no_values(): array
4063
+	{
4064
+		return $this->_yes_no_values;
4065
+	}
4066
+
4067
+
4068
+	/**
4069
+	 * @return string
4070
+	 * @throws ReflectionException
4071
+	 * @since 5.0.0.p
4072
+	 */
4073
+	protected function _get_dir(): string
4074
+	{
4075
+		$reflector = new ReflectionClass($this->class_name);
4076
+		return dirname($reflector->getFileName());
4077
+	}
4078
+
4079
+
4080
+	/**
4081
+	 * A helper for getting a "next link".
4082
+	 *
4083
+	 * @param string $url   The url to link to
4084
+	 * @param string $class The class to use.
4085
+	 * @return string
4086
+	 */
4087
+	protected function _next_link(string $url, string $class = 'dashicons dashicons-arrow-right'): string
4088
+	{
4089
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4090
+	}
4091
+
4092
+
4093
+	/**
4094
+	 * A helper for getting a "previous link".
4095
+	 *
4096
+	 * @param string $url   The url to link to
4097
+	 * @param string $class The class to use.
4098
+	 * @return string
4099
+	 */
4100
+	protected function _previous_link(string $url, string $class = 'dashicons dashicons-arrow-left'): string
4101
+	{
4102
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4103
+	}
4104
+
4105
+
4106
+
4107
+
4108
+
4109
+
4110
+
4111
+	// below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4112
+
4113
+
4114
+	/**
4115
+	 * This processes a request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4116
+	 * 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
4117
+	 * _req_data array.
4118
+	 *
4119
+	 * @return bool success/fail
4120
+	 * @throws EE_Error
4121
+	 * @throws InvalidArgumentException
4122
+	 * @throws ReflectionException
4123
+	 * @throws InvalidDataTypeException
4124
+	 * @throws InvalidInterfaceException
4125
+	 */
4126
+	protected function _process_resend_registration(): bool
4127
+	{
4128
+		$this->_template_args['success'] = EED_Messages::process_resend($this->request->requestParams());
4129
+		do_action(
4130
+			'AHEE__EE_Admin_Page___process_resend_registration',
4131
+			$this->_template_args['success'],
4132
+			$this->request->requestParams()
4133
+		);
4134
+		return $this->_template_args['success'];
4135
+	}
4136
+
4137
+
4138
+	/**
4139
+	 * This automatically processes any payment message notifications when manual payment has been applied.
4140
+	 *
4141
+	 * @param EE_Payment $payment
4142
+	 * @return bool success/fail
4143
+	 */
4144
+	protected function _process_payment_notification(EE_Payment $payment): bool
4145
+	{
4146
+		add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4147
+		do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4148
+		$this->_template_args['success'] = apply_filters(
4149
+			'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4150
+			false,
4151
+			$payment
4152
+		);
4153
+		return $this->_template_args['success'];
4154
+	}
4155
+
4156
+
4157
+	/**
4158
+	 * @param EEM_Base      $entity_model
4159
+	 * @param string        $entity_PK_name name of the primary key field used as a request param, ie: id, ID, etc
4160
+	 * @param string        $action         one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4161
+	 * @param string        $delete_column  name of the field that denotes whether entity is trashed
4162
+	 * @param callable|null $callback       called after entity is trashed, restored, or deleted
4163
+	 * @return int|float
4164
+	 * @throws EE_Error
4165
+	 */
4166
+	protected function trashRestoreDeleteEntities(
4167
+		EEM_Base $entity_model,
4168
+		string $entity_PK_name,
4169
+		string $action = EE_Admin_List_Table::ACTION_DELETE,
4170
+		string $delete_column = '',
4171
+		?callable $callback = null
4172
+	) {
4173
+		$entity_PK      = $entity_model->get_primary_key_field();
4174
+		$entity_PK_name = $entity_PK_name ?: $entity_PK->get_name();
4175
+		$entity_PK_type = $this->resolveEntityFieldDataType($entity_PK);
4176
+		// grab ID if deleting a single entity
4177
+		if ($this->request->requestParamIsSet($entity_PK_name)) {
4178
+			$ID = $this->request->getRequestParam($entity_PK_name, 0, $entity_PK_type);
4179
+			return $this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback) ? 1 : 0;
4180
+		}
4181
+		// or grab checkbox array if bulk deleting
4182
+		$checkboxes = $this->request->getRequestParam('checkbox', [], $entity_PK_type, true);
4183
+		if (empty($checkboxes)) {
4184
+			return 0;
4185
+		}
4186
+		$success = 0;
4187
+		$IDs     = array_keys($checkboxes);
4188
+		// cycle thru bulk action checkboxes
4189
+		foreach ($IDs as $ID) {
4190
+			// increment $success
4191
+			if ($this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback)) {
4192
+				$success++;
4193
+			}
4194
+		}
4195
+		$count = (int) count($checkboxes);
4196
+		// if multiple entities were deleted successfully, then $deleted will be full count of deletions,
4197
+		// otherwise it will be a fraction of ( actual deletions / total entities to be deleted )
4198
+		return $success === $count ? $count : $success / $count;
4199
+	}
4200
+
4201
+
4202
+	/**
4203
+	 * @param EE_Primary_Key_Field_Base $entity_PK
4204
+	 * @return string
4205
+	 * @throws EE_Error
4206
+	 * @since   4.10.30.p
4207
+	 */
4208
+	private function resolveEntityFieldDataType(EE_Primary_Key_Field_Base $entity_PK): string
4209
+	{
4210
+		$entity_PK_type = $entity_PK->getSchemaType();
4211
+		switch ($entity_PK_type) {
4212
+			case 'boolean':
4213
+				return DataType::BOOL;
4214
+			case 'integer':
4215
+				return DataType::INT;
4216
+			case 'number':
4217
+				return DataType::FLOAT;
4218
+			case 'string':
4219
+				return DataType::STRING;
4220
+		}
4221
+		throw new RuntimeException(
4222
+			sprintf(
4223
+				esc_html__(
4224
+					'"%1$s" is an invalid schema type for the %2$s primary key.',
4225
+					'event_espresso'
4226
+				),
4227
+				$entity_PK_type,
4228
+				$entity_PK->get_name()
4229
+			)
4230
+		);
4231
+	}
4232
+
4233
+
4234
+	/**
4235
+	 * @param EEM_Base      $entity_model
4236
+	 * @param int|string    $entity_ID
4237
+	 * @param string        $action        one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4238
+	 * @param string        $delete_column name of the field that denotes whether entity is trashed
4239
+	 * @param callable|null $callback      called after entity is trashed, restored, or deleted
4240
+	 * @return bool
4241
+	 */
4242
+	protected function trashRestoreDeleteEntity(
4243
+		EEM_Base $entity_model,
4244
+		$entity_ID,
4245
+		string $action,
4246
+		string $delete_column,
4247
+		?callable $callback = null
4248
+	): bool {
4249
+		$entity_ID = absint($entity_ID);
4250
+		if (! $entity_ID) {
4251
+			$this->trashRestoreDeleteError($action, $entity_model);
4252
+		}
4253
+		$result = 0;
4254
+		try {
4255
+			$entity = $entity_model->get_one_by_ID($entity_ID);
4256
+			if (! $entity instanceof EE_Base_Class) {
4257
+				throw new DomainException(
4258
+					sprintf(
4259
+						esc_html__(
4260
+							'Missing or invalid %1$s entity with ID of "%2$s" returned from db.',
4261
+							'event_espresso'
4262
+						),
4263
+						str_replace('EEM_', '', $entity_model->get_this_model_name()),
4264
+						$entity_ID
4265
+					)
4266
+				);
4267
+			}
4268
+			switch ($action) {
4269
+				case EE_Admin_List_Table::ACTION_DELETE:
4270
+					$result = (bool) $entity->delete_permanently();
4271
+					break;
4272
+				case EE_Admin_List_Table::ACTION_RESTORE:
4273
+					$result = $entity->delete_or_restore(false);
4274
+					break;
4275
+				case EE_Admin_List_Table::ACTION_TRASH:
4276
+					$result = $entity->delete_or_restore();
4277
+					break;
4278
+			}
4279
+		} catch (Exception $exception) {
4280
+			$this->trashRestoreDeleteError($action, $entity_model, $exception);
4281
+		}
4282
+		if (is_callable($callback)) {
4283
+			call_user_func_array($callback, [$entity_model, $entity_ID, $action, $result, $delete_column]);
4284
+		}
4285
+		return $result;
4286
+	}
4287
+
4288
+
4289
+	/**
4290
+	 * @param EEM_Base $entity_model
4291
+	 * @param string   $delete_column
4292
+	 * @since 4.10.30.p
4293
+	 */
4294
+	private function validateDeleteColumn(EEM_Base $entity_model, string $delete_column)
4295
+	{
4296
+		if (empty($delete_column)) {
4297
+			throw new DomainException(
4298
+				sprintf(
4299
+					esc_html__(
4300
+						'You need to specify the name of the "delete column" on the %2$s model, in order to trash or restore an entity.',
4301
+						'event_espresso'
4302
+					),
4303
+					$entity_model->get_this_model_name()
4304
+				)
4305
+			);
4306
+		}
4307
+		if (! $entity_model->has_field($delete_column)) {
4308
+			throw new DomainException(
4309
+				sprintf(
4310
+					esc_html__(
4311
+						'The %1$s field does not exist on the %2$s model.',
4312
+						'event_espresso'
4313
+					),
4314
+					$delete_column,
4315
+					$entity_model->get_this_model_name()
4316
+				)
4317
+			);
4318
+		}
4319
+	}
4320
+
4321
+
4322
+	/**
4323
+	 * @param EEM_Base       $entity_model
4324
+	 * @param Exception|null $exception
4325
+	 * @param string         $action
4326
+	 * @since 4.10.30.p
4327
+	 */
4328
+	private function trashRestoreDeleteError(string $action, EEM_Base $entity_model, Exception $exception = null)
4329
+	{
4330
+		if ($exception instanceof Exception) {
4331
+			throw new RuntimeException(
4332
+				sprintf(
4333
+					esc_html__(
4334
+						'Could not %1$s the %2$s because the following error occurred: %3$s',
4335
+						'event_espresso'
4336
+					),
4337
+					$action,
4338
+					$entity_model->get_this_model_name(),
4339
+					$exception->getMessage()
4340
+				)
4341
+			);
4342
+		}
4343
+		throw new RuntimeException(
4344
+			sprintf(
4345
+				esc_html__(
4346
+					'Could not %1$s the %2$s because an invalid ID was received.',
4347
+					'event_espresso'
4348
+				),
4349
+				$action,
4350
+				$entity_model->get_this_model_name()
4351
+			)
4352
+		);
4353
+	}
4354 4354
 }
Please login to merge, or discard this patch.
Spacing   +185 added lines, -185 removed lines patch added patch discarded remove patch
@@ -24,23 +24,23 @@  discard block
 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 31
     protected ?EE_Admin_List_Table $_list_table_object = null;
32 32
 
33
-    protected ?EE_Capabilities $capabilities       = null;
33
+    protected ?EE_Capabilities $capabilities = null;
34 34
 
35
-    protected ?EE_Registry $EE                 = null;
35
+    protected ?EE_Registry $EE = null;
36 36
 
37
-    protected ?FeatureFlags $feature            = null;
37
+    protected ?FeatureFlags $feature = null;
38 38
 
39
-    protected ?LoaderInterface $loader             = null;
39
+    protected ?LoaderInterface $loader = null;
40 40
 
41
-    protected ?RequestInterface $request            = null;
41
+    protected ?RequestInterface $request = null;
42 42
 
43
-    protected ?WP_Screen $_current_screen    = null;
43
+    protected ?WP_Screen $_current_screen = null;
44 44
 
45 45
     /**
46 46
      * @var array
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
 
125 125
     protected string $_column_template_path = '';
126 126
 
127
-    protected bool $_cpt_route            = false;
127
+    protected bool $_cpt_route = false;
128 128
 
129 129
     /**
130 130
      * set via request page and action args.
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
 
246 246
     public function capabilities(): EE_Capabilities
247 247
     {
248
-        if (! $this->capabilities instanceof EE_Capabilities) {
248
+        if ( ! $this->capabilities instanceof EE_Capabilities) {
249 249
             $this->capabilities = $this->loader->getShared(EE_Capabilities::class);
250 250
         }
251 251
         return $this->capabilities;
@@ -609,7 +609,7 @@  discard block
 block discarded – undo
609 609
         $ee_menu_slugs = (array) $ee_menu_slugs;
610 610
         if (
611 611
             ! $this->request->isAjax()
612
-            && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
612
+            && ( ! $this->_current_page || ! isset($ee_menu_slugs[$this->_current_page]))
613 613
         ) {
614 614
             return;
615 615
         }
@@ -629,7 +629,7 @@  discard block
 block discarded – undo
629 629
             ? $route
630 630
             : $req_action;
631 631
         $this->_current_view = $this->_req_action;
632
-        $this->_req_nonce    = $this->_req_action . '_nonce';
632
+        $this->_req_nonce    = $this->_req_action.'_nonce';
633 633
         $this->_define_page_props();
634 634
         $this->_current_page_view_url = add_query_arg(
635 635
             ['page' => $this->_current_page, 'action' => $this->_current_view],
@@ -653,33 +653,33 @@  discard block
 block discarded – undo
653 653
         }
654 654
         // filter routes and page_config so addons can add their stuff. Filtering done per class
655 655
         $this->_page_routes = apply_filters(
656
-            'FHEE__' . $this->class_name . '__page_setup__page_routes',
656
+            'FHEE__'.$this->class_name.'__page_setup__page_routes',
657 657
             $this->_page_routes,
658 658
             $this
659 659
         );
660 660
         $this->_page_config = apply_filters(
661
-            'FHEE__' . $this->class_name . '__page_setup__page_config',
661
+            'FHEE__'.$this->class_name.'__page_setup__page_config',
662 662
             $this->_page_config,
663 663
             $this
664 664
         );
665 665
         if ($this->base_class_name !== '') {
666 666
             $this->_page_routes = apply_filters(
667
-                'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
667
+                'FHEE__'.$this->base_class_name.'__page_setup__page_routes',
668 668
                 $this->_page_routes,
669 669
                 $this
670 670
             );
671 671
             $this->_page_config = apply_filters(
672
-                'FHEE__' . $this->base_class_name . '__page_setup__page_config',
672
+                'FHEE__'.$this->base_class_name.'__page_setup__page_config',
673 673
                 $this->_page_config,
674 674
                 $this
675 675
             );
676 676
         }
677 677
         // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
678 678
         // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
679
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
679
+        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_'.$this->_current_view)) {
680 680
             add_action(
681 681
                 'AHEE__EE_Admin_Page__route_admin_request',
682
-                [$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
682
+                [$this, 'AHEE__EE_Admin_Page__route_admin_request_'.$this->_current_view],
683 683
                 10,
684 684
                 2
685 685
             );
@@ -692,8 +692,8 @@  discard block
 block discarded – undo
692 692
             if ($this->_is_UI_request) {
693 693
                 // admin_init stuff - global, all views for this page class, specific view
694 694
                 add_action('admin_init', [$this, 'admin_init']);
695
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
696
-                    add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
695
+                if (method_exists($this, 'admin_init_'.$this->_current_view)) {
696
+                    add_action('admin_init', [$this, 'admin_init_'.$this->_current_view], 15);
697 697
                 }
698 698
             } else {
699 699
                 // hijack regular WP loading and route admin request immediately
@@ -712,12 +712,12 @@  discard block
 block discarded – undo
712 712
      */
713 713
     private function _do_other_page_hooks()
714 714
     {
715
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
715
+        $registered_pages = apply_filters('FHEE_do_other_page_hooks_'.$this->page_slug, []);
716 716
         foreach ($registered_pages as $page) {
717 717
             // now let's set up the file name and class that should be present
718 718
             $classname = str_replace('.class.php', '', $page);
719 719
             // autoloaders should take care of loading file
720
-            if (! class_exists($classname)) {
720
+            if ( ! class_exists($classname)) {
721 721
                 $error_msg[] = sprintf(
722 722
                     esc_html__(
723 723
                         'Something went wrong with loading the %s admin hooks page.',
@@ -734,7 +734,7 @@  discard block
 block discarded – undo
734 734
                                    ),
735 735
                                    $page,
736 736
                                    '<br />',
737
-                                   '<strong>' . $classname . '</strong>'
737
+                                   '<strong>'.$classname.'</strong>'
738 738
                                );
739 739
                 throw new EE_Error(implode('||', $error_msg));
740 740
             }
@@ -783,13 +783,13 @@  discard block
 block discarded – undo
783 783
         // load admin_notices - global, page class, and view specific
784 784
         add_action('admin_notices', [$this, 'admin_notices_global'], 5);
785 785
         add_action('admin_notices', [$this, 'admin_notices']);
786
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
787
-            add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
786
+        if (method_exists($this, 'admin_notices_'.$this->_current_view)) {
787
+            add_action('admin_notices', [$this, 'admin_notices_'.$this->_current_view], 15);
788 788
         }
789 789
         // load network admin_notices - global, page class, and view specific
790 790
         add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
791
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
792
-            add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
791
+        if (method_exists($this, 'network_admin_notices_'.$this->_current_view)) {
792
+            add_action('network_admin_notices', [$this, 'network_admin_notices_'.$this->_current_view]);
793 793
         }
794 794
         // this will save any per_page screen options if they are present
795 795
         $this->_set_per_page_screen_options();
@@ -915,7 +915,7 @@  discard block
 block discarded – undo
915 915
      */
916 916
     protected function _verify_routes(): bool
917 917
     {
918
-        if (! $this->_current_page && ! $this->request->isAjax()) {
918
+        if ( ! $this->_current_page && ! $this->request->isAjax()) {
919 919
             return false;
920 920
         }
921 921
         // check that the page_routes array is not empty
@@ -926,7 +926,7 @@  discard block
 block discarded – undo
926 926
                 $this->_admin_page_title
927 927
             );
928 928
             // developer error msg
929
-            $error_msg .= '||' . $error_msg
929
+            $error_msg .= '||'.$error_msg
930 930
                           . esc_html__(
931 931
                               ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
932 932
                               'event_espresso'
@@ -938,14 +938,14 @@  discard block
 block discarded – undo
938 938
         if (
939 939
             $this->_req_action === 'editpost'
940 940
             && ! isset($this->_page_routes['editpost'])
941
-            && isset($this->_page_routes[ $alt_edit_route ])
941
+            && isset($this->_page_routes[$alt_edit_route])
942 942
         ) {
943 943
             $this->_req_action = $alt_edit_route;
944 944
         }
945 945
         // and that the requested page route exists
946 946
         if (array_key_exists($this->_req_action, $this->_page_routes)) {
947
-            $this->_route        = $this->_page_routes[ $this->_req_action ];
948
-            $this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
947
+            $this->_route        = $this->_page_routes[$this->_req_action];
948
+            $this->_route_config = $this->_page_config[$this->_req_action] ?? [];
949 949
         } else {
950 950
             // user error msg
951 951
             $error_msg = sprintf(
@@ -956,7 +956,7 @@  discard block
 block discarded – undo
956 956
                 $this->_admin_page_title
957 957
             );
958 958
             // developer error msg
959
-            $error_msg .= '||' . $error_msg
959
+            $error_msg .= '||'.$error_msg
960 960
                           . sprintf(
961 961
                               esc_html__(
962 962
                                   ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
@@ -967,7 +967,7 @@  discard block
 block discarded – undo
967 967
             throw new EE_Error($error_msg);
968 968
         }
969 969
         // and that a default route exists
970
-        if (! array_key_exists('default', $this->_page_routes)) {
970
+        if ( ! array_key_exists('default', $this->_page_routes)) {
971 971
             // user error msg
972 972
             $error_msg = sprintf(
973 973
                 esc_html__(
@@ -977,7 +977,7 @@  discard block
 block discarded – undo
977 977
                 $this->_admin_page_title
978 978
             );
979 979
             // developer error msg
980
-            $error_msg .= '||' . $error_msg
980
+            $error_msg .= '||'.$error_msg
981 981
                           . esc_html__(
982 982
                               ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
983 983
                               'event_espresso'
@@ -1017,7 +1017,7 @@  discard block
 block discarded – undo
1017 1017
             $this->_admin_page_title
1018 1018
         );
1019 1019
         // developer error msg
1020
-        $error_msg .= '||' . $error_msg
1020
+        $error_msg .= '||'.$error_msg
1021 1021
                       . sprintf(
1022 1022
                           esc_html__(
1023 1023
                               ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
@@ -1045,7 +1045,7 @@  discard block
 block discarded – undo
1045 1045
     protected function _verify_nonce(string $nonce, string $nonce_ref)
1046 1046
     {
1047 1047
         // verify nonce against expected value
1048
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
1048
+        if ( ! wp_verify_nonce($nonce, $nonce_ref)) {
1049 1049
             // these are not the droids you are looking for !!!
1050 1050
             $msg = sprintf(
1051 1051
                 esc_html__('%sNonce Fail.%s', 'event_espresso'),
@@ -1062,7 +1062,7 @@  discard block
 block discarded – undo
1062 1062
                     __CLASS__
1063 1063
                 );
1064 1064
             }
1065
-            if (! $this->request->isAjax()) {
1065
+            if ( ! $this->request->isAjax()) {
1066 1066
                 wp_die($msg);
1067 1067
             }
1068 1068
             EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
@@ -1087,7 +1087,7 @@  discard block
 block discarded – undo
1087 1087
      */
1088 1088
     protected function _route_admin_request()
1089 1089
     {
1090
-        if (! $this->_is_UI_request) {
1090
+        if ( ! $this->_is_UI_request) {
1091 1091
             $this->_verify_routes();
1092 1092
         }
1093 1093
         $nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
@@ -1107,7 +1107,7 @@  discard block
 block discarded – undo
1107 1107
 
1108 1108
         // action right before calling route
1109 1109
         // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1110
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1110
+        if ( ! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1111 1111
             do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1112 1112
         }
1113 1113
         // strip _wp_http_referer from the server REQUEST_URI
@@ -1122,7 +1122,7 @@  discard block
 block discarded – undo
1122 1122
         $this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1123 1123
         $this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1124 1124
         $route_callback = [];
1125
-        if (! empty($func)) {
1125
+        if ( ! empty($func)) {
1126 1126
             if (is_array($func) && is_callable($func)) {
1127 1127
                 $route_callback = $func;
1128 1128
             } elseif (is_string($func)) {
@@ -1136,7 +1136,7 @@  discard block
 block discarded – undo
1136 1136
             }
1137 1137
             [$class, $method] = $route_callback;
1138 1138
             // is it neither a class method NOR a standalone function?
1139
-            if (! is_callable($route_callback)) {
1139
+            if ( ! is_callable($route_callback)) {
1140 1140
                 // user error msg
1141 1141
                 $error_msg = esc_html__(
1142 1142
                     'An error occurred. The  requested page route could not be found.',
@@ -1245,7 +1245,7 @@  discard block
 block discarded – undo
1245 1245
                 if (strpos($key, 'nonce') !== false) {
1246 1246
                     continue;
1247 1247
                 }
1248
-                $args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1248
+                $args['wp_referer['.$key.']'] = is_string($value) ? htmlspecialchars($value) : $value;
1249 1249
             }
1250 1250
         }
1251 1251
         return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce, $context);
@@ -1285,12 +1285,12 @@  discard block
 block discarded – undo
1285 1285
      */
1286 1286
     protected function _add_help_tabs()
1287 1287
     {
1288
-        if (isset($this->_page_config[ $this->_req_action ])) {
1289
-            $config = $this->_page_config[ $this->_req_action ];
1288
+        if (isset($this->_page_config[$this->_req_action])) {
1289
+            $config = $this->_page_config[$this->_req_action];
1290 1290
             // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1291 1291
             if (is_array($config) && isset($config['help_sidebar'])) {
1292 1292
                 // check that the callback given is valid
1293
-                if (! method_exists($this, $config['help_sidebar'])) {
1293
+                if ( ! method_exists($this, $config['help_sidebar'])) {
1294 1294
                     throw new EE_Error(
1295 1295
                         sprintf(
1296 1296
                             esc_html__(
@@ -1303,18 +1303,18 @@  discard block
 block discarded – undo
1303 1303
                     );
1304 1304
                 }
1305 1305
                 $content = apply_filters(
1306
-                    'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1306
+                    'FHEE__'.$this->class_name.'__add_help_tabs__help_sidebar',
1307 1307
                     $this->{$config['help_sidebar']}()
1308 1308
                 );
1309 1309
                 $this->_current_screen->set_help_sidebar($content);
1310 1310
             }
1311
-            if (! isset($config['help_tabs'])) {
1311
+            if ( ! isset($config['help_tabs'])) {
1312 1312
                 return;
1313 1313
             } //no help tabs for this route
1314 1314
             foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1315 1315
                 // we're here so there ARE help tabs!
1316 1316
                 // make sure we've got what we need
1317
-                if (! isset($cfg['title'])) {
1317
+                if ( ! isset($cfg['title'])) {
1318 1318
                     throw new EE_Error(
1319 1319
                         esc_html__(
1320 1320
                             'The _page_config array is not set up properly for help tabs.  It is missing a title',
@@ -1322,7 +1322,7 @@  discard block
 block discarded – undo
1322 1322
                         )
1323 1323
                     );
1324 1324
                 }
1325
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1325
+                if ( ! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1326 1326
                     throw new EE_Error(
1327 1327
                         esc_html__(
1328 1328
                             '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,11 +1331,11 @@  discard block
 block discarded – undo
1331 1331
                     );
1332 1332
                 }
1333 1333
                 // first priority goes to content.
1334
-                if (! empty($cfg['content'])) {
1334
+                if ( ! empty($cfg['content'])) {
1335 1335
                     $content = $cfg['content'];
1336 1336
                     // second priority goes to filename
1337
-                } elseif (! empty($cfg['filename'])) {
1338
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1337
+                } elseif ( ! empty($cfg['filename'])) {
1338
+                    $file_path = $this->_get_dir().'/help_tabs/'.$cfg['filename'].'.help_tab.php';
1339 1339
                     // 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)
1340 1340
                     $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1341 1341
                                                              . basename($this->_get_dir())
@@ -1343,7 +1343,7 @@  discard block
 block discarded – undo
1343 1343
                                                              . $cfg['filename']
1344 1344
                                                              . '.help_tab.php' : $file_path;
1345 1345
                     // if file is STILL not readable then let's do an EE_Error so its more graceful than a fatal error.
1346
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1346
+                    if ( ! isset($cfg['callback']) && ! is_readable($file_path)) {
1347 1347
                         EE_Error::add_error(
1348 1348
                             sprintf(
1349 1349
                                 esc_html__(
@@ -1391,7 +1391,7 @@  discard block
 block discarded – undo
1391 1391
                     return;
1392 1392
                 }
1393 1393
                 // setup config array for help tab method
1394
-                $id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1394
+                $id  = $this->page_slug.'-'.$this->_req_action.'-'.$tab_id;
1395 1395
                 $_ht = [
1396 1396
                     'id'       => $id,
1397 1397
                     'title'    => $cfg['title'],
@@ -1417,8 +1417,8 @@  discard block
 block discarded – undo
1417 1417
             $qtips = (array) $this->_route_config['qtips'];
1418 1418
             // load qtip loader
1419 1419
             $path = [
1420
-                $this->_get_dir() . '/qtips/',
1421
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1420
+                $this->_get_dir().'/qtips/',
1421
+                EE_ADMIN_PAGES.basename($this->_get_dir()).'/qtips/',
1422 1422
             ];
1423 1423
             EEH_Qtip_Loader::instance()->register($qtips, $path);
1424 1424
         }
@@ -1440,7 +1440,7 @@  discard block
 block discarded – undo
1440 1440
         $i        = 0;
1441 1441
         $only_tab = count($this->_page_config) < 2;
1442 1442
         foreach ($this->_page_config as $slug => $config) {
1443
-            if (! is_array($config) || empty($config['nav'])) {
1443
+            if ( ! is_array($config) || empty($config['nav'])) {
1444 1444
                 continue;
1445 1445
             }
1446 1446
             // no nav tab for this config
@@ -1449,7 +1449,7 @@  discard block
 block discarded – undo
1449 1449
                 // nav tab is only to appear when route requested.
1450 1450
                 continue;
1451 1451
             }
1452
-            if (! $this->check_user_access($slug, true)) {
1452
+            if ( ! $this->check_user_access($slug, true)) {
1453 1453
                 // no nav tab because current user does not have access.
1454 1454
                 continue;
1455 1455
             }
@@ -1457,20 +1457,20 @@  discard block
 block discarded – undo
1457 1457
             $css_class .= $only_tab ? ' ee-only-tab' : '';
1458 1458
             $css_class .= " ee-nav-tab__$slug";
1459 1459
 
1460
-            $this->_nav_tabs[ $slug ] = [
1460
+            $this->_nav_tabs[$slug] = [
1461 1461
                 'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1462 1462
                     ['action' => $slug],
1463 1463
                     $this->_admin_base_url
1464 1464
                 ),
1465 1465
                 'link_text' => $this->navTabLabel($config['nav'], $slug),
1466
-                'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1466
+                'css_class' => $this->_req_action === $slug ? $css_class.' nav-tab-active' : $css_class,
1467 1467
                 'order'     => $config['nav']['order'] ?? $i,
1468 1468
             ];
1469 1469
             $i++;
1470 1470
         }
1471 1471
         // if $this->_nav_tabs is empty then lets set the default
1472 1472
         if (empty($this->_nav_tabs)) {
1473
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1473
+            $this->_nav_tabs[$this->_default_nav_tab_name] = [
1474 1474
                 'url'       => $this->_admin_base_url,
1475 1475
                 'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1476 1476
                 'css_class' => 'nav-tab-active',
@@ -1486,11 +1486,11 @@  discard block
 block discarded – undo
1486 1486
     {
1487 1487
         $label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1488 1488
         $icon  = $nav_tab['icon'] ?? null;
1489
-        $icon  = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1489
+        $icon  = $icon ? '<span class="dashicons '.$icon.'"></span>' : '';
1490 1490
         return '
1491 1491
             <span class="ee-admin-screen-tab__label">
1492
-                ' . $icon . '
1493
-                <span class="ee-nav-label__text">' . $label . '</span>
1492
+                ' . $icon.'
1493
+                <span class="ee-nav-label__text">' . $label.'</span>
1494 1494
             </span>';
1495 1495
     }
1496 1496
 
@@ -1508,10 +1508,10 @@  discard block
 block discarded – undo
1508 1508
             foreach ($this->_route_config['labels'] as $label => $text) {
1509 1509
                 if (is_array($text)) {
1510 1510
                     foreach ($text as $sublabel => $subtext) {
1511
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1511
+                        $this->_labels[$label][$sublabel] = $subtext;
1512 1512
                     }
1513 1513
                 } else {
1514
-                    $this->_labels[ $label ] = $text;
1514
+                    $this->_labels[$label] = $text;
1515 1515
                 }
1516 1516
             }
1517 1517
         }
@@ -1533,8 +1533,8 @@  discard block
 block discarded – undo
1533 1533
     {
1534 1534
         // if no route_to_check is passed in then use the current route set via _req_action
1535 1535
         $action = $route_to_check ?: $this->_req_action;
1536
-        $capability = ! empty($this->_page_routes[ $action ]['capability'])
1537
-            ? $this->_page_routes[ $action ]['capability']
1536
+        $capability = ! empty($this->_page_routes[$action]['capability'])
1537
+            ? $this->_page_routes[$action]['capability']
1538 1538
             : null;
1539 1539
 
1540 1540
         if (empty($capability)) {
@@ -1584,14 +1584,14 @@  discard block
 block discarded – undo
1584 1584
         string $priority = 'default',
1585 1585
         ?array $callback_args = null
1586 1586
     ) {
1587
-        if (! (is_callable($callback) || ! function_exists($callback))) {
1587
+        if ( ! (is_callable($callback) || ! function_exists($callback))) {
1588 1588
             return;
1589 1589
         }
1590 1590
 
1591 1591
         add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1592 1592
         add_filter(
1593 1593
             "postbox_classes_{$this->_wp_page_slug}_$box_id",
1594
-            function ($classes) {
1594
+            function($classes) {
1595 1595
                 $classes[] = 'ee-admin-container';
1596 1596
                 return $classes;
1597 1597
             }
@@ -1682,8 +1682,8 @@  discard block
 block discarded – undo
1682 1682
             <div class="ee-notices"></div>
1683 1683
             <div class="ee-admin-dialog-container-inner-content"></div>
1684 1684
         </div>
1685
-        <span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>
1686
-        <input type="hidden" id="espresso_admin_current_page" value="' . esc_attr($this->_current_page) . '"/>';
1685
+        <span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()).'</span>
1686
+        <input type="hidden" id="espresso_admin_current_page" value="' . esc_attr($this->_current_page).'"/>';
1687 1687
     }
1688 1688
 
1689 1689
 
@@ -1717,7 +1717,7 @@  discard block
 block discarded – undo
1717 1717
         // loop through the array and setup content
1718 1718
         foreach ($help_array as $trigger => $help) {
1719 1719
             // make sure the array is set up properly
1720
-            if (! isset($help['title'], $help['content'])) {
1720
+            if ( ! isset($help['title'], $help['content'])) {
1721 1721
                 throw new EE_Error(
1722 1722
                     esc_html__(
1723 1723
                         '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',
@@ -1731,8 +1731,8 @@  discard block
 block discarded – undo
1731 1731
                 'help_popup_title'   => $help['title'],
1732 1732
                 'help_popup_content' => $help['content'],
1733 1733
             ];
1734
-            $content       .= EEH_Template::display_template(
1735
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1734
+            $content .= EEH_Template::display_template(
1735
+                EE_ADMIN_TEMPLATE.'admin_help_popup.template.php',
1736 1736
                 $template_args,
1737 1737
                 true
1738 1738
             );
@@ -1754,15 +1754,15 @@  discard block
 block discarded – undo
1754 1754
     private function _get_help_content(): array
1755 1755
     {
1756 1756
         // what is the method we're looking for?
1757
-        $method_name = '_help_popup_content_' . $this->_req_action;
1757
+        $method_name = '_help_popup_content_'.$this->_req_action;
1758 1758
         // if method doesn't exist let's get out.
1759
-        if (! method_exists($this, $method_name)) {
1759
+        if ( ! method_exists($this, $method_name)) {
1760 1760
             return [];
1761 1761
         }
1762 1762
         // k we're good to go let's retrieve the help array
1763 1763
         $help_array = $this->{$method_name}();
1764 1764
         // make sure we've got an array!
1765
-        if (! is_array($help_array)) {
1765
+        if ( ! is_array($help_array)) {
1766 1766
             throw new EE_Error(
1767 1767
                 esc_html__(
1768 1768
                     'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
@@ -1794,15 +1794,15 @@  discard block
 block discarded – undo
1794 1794
         // 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
1795 1795
         $help_array   = $this->_get_help_content();
1796 1796
         $help_content = '';
1797
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1798
-            $help_array[ $trigger_id ] = [
1797
+        if (empty($help_array) || ! isset($help_array[$trigger_id])) {
1798
+            $help_array[$trigger_id] = [
1799 1799
                 'title'   => esc_html__('Missing Content', 'event_espresso'),
1800 1800
                 'content' => esc_html__(
1801 1801
                     '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.)',
1802 1802
                     'event_espresso'
1803 1803
                 ),
1804 1804
             ];
1805
-            $help_content              = $this->_set_help_popup_content($help_array);
1805
+            $help_content = $this->_set_help_popup_content($help_array);
1806 1806
         }
1807 1807
         $height   = esc_attr($dimensions[0]) ?? 400;
1808 1808
         $width    = esc_attr($dimensions[1]) ?? 640;
@@ -1890,7 +1890,7 @@  discard block
 block discarded – undo
1890 1890
 
1891 1891
         add_filter(
1892 1892
             'admin_body_class',
1893
-            function ($classes) {
1893
+            function($classes) {
1894 1894
                 if (strpos($classes, 'espresso-admin') === false) {
1895 1895
                     $classes .= ' espresso-admin';
1896 1896
                 }
@@ -1981,12 +1981,12 @@  discard block
 block discarded – undo
1981 1981
     protected function _set_list_table()
1982 1982
     {
1983 1983
         // first is this a list_table view?
1984
-        if (! isset($this->_route_config['list_table'])) {
1984
+        if ( ! isset($this->_route_config['list_table'])) {
1985 1985
             return;
1986 1986
         } //not a list_table view so get out.
1987 1987
         // list table functions are per view specific (because some admin pages might have more than one list table!)
1988
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
1989
-        if (! method_exists($this, $list_table_view)) {
1988
+        $list_table_view = '_set_list_table_views_'.$this->_req_action;
1989
+        if ( ! method_exists($this, $list_table_view)) {
1990 1990
             // user error msg
1991 1991
             $error_msg = esc_html__(
1992 1992
                 'An error occurred. The requested list table views could not be found.',
@@ -2007,10 +2007,10 @@  discard block
 block discarded – undo
2007 2007
         $this->{$list_table_view}();
2008 2008
         // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2009 2009
         $this->_views = apply_filters(
2010
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2010
+            'FHEE_list_table_views_'.$this->page_slug.'_'.$this->_req_action,
2011 2011
             $this->_views
2012 2012
         );
2013
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2013
+        $this->_views = apply_filters('FHEE_list_table_views_'.$this->page_slug, $this->_views);
2014 2014
         $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2015 2015
         $this->_set_list_table_view();
2016 2016
         $this->_set_list_table_object();
@@ -2045,7 +2045,7 @@  discard block
 block discarded – undo
2045 2045
     protected function _set_list_table_object()
2046 2046
     {
2047 2047
         if (isset($this->_route_config['list_table'])) {
2048
-            if (! class_exists($this->_route_config['list_table'])) {
2048
+            if ( ! class_exists($this->_route_config['list_table'])) {
2049 2049
                 throw new EE_Error(
2050 2050
                     sprintf(
2051 2051
                         esc_html__(
@@ -2088,19 +2088,19 @@  discard block
 block discarded – undo
2088 2088
         // cycle thru views
2089 2089
         foreach ($this->_views as $key => $view) {
2090 2090
             $query_args = [];
2091
-            if ( ! isset($this->_views[ $key ]['class'])) {
2092
-                $this->_views[ $key ]['class'] = '';
2091
+            if ( ! isset($this->_views[$key]['class'])) {
2092
+                $this->_views[$key]['class'] = '';
2093 2093
             }
2094 2094
             // check for current view
2095
-            $this->_views[ $key ]['class'] .= $this->_view === $view['slug'] ? ' current' : '';
2095
+            $this->_views[$key]['class'] .= $this->_view === $view['slug'] ? ' current' : '';
2096 2096
             $query_args['action']          = $this->_req_action;
2097
-            $query_args[ $action_nonce ]   = $nonce;
2097
+            $query_args[$action_nonce]   = $nonce;
2098 2098
             $query_args['status']          = $view['slug'];
2099 2099
             // merge any other arguments sent in.
2100
-            if (isset($extra_query_args[ $view['slug'] ])) {
2101
-                $query_args = array_merge($query_args, $extra_query_args[ $view['slug'] ]);
2100
+            if (isset($extra_query_args[$view['slug']])) {
2101
+                $query_args = array_merge($query_args, $extra_query_args[$view['slug']]);
2102 2102
             }
2103
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2103
+            $this->_views[$key]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2104 2104
         }
2105 2105
         return $this->_views;
2106 2106
     }
@@ -2129,14 +2129,14 @@  discard block
 block discarded – undo
2129 2129
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2130 2130
         foreach ($values as $value) {
2131 2131
             if ($value < $max_entries) {
2132
-                $selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2132
+                $selected = $value === $per_page ? ' selected="'.$per_page.'"' : '';
2133 2133
                 $entries_per_page_dropdown .= '
2134
-						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2134
+						<option value="' . $value.'"'.$selected.'>'.$value.'&nbsp;&nbsp;</option>';
2135 2135
             }
2136 2136
         }
2137
-        $selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2137
+        $selected = $max_entries === $per_page ? ' selected="'.$per_page.'"' : '';
2138 2138
         $entries_per_page_dropdown .= '
2139
-						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2139
+						<option value="' . $max_entries.'"'.$selected.'>All&nbsp;&nbsp;</option>';
2140 2140
         $entries_per_page_dropdown .= '
2141 2141
 					</select>
2142 2142
 					entries
@@ -2160,7 +2160,7 @@  discard block
 block discarded – undo
2160 2160
             empty($this->_search_btn_label) ? $this->page_label
2161 2161
                 : $this->_search_btn_label
2162 2162
         );
2163
-        $this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2163
+        $this->_template_args['search']['callback'] = 'search_'.$this->page_slug;
2164 2164
     }
2165 2165
 
2166 2166
 
@@ -2221,7 +2221,7 @@  discard block
 block discarded – undo
2221 2221
                                   );
2222 2222
                     throw new EE_Error($error_msg);
2223 2223
                 }
2224
-                unset($this->_route_config['metaboxes'][ $key ]);
2224
+                unset($this->_route_config['metaboxes'][$key]);
2225 2225
             }
2226 2226
         }
2227 2227
     }
@@ -2254,7 +2254,7 @@  discard block
 block discarded – undo
2254 2254
             $total_columns                                       = ! empty($screen_columns)
2255 2255
                 ? $screen_columns
2256 2256
                 : $this->_route_config['columns'][1];
2257
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2257
+            $this->_template_args['current_screen_widget_class'] = 'columns-'.$total_columns;
2258 2258
             $this->_template_args['current_page']                = $this->_wp_page_slug;
2259 2259
             $this->_template_args['screen']                      = $this->_current_screen;
2260 2260
             $this->_column_template_path                         = EE_ADMIN_TEMPLATE
@@ -2300,7 +2300,7 @@  discard block
 block discarded – undo
2300 2300
      */
2301 2301
     protected function _espresso_ratings_request()
2302 2302
     {
2303
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2303
+        if ( ! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2304 2304
             return;
2305 2305
         }
2306 2306
         $ratings_box_title = apply_filters(
@@ -2327,28 +2327,28 @@  discard block
 block discarded – undo
2327 2327
      */
2328 2328
     public function espresso_ratings_request()
2329 2329
     {
2330
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2330
+        EEH_Template::display_template(EE_ADMIN_TEMPLATE.'espresso_ratings_request_content.template.php');
2331 2331
     }
2332 2332
 
2333 2333
 
2334 2334
     public static function cached_rss_display(string $rss_id, string $url): bool
2335 2335
     {
2336
-        $loading   = '<p class="widget-loading hide-if-no-js">'
2336
+        $loading = '<p class="widget-loading hide-if-no-js">'
2337 2337
                      . esc_html__('Loading&#8230;', 'event_espresso')
2338 2338
                      . '</p><p class="hide-if-js">'
2339 2339
                      . esc_html__('This widget requires JavaScript.', 'event_espresso')
2340 2340
                      . '</p>';
2341
-        $pre       = '<div class="espresso-rss-display">' . "\n\t";
2342
-        $pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2343
-        $post      = '</div>' . "\n";
2344
-        $cache_key = 'ee_rss_' . md5($rss_id);
2341
+        $pre       = '<div class="espresso-rss-display">'."\n\t";
2342
+        $pre .= '<span id="'.esc_attr($rss_id).'_url" class="hidden">'.esc_url_raw($url).'</span>';
2343
+        $post      = '</div>'."\n";
2344
+        $cache_key = 'ee_rss_'.md5($rss_id);
2345 2345
         $output    = get_transient($cache_key);
2346 2346
         if ($output !== false) {
2347
-            echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2347
+            echo wp_kses($pre.$output.$post, AllowedTags::getWithFormTags());
2348 2348
             return true;
2349 2349
         }
2350
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2351
-            echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2350
+        if ( ! (defined('DOING_AJAX') && DOING_AJAX)) {
2351
+            echo wp_kses($pre.$loading.$post, AllowedTags::getWithFormTags());
2352 2352
             return false;
2353 2353
         }
2354 2354
         ob_start();
@@ -2415,7 +2415,7 @@  discard block
 block discarded – undo
2415 2415
     public function espresso_sponsors_post_box()
2416 2416
     {
2417 2417
         EEH_Template::display_template(
2418
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2418
+            EE_ADMIN_TEMPLATE.'admin_general_metabox_contents_espresso_sponsors.template.php'
2419 2419
         );
2420 2420
     }
2421 2421
 
@@ -2432,9 +2432,9 @@  discard block
 block discarded – undo
2432 2432
     protected function getPublishBoxTitle(): string
2433 2433
     {
2434 2434
         $publish_box_title = esc_html__('Publish', 'event_espresso');
2435
-        if (! empty($this->_labels['publishbox'])) {
2435
+        if ( ! empty($this->_labels['publishbox'])) {
2436 2436
             if (is_array($this->_labels['publishbox'])) {
2437
-                $publish_box_title = $this->_labels['publishbox'][ $this->_req_action ] ?? $publish_box_title;
2437
+                $publish_box_title = $this->_labels['publishbox'][$this->_req_action] ?? $publish_box_title;
2438 2438
             } else {
2439 2439
                 $publish_box_title = $this->_labels['publishbox'];
2440 2440
             }
@@ -2484,7 +2484,7 @@  discard block
 block discarded – undo
2484 2484
         // if we have extra content set let's add it in if not make sure its empty
2485 2485
         $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2486 2486
         echo EEH_Template::display_template(
2487
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2487
+            EE_ADMIN_TEMPLATE.'admin_details_publish_metabox.template.php',
2488 2488
             $this->_template_args,
2489 2489
             true
2490 2490
         );
@@ -2537,10 +2537,10 @@  discard block
 block discarded – undo
2537 2537
                 'submitdelete deletion button button--outline button--caution'
2538 2538
             );
2539 2539
         }
2540
-        if (! isset($this->_template_args['publish_delete_link'])) {
2540
+        if ( ! isset($this->_template_args['publish_delete_link'])) {
2541 2541
             $this->_template_args['publish_delete_link'] = '';
2542 2542
         }
2543
-        if (! empty($name) && ! empty($id)) {
2543
+        if ( ! empty($name) && ! empty($id)) {
2544 2544
             $this->addPublishPostMetaBoxHiddenFields($name, ['type' => 'hidden', 'value' => $id]);
2545 2545
         }
2546 2546
         $hidden_fields = $this->_generate_admin_form_fields($this->publish_post_meta_box_hidden_fields, 'array');
@@ -2573,7 +2573,7 @@  discard block
 block discarded – undo
2573 2573
 
2574 2574
     protected function addPublishPostMetaBoxHiddenFields(string $field_name, array $field_attributes)
2575 2575
     {
2576
-        $this->publish_post_meta_box_hidden_fields[ $field_name ] = $field_attributes;
2576
+        $this->publish_post_meta_box_hidden_fields[$field_name] = $field_attributes;
2577 2577
     }
2578 2578
 
2579 2579
 
@@ -2675,7 +2675,7 @@  discard block
 block discarded – undo
2675 2675
         }
2676 2676
         // 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)
2677 2677
         $call_back_func = $create_func
2678
-            ? static function ($post, $metabox) {
2678
+            ? static function($post, $metabox) {
2679 2679
                 echo EEH_Template::display_template(
2680 2680
                     $metabox['args']['template_path'],
2681 2681
                     $metabox['args']['template_args'],
@@ -2684,7 +2684,7 @@  discard block
 block discarded – undo
2684 2684
             }
2685 2685
             : $callback;
2686 2686
         $this->addMetaBox(
2687
-            str_replace('_', '-', $action) . '-mbox',
2687
+            str_replace('_', '-', $action).'-mbox',
2688 2688
             $title,
2689 2689
             $call_back_func,
2690 2690
             $this->_wp_page_slug,
@@ -2800,15 +2800,15 @@  discard block
 block discarded – undo
2800 2800
             'event-espresso_page_espresso_',
2801 2801
             '',
2802 2802
             $this->_wp_page_slug
2803
-        ) . ' ' . $this->_req_action . '-route';
2803
+        ).' '.$this->_req_action.'-route';
2804 2804
 
2805 2805
         $template_path = $sidebar
2806 2806
             ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2807
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2807
+            : EE_ADMIN_TEMPLATE.'admin_details_wrapper_no_sidebar.template.php';
2808 2808
 
2809 2809
         $this->_template_args['is_ajax'] = $this->request->isAjax();
2810 2810
         if ($this->request->isAjax()) {
2811
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2811
+            $template_path = EE_ADMIN_TEMPLATE.'admin_details_wrapper_no_sidebar_ajax.template.php';
2812 2812
         }
2813 2813
         $template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2814 2814
 
@@ -2820,10 +2820,10 @@  discard block
 block discarded – undo
2820 2820
         // to prevent WooCommerce from blowing things up if not using CPT
2821 2821
         global $post_type, $post;
2822 2822
         $this->_template_args['post_type'] = $post_type ?? '';
2823
-        $this->_template_args['post']  = $post ?? new WP_Post((object) [ 'ID' => 0, 'filter' => 'raw' ]);
2823
+        $this->_template_args['post'] = $post ?? new WP_Post((object) ['ID' => 0, 'filter' => 'raw']);
2824 2824
 
2825 2825
         $this->_template_args['post_body_content'] = EEH_Template::display_template(
2826
-            EE_ADMIN_TEMPLATE . 'admin_details_wrapper_post_body_content.template.php',
2826
+            EE_ADMIN_TEMPLATE.'admin_details_wrapper_post_body_content.template.php',
2827 2827
             $this->_template_args,
2828 2828
             true
2829 2829
         );
@@ -2855,11 +2855,11 @@  discard block
 block discarded – undo
2855 2855
     public function display_admin_caf_preview_page(string $utm_campaign_source = '', bool $display_sidebar = true)
2856 2856
     {
2857 2857
         // let's generate a default preview action button if there isn't one already present.
2858
-        $this->_labels['buttons']['buy_now']           = esc_html__(
2858
+        $this->_labels['buttons']['buy_now'] = esc_html__(
2859 2859
             'Upgrade to Event Espresso 4 Right Now',
2860 2860
             'event_espresso'
2861 2861
         );
2862
-        $buy_now_url                                   = add_query_arg(
2862
+        $buy_now_url = add_query_arg(
2863 2863
             [
2864 2864
                 'ee_ver'       => 'ee4',
2865 2865
                 'utm_source'   => 'ee4_plugin_admin',
@@ -2879,8 +2879,8 @@  discard block
 block discarded – undo
2879 2879
                 true
2880 2880
             )
2881 2881
             : $this->_template_args['preview_action_button'];
2882
-        $this->_template_args['admin_page_content']    = EEH_Template::display_template(
2883
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2882
+        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2883
+            EE_ADMIN_TEMPLATE.'admin_caf_full_page_preview.template.php',
2884 2884
             $this->_template_args,
2885 2885
             true
2886 2886
         );
@@ -2938,7 +2938,7 @@  discard block
 block discarded – undo
2938 2938
         // setup search attributes
2939 2939
         $this->_set_search_attributes();
2940 2940
         $this->_template_args['current_page']     = $this->_wp_page_slug;
2941
-        $template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2941
+        $template_path                            = EE_ADMIN_TEMPLATE.'admin_list_wrapper.template.php';
2942 2942
         $this->_template_args['table_url']        = $this->request->isAjax()
2943 2943
             ? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2944 2944
             : add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
@@ -2946,10 +2946,10 @@  discard block
 block discarded – undo
2946 2946
         $this->_template_args['current_route']    = $this->_req_action;
2947 2947
         $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2948 2948
         $ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2949
-        if (! empty($ajax_sorting_callback)) {
2949
+        if ( ! empty($ajax_sorting_callback)) {
2950 2950
             $sortable_list_table_form_fields = wp_nonce_field(
2951
-                $ajax_sorting_callback . '_nonce',
2952
-                $ajax_sorting_callback . '_nonce',
2951
+                $ajax_sorting_callback.'_nonce',
2952
+                $ajax_sorting_callback.'_nonce',
2953 2953
                 false,
2954 2954
                 false
2955 2955
             );
@@ -2966,18 +2966,18 @@  discard block
 block discarded – undo
2966 2966
 
2967 2967
         $hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2968 2968
 
2969
-        $nonce_ref          = $this->_req_action . '_nonce';
2969
+        $nonce_ref          = $this->_req_action.'_nonce';
2970 2970
         $hidden_form_fields .= '
2971
-            <input type="hidden" name="' . $nonce_ref . '" value="' . wp_create_nonce($nonce_ref) . '">';
2971
+            <input type="hidden" name="' . $nonce_ref.'" value="'.wp_create_nonce($nonce_ref).'">';
2972 2972
 
2973 2973
         $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2974 2974
         // display message about search results?
2975
-        $search                                    = $this->request->getRequestParam('s');
2975
+        $search = $this->request->getRequestParam('s');
2976 2976
         $this->_template_args['before_list_table'] .= ! empty($search)
2977
-            ? '<p class="ee-search-results">' . sprintf(
2977
+            ? '<p class="ee-search-results">'.sprintf(
2978 2978
                 esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2979 2979
                 trim($search, '%')
2980
-            ) . '</p>'
2980
+            ).'</p>'
2981 2981
             : '';
2982 2982
         // filter before_list_table template arg
2983 2983
         $this->_template_args['before_list_table'] = apply_filters(
@@ -3011,7 +3011,7 @@  discard block
 block discarded – undo
3011 3011
         // convert to array and filter again
3012 3012
         // arrays are easier to inject new items in a specific location,
3013 3013
         // but would not be backwards compatible, so we have to add a new filter
3014
-        $this->_template_args['after_list_table']   = implode(
3014
+        $this->_template_args['after_list_table'] = implode(
3015 3015
             " \n",
3016 3016
             (array) apply_filters(
3017 3017
                 'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
@@ -3066,7 +3066,7 @@  discard block
 block discarded – undo
3066 3066
             $this->page_slug
3067 3067
         );
3068 3068
         return EEH_Template::display_template(
3069
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3069
+            EE_ADMIN_TEMPLATE.'admin_details_legend.template.php',
3070 3070
             $this->_template_args,
3071 3071
             true
3072 3072
         );
@@ -3189,7 +3189,7 @@  discard block
 block discarded – undo
3189 3189
         if ($this->request->isAjax()) {
3190 3190
             $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3191 3191
             // $template_path,
3192
-                EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3192
+                EE_ADMIN_TEMPLATE.'admin_wrapper_ajax.template.php',
3193 3193
                 $this->_template_args,
3194 3194
                 true
3195 3195
             );
@@ -3198,7 +3198,7 @@  discard block
 block discarded – undo
3198 3198
         // load settings page wrapper template
3199 3199
         $template_path = $about
3200 3200
             ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3201
-            : EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3201
+            : EE_ADMIN_TEMPLATE.'admin_wrapper.template.php';
3202 3202
 
3203 3203
         EEH_Template::display_template($template_path, $this->_template_args);
3204 3204
     }
@@ -3282,12 +3282,12 @@  discard block
 block discarded – undo
3282 3282
         $default_names = ['save', 'save_and_close'];
3283 3283
         $buttons       = '';
3284 3284
         foreach ($button_text as $key => $button) {
3285
-            $ref     = $default_names[ $key ];
3286
-            $name    = ! empty($actions) ? $actions[ $key ] : $ref;
3287
-            $buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3288
-                        . 'value="' . $button . '" name="' . $name . '" '
3289
-                        . 'id="' . $this->_current_view . '_' . $ref . '" />';
3290
-            if (! $both) {
3285
+            $ref     = $default_names[$key];
3286
+            $name    = ! empty($actions) ? $actions[$key] : $ref;
3287
+            $buttons .= '<input type="submit" class="button button--primary '.$ref.'" '
3288
+                        . 'value="'.$button.'" name="'.$name.'" '
3289
+                        . 'id="'.$this->_current_view.'_'.$ref.'" />';
3290
+            if ( ! $both) {
3291 3291
                 break;
3292 3292
             }
3293 3293
         }
@@ -3328,13 +3328,13 @@  discard block
 block discarded – undo
3328 3328
                 'An error occurred. No action was set for this page\'s form.',
3329 3329
                 'event_espresso'
3330 3330
             );
3331
-            $dev_msg  = $user_msg . "\n"
3331
+            $dev_msg = $user_msg."\n"
3332 3332
                         . sprintf(
3333 3333
                             esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3334 3334
                             __FUNCTION__,
3335 3335
                             __CLASS__
3336 3336
                         );
3337
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3337
+            EE_Error::add_error($user_msg.'||'.$dev_msg, __FILE__, __FUNCTION__, __LINE__);
3338 3338
         }
3339 3339
         // open form
3340 3340
         $action                                            = $this->_admin_base_url;
@@ -3342,9 +3342,9 @@  discard block
 block discarded – undo
3342 3342
             <form name='form' method='post' action='$action' id='{$route}_event_form' class='ee-admin-page-form' >
3343 3343
             ";
3344 3344
         // add nonce
3345
-        $nonce                                             =
3346
-            wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3347
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3345
+        $nonce =
3346
+            wp_nonce_field($route.'_nonce', $route.'_nonce', false, false);
3347
+        $this->_template_args['before_admin_page_content'] .= "\n\t".$nonce;
3348 3348
         // add REQUIRED form action
3349 3349
         $hidden_fields = [
3350 3350
             'action' => ['type' => 'hidden', 'value' => $route],
@@ -3357,7 +3357,7 @@  discard block
 block discarded – undo
3357 3357
         $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3358 3358
         // add fields to form
3359 3359
         foreach ((array) $form_fields as $form_field) {
3360
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3360
+            $this->_template_args['before_admin_page_content'] .= "\n\t".$form_field['field'];
3361 3361
         }
3362 3362
         // close form
3363 3363
         $this->_template_args['after_admin_page_content'] = '</form>';
@@ -3440,10 +3440,10 @@  discard block
 block discarded – undo
3440 3440
     ) {
3441 3441
         $notices = EE_Error::get_notices(false);
3442 3442
         // overwrite default success messages //BUT ONLY if overwrite not overridden
3443
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3443
+        if ( ! $override_overwrite || ! empty($notices['errors'])) {
3444 3444
             EE_Error::overwrite_success();
3445 3445
         }
3446
-        if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3446
+        if ( ! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3447 3447
             // how many records affected ? more than one record ? or just one ?
3448 3448
             EE_Error::add_success(
3449 3449
                 sprintf(
@@ -3494,7 +3494,7 @@  discard block
 block discarded – undo
3494 3494
             $redirect_url = admin_url('admin.php');
3495 3495
         }
3496 3496
         // merge any default query_args set in _default_route_query_args property
3497
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3497
+        if ( ! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3498 3498
             $args_to_merge = [];
3499 3499
             foreach ($this->_default_route_query_args as $query_param => $query_value) {
3500 3500
                 // is there a wp_referer array in our _default_route_query_args property?
@@ -3506,15 +3506,15 @@  discard block
 block discarded – undo
3506 3506
                         }
3507 3507
                         // finally we will override any arguments in the referer with
3508 3508
                         // what might be set on the _default_route_query_args array.
3509
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3510
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3509
+                        if (isset($this->_default_route_query_args[$reference])) {
3510
+                            $args_to_merge[$reference] = urlencode($this->_default_route_query_args[$reference]);
3511 3511
                         } else {
3512
-                            $args_to_merge[ $reference ] = urlencode($value);
3512
+                            $args_to_merge[$reference] = urlencode($value);
3513 3513
                         }
3514 3514
                     }
3515 3515
                     continue;
3516 3516
                 }
3517
-                $args_to_merge[ $query_param ] = $query_value;
3517
+                $args_to_merge[$query_param] = $query_value;
3518 3518
             }
3519 3519
             // now let's merge these arguments but override with what was specifically sent in to the
3520 3520
             // redirect.
@@ -3526,19 +3526,19 @@  discard block
 block discarded – undo
3526 3526
         if (isset($query_args['action'])) {
3527 3527
             // manually generate wp_nonce and merge that with the query vars
3528 3528
             // becuz the wp_nonce_url function wrecks havoc on some vars
3529
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3529
+            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'].'_nonce');
3530 3530
         }
3531 3531
         // we're adding some hooks and filters in here for processing any things just before redirects
3532 3532
         // (example: an admin page has done an insert or update and we want to run something after that).
3533
-        do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3533
+        do_action('AHEE_redirect_'.$this->class_name.$this->_req_action, $query_args);
3534 3534
         $redirect_url = apply_filters(
3535
-            'FHEE_redirect_' . $this->class_name . $this->_req_action,
3535
+            'FHEE_redirect_'.$this->class_name.$this->_req_action,
3536 3536
             EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3537 3537
             $query_args
3538 3538
         );
3539 3539
         // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3540 3540
         if ($this->request->isAjax()) {
3541
-            $default_data                    = [
3541
+            $default_data = [
3542 3542
                 'close'        => true,
3543 3543
                 'redirect_url' => $redirect_url,
3544 3544
                 'where'        => 'main',
@@ -3591,7 +3591,7 @@  discard block
 block discarded – undo
3591 3591
         }
3592 3592
         $this->_template_args['notices'] = EE_Error::get_notices();
3593 3593
         // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3594
-        if (! $this->request->isAjax() || $sticky_notices) {
3594
+        if ( ! $this->request->isAjax() || $sticky_notices) {
3595 3595
             $route = $query_args['action'] ?? 'default';
3596 3596
             $this->_add_transient(
3597 3597
                 $route,
@@ -3631,7 +3631,7 @@  discard block
 block discarded – undo
3631 3631
         bool $exclude_nonce = false
3632 3632
     ): string {
3633 3633
         // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3634
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3634
+        if (empty($base_url) && ! isset($this->_page_routes[$action])) {
3635 3635
             throw new EE_Error(
3636 3636
                 sprintf(
3637 3637
                     esc_html__(
@@ -3642,7 +3642,7 @@  discard block
 block discarded – undo
3642 3642
                 )
3643 3643
             );
3644 3644
         }
3645
-        if (! isset($this->_labels['buttons'][ $type ])) {
3645
+        if ( ! isset($this->_labels['buttons'][$type])) {
3646 3646
             throw new EE_Error(
3647 3647
                 sprintf(
3648 3648
                     esc_html__(
@@ -3655,7 +3655,7 @@  discard block
 block discarded – undo
3655 3655
         }
3656 3656
         // finally check user access for this button.
3657 3657
         $has_access = $this->check_user_access($action, true);
3658
-        if (! $has_access) {
3658
+        if ( ! $has_access) {
3659 3659
             return '';
3660 3660
         }
3661 3661
         $_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
@@ -3663,11 +3663,11 @@  discard block
 block discarded – undo
3663 3663
             'action' => $action,
3664 3664
         ];
3665 3665
         // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3666
-        if (! empty($extra_request)) {
3666
+        if ( ! empty($extra_request)) {
3667 3667
             $query_args = array_merge($extra_request, $query_args);
3668 3668
         }
3669 3669
         $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3670
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3670
+        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][$type], $class);
3671 3671
     }
3672 3672
 
3673 3673
 
@@ -3693,7 +3693,7 @@  discard block
 block discarded – undo
3693 3693
                 'FHEE__EE_Admin_Page___per_page_screen_options__default',
3694 3694
                 20
3695 3695
             ),
3696
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3696
+            'option'  => $this->_current_page.'_'.$this->_current_view.'_per_page',
3697 3697
         ];
3698 3698
         // ONLY add the screen option if the user has access to it.
3699 3699
         if ($this->check_user_access($this->_current_view, true)) {
@@ -3714,18 +3714,18 @@  discard block
 block discarded – undo
3714 3714
     {
3715 3715
         if ($this->request->requestParamIsSet('wp_screen_options')) {
3716 3716
             check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3717
-            if (! $user = wp_get_current_user()) {
3717
+            if ( ! $user = wp_get_current_user()) {
3718 3718
                 return;
3719 3719
             }
3720 3720
             $option = $this->request->getRequestParam('wp_screen_options[option]', '', DataType::KEY);
3721
-            if (! $option) {
3721
+            if ( ! $option) {
3722 3722
                 return;
3723 3723
             }
3724 3724
             $value      = $this->request->getRequestParam('wp_screen_options[value]', 0, DataType::INT);
3725 3725
             $map_option = $option;
3726 3726
             $option     = str_replace('-', '_', $option);
3727 3727
             switch ($map_option) {
3728
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3728
+                case $this->_current_page.'_'.$this->_current_view.'_per_page':
3729 3729
                     $max_value = apply_filters(
3730 3730
                         'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3731 3731
                         999,
@@ -3792,7 +3792,7 @@  discard block
 block discarded – undo
3792 3792
         bool $skip_route_verify = false
3793 3793
     ) {
3794 3794
         $user_id = get_current_user_id();
3795
-        if (! $skip_route_verify) {
3795
+        if ( ! $skip_route_verify) {
3796 3796
             $this->_verify_route($route);
3797 3797
         }
3798 3798
         // now let's set the string for what kind of transient we're setting
@@ -3825,8 +3825,8 @@  discard block
 block discarded – undo
3825 3825
         $user_id   = get_current_user_id();
3826 3826
         $route     = ! $route ? $this->_req_action : $route;
3827 3827
         $transient = $notices
3828
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3829
-            : 'rte_tx_' . $route . '_' . $user_id;
3828
+            ? 'ee_rte_n_tx_'.$route.'_'.$user_id
3829
+            : 'rte_tx_'.$route.'_'.$user_id;
3830 3830
         $data      = is_multisite() && is_network_admin()
3831 3831
             ? get_site_transient($transient)
3832 3832
             : get_transient($transient);
@@ -4086,7 +4086,7 @@  discard block
 block discarded – undo
4086 4086
      */
4087 4087
     protected function _next_link(string $url, string $class = 'dashicons dashicons-arrow-right'): string
4088 4088
     {
4089
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4089
+        return '<a class="'.$class.'" href="'.$url.'"></a>';
4090 4090
     }
4091 4091
 
4092 4092
 
@@ -4099,7 +4099,7 @@  discard block
 block discarded – undo
4099 4099
      */
4100 4100
     protected function _previous_link(string $url, string $class = 'dashicons dashicons-arrow-left'): string
4101 4101
     {
4102
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4102
+        return '<a class="'.$class.'" href="'.$url.'"></a>';
4103 4103
     }
4104 4104
 
4105 4105
 
@@ -4247,13 +4247,13 @@  discard block
 block discarded – undo
4247 4247
         ?callable $callback = null
4248 4248
     ): bool {
4249 4249
         $entity_ID = absint($entity_ID);
4250
-        if (! $entity_ID) {
4250
+        if ( ! $entity_ID) {
4251 4251
             $this->trashRestoreDeleteError($action, $entity_model);
4252 4252
         }
4253 4253
         $result = 0;
4254 4254
         try {
4255 4255
             $entity = $entity_model->get_one_by_ID($entity_ID);
4256
-            if (! $entity instanceof EE_Base_Class) {
4256
+            if ( ! $entity instanceof EE_Base_Class) {
4257 4257
                 throw new DomainException(
4258 4258
                     sprintf(
4259 4259
                         esc_html__(
@@ -4304,7 +4304,7 @@  discard block
 block discarded – undo
4304 4304
                 )
4305 4305
             );
4306 4306
         }
4307
-        if (! $entity_model->has_field($delete_column)) {
4307
+        if ( ! $entity_model->has_field($delete_column)) {
4308 4308
             throw new DomainException(
4309 4309
                 sprintf(
4310 4310
                     esc_html__(
Please login to merge, or discard this patch.
core/admin/EE_Admin.core.php 1 patch
Indentation   +990 added lines, -990 removed lines patch added patch discarded remove patch
@@ -24,994 +24,994 @@
 block discarded – undo
24 24
  */
25 25
 final class EE_Admin implements InterminableInterface
26 26
 {
27
-    private static ?EE_Admin $_instance = null;
28
-
29
-    private ?PersistentAdminNoticeManager $persistent_admin_notice_manager = null;
30
-
31
-    protected LoaderInterface $loader;
32
-
33
-    protected RequestInterface $request;
34
-
35
-
36
-    /**
37
-     * @singleton method used to instantiate class object
38
-     * @param LoaderInterface|null  $loader
39
-     * @param RequestInterface|null $request
40
-     * @return EE_Admin|null
41
-     * @throws EE_Error
42
-     */
43
-    public static function instance(?LoaderInterface $loader = null, ?RequestInterface $request = null): ?EE_Admin
44
-    {
45
-        // check if class object is instantiated
46
-        if (! EE_Admin::$_instance instanceof EE_Admin) {
47
-            EE_Admin::$_instance = new EE_Admin($loader, $request);
48
-        }
49
-        return EE_Admin::$_instance;
50
-    }
51
-
52
-
53
-    /**
54
-     * @return EE_Admin|null
55
-     * @throws EE_Error
56
-     */
57
-    public static function reset(): ?EE_Admin
58
-    {
59
-        EE_Admin::$_instance = null;
60
-        $loader = LoaderFactory::getLoader();
61
-        $request = $loader->getShared('EventEspresso\core\services\request\Request');
62
-        return EE_Admin::instance($loader, $request);
63
-    }
64
-
65
-
66
-    /**
67
-     * @param LoaderInterface  $loader
68
-     * @param RequestInterface $request
69
-     * @throws EE_Error
70
-     * @throws InvalidDataTypeException
71
-     * @throws InvalidInterfaceException
72
-     * @throws InvalidArgumentException
73
-     */
74
-    protected function __construct(LoaderInterface $loader, RequestInterface $request)
75
-    {
76
-        $this->loader = $loader;
77
-        $this->request = $request;
78
-        // define global EE_Admin constants
79
-        $this->_define_all_constants();
80
-        // set autoloaders for our admin page classes based on included path information
81
-        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(EE_ADMIN);
82
-        // reset Environment config (we only do this on admin page loads);
83
-        EE_Registry::instance()->CFG->environment->recheck_values();
84
-        // load EE_Request_Handler early
85
-        add_action('AHEE__EE_System__initialize_last', [$this, 'init']);
86
-        add_action('admin_init', [$this, 'admin_init'], 100);
87
-        if (! $this->request->isActivation() && ! $this->request->isAjax()) {
88
-            // admin hooks
89
-            add_action('admin_notices', [$this, 'display_admin_notices']);
90
-            add_action('network_admin_notices', [$this, 'display_admin_notices']);
91
-            add_filter('pre_update_option', [$this, 'check_for_invalid_datetime_formats'], 100, 2);
92
-            add_filter('plugin_action_links', [$this, 'filter_plugin_actions'], 10, 2);
93
-            add_filter('admin_footer_text', [$this, 'espresso_admin_footer']);
94
-            add_action('display_post_states', [$this, 'displayStateForCriticalPages'], 10, 2);
95
-            add_filter('plugin_row_meta', [$this, 'addLinksToPluginRowMeta'], 10, 2);
96
-        }
97
-        do_action('AHEE__EE_Admin__loaded');
98
-    }
99
-
100
-
101
-    /**
102
-     * _define_all_constants
103
-     * define constants that are set globally for all admin pages
104
-     *
105
-     * @return void
106
-     */
107
-    private function _define_all_constants()
108
-    {
109
-        if (! defined('EE_ADMIN_URL')) {
110
-            define('EE_ADMIN_URL', EE_PLUGIN_DIR_URL . 'core/admin/');
111
-            define('EE_ADMIN_PAGES_URL', EE_PLUGIN_DIR_URL . 'admin_pages/');
112
-            define('EE_ADMIN_TEMPLATE', EE_ADMIN . 'templates/');
113
-            define('WP_ADMIN_PATH', ABSPATH . 'wp-admin/');
114
-            define('WP_AJAX_URL', admin_url('admin-ajax.php'));
115
-        }
116
-    }
117
-
118
-
119
-    /**
120
-     * filter_plugin_actions - adds links to the Plugins page listing
121
-     *
122
-     * @param array  $links
123
-     * @param string $plugin
124
-     * @return    array
125
-     */
126
-    public function filter_plugin_actions($links, $plugin)
127
-    {
128
-        // set $main_file in stone
129
-        static $main_file;
130
-        // if $main_file is not set yet
131
-        if (! $main_file) {
132
-            $main_file = EE_PLUGIN_BASENAME;
133
-        }
134
-        if ($plugin === $main_file) {
135
-            // compare current plugin to this one
136
-            if (MaintenanceStatus::isFullSite()) {
137
-                $maintenance_link = '<a href="admin.php?page=espresso_maintenance_settings"'
138
-                                    . ' title="Event Espresso is in maintenance mode.  Click this link to learn why.">'
139
-                                    . esc_html__('Maintenance Mode Active', 'event_espresso')
140
-                                    . '</a>';
141
-                array_unshift($links, $maintenance_link);
142
-            } else {
143
-                $org_settings_link = '<a href="admin.php?page=espresso_general_settings">'
144
-                                     . esc_html__('Settings', 'event_espresso')
145
-                                     . '</a>';
146
-                $events_link       = '<a href="admin.php?page=espresso_events">'
147
-                                     . esc_html__('Events', 'event_espresso')
148
-                                     . '</a>';
149
-                // add before other links
150
-                array_unshift($links, $org_settings_link, $events_link);
151
-            }
152
-        }
153
-        return $links;
154
-    }
155
-
156
-
157
-    /**
158
-     * hide_admin_pages_except_maintenance_mode
159
-     *
160
-     * @param array $admin_page_folder_names
161
-     * @return array
162
-     */
163
-    public function hide_admin_pages_except_maintenance_mode($admin_page_folder_names = [])
164
-    {
165
-        return [
166
-            'maintenance' => EE_ADMIN_PAGES . 'maintenance/',
167
-            'about'       => EE_ADMIN_PAGES . 'about/',
168
-            'support'     => EE_ADMIN_PAGES . 'support/',
169
-        ];
170
-    }
171
-
172
-
173
-    /**
174
-     * init- should fire after shortcode, module,  addon, other plugin (default priority), and even
175
-     * EE_Front_Controller's init phases have run
176
-     *
177
-     * @return void
178
-     * @throws EE_Error
179
-     * @throws InvalidArgumentException
180
-     * @throws InvalidDataTypeException
181
-     * @throws InvalidInterfaceException
182
-     * @throws ReflectionException
183
-     * @throws ServiceNotFoundException
184
-     */
185
-    public function init()
186
-    {
187
-        // only enable most of the EE_Admin IF we're not in full maintenance mode
188
-        if (DbStatus::isOnline()) {
189
-            $this->initModelsReady();
190
-        }
191
-        // run the admin page factory but ONLY if:
192
-        // - it is a regular non ajax admin request
193
-        // - we are doing an ee admin ajax request
194
-        if ($this->request->isAdmin() || $this->request->isAdminAjax() || $this->request->isActivation()) {
195
-            try {
196
-                // this loads the controller for the admin pages which will setup routing etc
197
-                $admin_page_loader = $this->loader->getShared('EE_Admin_Page_Loader', [$this->loader]);
198
-                /** @var EE_Admin_Page_Loader $admin_page_loader */
199
-                $admin_page_loader->init();
200
-            } catch (EE_Error $e) {
201
-                $e->get_error();
202
-            }
203
-        }
204
-        if ($this->request->isAjax()) {
205
-            return;
206
-        }
207
-        add_filter('content_save_pre', [$this, 'its_eSpresso']);
208
-        // make sure our CPTs and custom taxonomy metaboxes get shown for first time users
209
-        add_action('admin_head', [$this, 'enable_hidden_ee_nav_menu_metaboxes']);
210
-        add_action('admin_head', [$this, 'register_custom_nav_menu_boxes']);
211
-        // exclude EE critical pages from all nav menus and wp_list_pages
212
-        add_filter('nav_menu_meta_box_object', [$this, 'remove_pages_from_nav_menu']);
213
-    }
214
-
215
-
216
-    /**
217
-     * Gets the loader (and if it wasn't previously set, sets it)
218
-     *
219
-     * @return LoaderInterface
220
-     * @throws InvalidArgumentException
221
-     * @throws InvalidDataTypeException
222
-     * @throws InvalidInterfaceException
223
-     */
224
-    protected function getLoader()
225
-    {
226
-        return $this->loader;
227
-    }
228
-
229
-
230
-    /**
231
-     * Method that's fired on admin requests (including admin ajax) but only when the models are usable
232
-     * (ie, the site isn't in maintenance mode)
233
-     *
234
-     * @return void
235
-     * @throws EE_Error
236
-     * @throws ReflectionException
237
-     * @since 4.9.63.p
238
-     */
239
-    protected function initModelsReady()
240
-    {
241
-        $page = $this->request->getRequestParam('page', '');
242
-        // ok so we want to enable the entire admin
243
-        $this->persistent_admin_notice_manager = $this->loader->getShared(PersistentAdminNoticeManager::class);
244
-        $this->persistent_admin_notice_manager->setReturnUrl(
245
-            EE_Admin_Page::add_query_args_and_nonce(
246
-                [
247
-                    'page'   => $page,
248
-                    'action' => $this->request->getRequestParam('action', ''),
249
-                ],
250
-                EE_ADMIN_URL
251
-            )
252
-        );
253
-        if ($page === 'pricing' || strpos($page, 'espresso') !== false) {
254
-            $this->persistent_admin_notice_manager->loadAdminNotices();
255
-        }
256
-        $this->maybeSetDatetimeWarningNotice();
257
-        // at a glance dashboard widget
258
-        add_filter('dashboard_glance_items', [$this, 'dashboard_glance_items']);
259
-        // filter for get_edit_post_link used on comments for custom post types
260
-        add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
261
-    }
262
-
263
-
264
-    /**
265
-     *    get_persistent_admin_notices
266
-     *
267
-     * @access    public
268
-     * @return void
269
-     * @throws EE_Error
270
-     * @throws InvalidArgumentException
271
-     * @throws InvalidDataTypeException
272
-     * @throws InvalidInterfaceException
273
-     * @throws ReflectionException
274
-     */
275
-    public function maybeSetDatetimeWarningNotice()
276
-    {
277
-        // add dismissible notice for datetime changes.  Only valid if site does not have a timezone_string set.
278
-        // @todo This needs to stay in core for a bit to catch anyone upgrading from a version without this to a version
279
-        // with this.  But after enough time (indeterminate at this point) we can just remove this notice.
280
-        // this was added with https://events.codebasehq.com/projects/event-espresso/tickets/10626
281
-        if (
282
-            apply_filters('FHEE__EE_Admin__maybeSetDatetimeWarningNotice', true)
283
-            && ! get_option('timezone_string')
284
-            && EEM_Event::instance()->count() > 0
285
-        ) {
286
-            new PersistentAdminNotice(
287
-                'datetime_fix_notice',
288
-                sprintf(
289
-                    esc_html__(
290
-                        '%1$sImportant announcement related to your install of Event Espresso%2$s: There are some changes made to your site that could affect how dates display for your events and other related items with dates and times.  Read more about it %3$shere%4$s. If your dates and times are displaying incorrectly (incorrect offset), you can fix it using the tool on %5$sthis page%4$s.',
291
-                        'event_espresso'
292
-                    ),
293
-                    '<strong>',
294
-                    '</strong>',
295
-                    '<a href="https://eventespresso.com/2017/08/important-upcoming-changes-dates-times">',
296
-                    '</a>',
297
-                    '<a href="' . EE_Admin_Page::add_query_args_and_nonce(
298
-                        [
299
-                            'page'   => 'espresso_maintenance_settings',
300
-                            'action' => 'datetime_tools',
301
-                        ],
302
-                        admin_url('admin.php')
303
-                    ) . '">'
304
-                ),
305
-                false,
306
-                'manage_options',
307
-                'datetime_fix_persistent_notice'
308
-            );
309
-        }
310
-    }
311
-
312
-
313
-    /**
314
-     * this simply hooks into the nav menu setup of pages metabox and makes sure that we remove EE critical pages from
315
-     * the list of options. the wp function "wp_nav_menu_item_post_type_meta_box" found in
316
-     * wp-admin/includes/nav-menu.php looks for the "_default_query" property on the post_type object and it uses that
317
-     * to override any queries found in the existing query for the given post type.  Note that _default_query is not a
318
-     * normal property on the post_type object.  It's found ONLY in this particular context.
319
-     *
320
-     * @param WP_Post $post_type WP post type object
321
-     * @return WP_Post
322
-     * @throws InvalidArgumentException
323
-     * @throws InvalidDataTypeException
324
-     * @throws InvalidInterfaceException
325
-     */
326
-    public function remove_pages_from_nav_menu($post_type)
327
-    {
328
-        // if this isn't the "pages" post type let's get out
329
-        if ($post_type->name !== 'page') {
330
-            return $post_type;
331
-        }
332
-        $critical_pages            = EE_Registry::instance()->CFG->core->get_critical_pages_array();
333
-        $post_type->_default_query = [
334
-            'post__not_in' => $critical_pages,
335
-        ];
336
-        return $post_type;
337
-    }
338
-
339
-
340
-    /**
341
-     * WP by default only shows three metaboxes in "nav-menus.php" for first times users.
342
-     * We want to make sure our metaboxes get shown as well
343
-     *
344
-     * @return void
345
-     */
346
-    public function enable_hidden_ee_nav_menu_metaboxes()
347
-    {
348
-        global $wp_meta_boxes, $pagenow;
349
-        if (! is_array($wp_meta_boxes) || $pagenow !== 'nav-menus.php') {
350
-            return;
351
-        }
352
-        $user = wp_get_current_user();
353
-        // has this been done yet?
354
-        if (get_user_option('ee_nav_menu_initialized', $user->ID)) {
355
-            return;
356
-        }
357
-
358
-        $hidden_meta_boxes  = get_user_option('metaboxhidden_nav-menus', $user->ID);
359
-        $initial_meta_boxes = apply_filters(
360
-            'FHEE__EE_Admin__enable_hidden_ee_nav_menu_boxes__initial_meta_boxes',
361
-            [
362
-                'nav-menu-theme-locations',
363
-                'add-page',
364
-                'add-custom-links',
365
-                'add-category',
366
-                'add-espresso_events',
367
-                'add-espresso_venues',
368
-                'add-espresso_event_categories',
369
-                'add-espresso_venue_categories',
370
-                'add-post-type-post',
371
-                'add-post-type-page',
372
-            ]
373
-        );
374
-
375
-        if (is_array($hidden_meta_boxes)) {
376
-            foreach ($hidden_meta_boxes as $key => $meta_box_id) {
377
-                if (in_array($meta_box_id, $initial_meta_boxes, true)) {
378
-                    unset($hidden_meta_boxes[ $key ]);
379
-                }
380
-            }
381
-        }
382
-        update_user_option($user->ID, 'metaboxhidden_nav-menus', $hidden_meta_boxes, true);
383
-        update_user_option($user->ID, 'ee_nav_menu_initialized', 1, true);
384
-    }
385
-
386
-
387
-    /**
388
-     * This method simply registers custom nav menu boxes for "nav_menus.php route"
389
-     * Currently EE is using this to make sure there are menu options for our CPT archive page routes.
390
-     *
391
-     * @return void
392
-     * @todo   modify this so its more dynamic and automatic for all ee CPTs and setups and can also be hooked into by
393
-     *         addons etc.
394
-     */
395
-    public function register_custom_nav_menu_boxes()
396
-    {
397
-        add_meta_box(
398
-            'add-extra-nav-menu-pages',
399
-            esc_html__('Event Espresso Pages', 'event_espresso'),
400
-            [$this, 'ee_cpt_archive_pages'],
401
-            'nav-menus',
402
-            'side',
403
-            'core'
404
-        );
405
-        add_filter(
406
-            "postbox_classes_nav-menus_add-extra-nav-menu-pages",
407
-            function ($classes) {
408
-                $classes[] = 'ee-admin-container';
409
-                return $classes;
410
-            }
411
-        );
412
-    }
413
-
414
-
415
-    /**
416
-     * Use this to edit the post link for our cpts so that the edit link points to the correct page.
417
-     *
418
-     * @param string $link the original link generated by wp
419
-     * @param int    $id   post id
420
-     * @return string  the (maybe) modified link
421
-     * @since   4.3.0
422
-     */
423
-    public function modify_edit_post_link($link, $id)
424
-    {
425
-        if (! $post = get_post($id)) {
426
-            return $link;
427
-        }
428
-        if ($post->post_type === EspressoPostType::ATTENDEES) {
429
-            $query_args = [
430
-                'action' => 'edit_attendee',
431
-                'post'   => $id,
432
-            ];
433
-            return EEH_URL::add_query_args_and_nonce(
434
-                $query_args,
435
-                admin_url('admin.php?page=espresso_registrations')
436
-            );
437
-        }
438
-        return $link;
439
-    }
440
-
441
-
442
-    public function ee_cpt_archive_pages()
443
-    {
444
-        global $nav_menu_selected_id;
445
-        $removed_args = [
446
-            'action',
447
-            'customlink-tab',
448
-            'edit-menu-item',
449
-            'menu-item',
450
-            'page-tab',
451
-            '_wpnonce',
452
-        ];
453
-        $nav_tab_link = $nav_menu_selected_id
454
-            ? esc_url(
455
-                add_query_arg(
456
-                    'extra-nav-menu-pages-tab',
457
-                    'event-archives',
458
-                    remove_query_arg($removed_args)
459
-                )
460
-            )
461
-            : '';
462
-        $select_all_link = esc_url(
463
-            add_query_arg(
464
-                [
465
-                    'extra-nav-menu-pages-tab' => 'event-archives',
466
-                    'selectall'                => 1,
467
-                ],
468
-                remove_query_arg($removed_args)
469
-            )
470
-        );
471
-        $pages = $this->_get_extra_nav_menu_pages_items();
472
-        $args['walker'] = new Walker_Nav_Menu_Checklist(false);
473
-        $nav_menu_pages_items = walk_nav_menu_tree(
474
-            array_map(
475
-                [$this, '_setup_extra_nav_menu_pages_items'],
476
-                $pages
477
-            ),
478
-            0,
479
-            (object) $args
480
-        );
481
-        EEH_Template::display_template(
482
-            EE_ADMIN_TEMPLATE . 'cpt_archive_page.template.php',
483
-            [
484
-                'nav_menu_selected_id' => $nav_menu_selected_id,
485
-                'nav_menu_pages_items' => $nav_menu_pages_items,
486
-                'nav_tab_link'         => $nav_tab_link,
487
-                'select_all_link'      => $select_all_link,
488
-            ]
489
-        );
490
-    }
491
-
492
-
493
-    /**
494
-     * Returns an array of event archive nav items.
495
-     *
496
-     * @return array
497
-     * @todo  for now this method is just in place so when it gets abstracted further we can substitute in whatever
498
-     *        method we use for getting the extra nav menu items
499
-     */
500
-    private function _get_extra_nav_menu_pages_items()
501
-    {
502
-        $menuitems[] = [
503
-            'title'       => esc_html__('Event List', 'event_espresso'),
504
-            'url'         => get_post_type_archive_link(EspressoPostType::EVENTS),
505
-            'description' => esc_html__('Archive page for all events.', 'event_espresso'),
506
-        ];
507
-        return apply_filters('FHEE__EE_Admin__get_extra_nav_menu_pages_items', $menuitems);
508
-    }
509
-
510
-
511
-    /**
512
-     * Setup nav menu walker item for usage in the event archive nav menu metabox.  It receives a menu_item array with
513
-     * the properties and converts it to the menu item object.
514
-     *
515
-     * @param $menu_item_values
516
-     * @return stdClass
517
-     * @see wp_setup_nav_menu_item() in wp-includes/nav-menu.php
518
-     */
519
-    private function _setup_extra_nav_menu_pages_items($menu_item_values)
520
-    {
521
-        $menu_item = new stdClass();
522
-        $keys      = [
523
-            'ID'               => 0,
524
-            'db_id'            => 0,
525
-            'menu_item_parent' => 0,
526
-            'object_id'        => -1,
527
-            'post_parent'      => 0,
528
-            'type'             => 'custom',
529
-            'object'           => '',
530
-            'type_label'       => esc_html__('Extra Nav Menu Item', 'event_espresso'),
531
-            'title'            => '',
532
-            'url'              => '',
533
-            'target'           => '',
534
-            'attr_title'       => '',
535
-            'description'      => '',
536
-            'classes'          => [],
537
-            'xfn'              => '',
538
-        ];
539
-        foreach ($keys as $key => $value) {
540
-            $menu_item->{$key} = $menu_item_values[ $key ] ?? $value;
541
-        }
542
-        return $menu_item;
543
-    }
544
-
545
-
546
-    /**
547
-     * admin_init
548
-     *
549
-     * @return void
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidDataTypeException
552
-     * @throws InvalidInterfaceException
553
-     */
554
-    public function admin_init()
555
-    {
556
-        /**
557
-         * our cpt models must be instantiated on WordPress post processing routes (wp-admin/post.php),
558
-         * so any hooking into core WP routes is taken care of.  So in this next few lines of code:
559
-         * - check if doing post processing.
560
-         * - check if doing post processing of one of EE CPTs
561
-         * - instantiate the corresponding EE CPT model for the post_type being processed.
562
-         */
563
-        $action    = $this->request->getRequestParam('action');
564
-        $post_type = $this->request->getRequestParam('post_type');
565
-        if ($post_type && $action === 'editpost') {
566
-            /** @var CustomPostTypeDefinitions $custom_post_types */
567
-            $custom_post_types = $this->loader->getShared(CustomPostTypeDefinitions::class);
568
-            $custom_post_types->getCustomPostTypeModels($post_type);
569
-        }
570
-
571
-
572
-        if (! $this->request->isAjax()) {
573
-            /**
574
-             * This code excludes EE critical pages anywhere `wp_dropdown_pages` is used to create a dropdown for selecting
575
-             * critical pages.  The only place critical pages need included in a generated dropdown is on the "Critical
576
-             * Pages" tab in the EE General Settings Admin page.
577
-             * This is for user-proofing.
578
-             */
579
-            add_filter('wp_dropdown_pages', [$this, 'modify_dropdown_pages']);
580
-            if (DbStatus::isOnline()) {
581
-                $this->adminInitModelsReady();
582
-            }
583
-        }
584
-    }
585
-
586
-
587
-    /**
588
-     * Runs on admin_init but only if models are usable (ie, we're not in maintenance mode)
589
-     */
590
-    protected function adminInitModelsReady()
591
-    {
592
-        if (function_exists('wp_add_privacy_policy_content')) {
593
-            $this->loader->getShared('EventEspresso\core\services\privacy\policy\PrivacyPolicyManager');
594
-        }
595
-    }
596
-
597
-
598
-    /**
599
-     * Callback for wp_dropdown_pages hook to remove ee critical pages from the dropdown selection.
600
-     *
601
-     * @param string $output Current output.
602
-     * @return string
603
-     * @throws InvalidArgumentException
604
-     * @throws InvalidDataTypeException
605
-     * @throws InvalidInterfaceException
606
-     */
607
-    public function modify_dropdown_pages($output)
608
-    {
609
-        // get critical pages
610
-        $critical_pages = EE_Registry::instance()->CFG->core->get_critical_pages_array();
611
-
612
-        // split current output by line break for easier parsing.
613
-        $split_output = explode("\n", $output);
614
-
615
-        // loop through to remove any critical pages from the array.
616
-        foreach ($critical_pages as $page_id) {
617
-            $needle = 'value="' . $page_id . '"';
618
-            foreach ($split_output as $key => $haystack) {
619
-                if (strpos($haystack, $needle) !== false) {
620
-                    unset($split_output[ $key ]);
621
-                }
622
-            }
623
-        }
624
-        // replace output with the new contents
625
-        return implode("\n", $split_output);
626
-    }
627
-
628
-
629
-    /**
630
-     * display_admin_notices
631
-     *
632
-     * @return void
633
-     */
634
-    public function display_admin_notices()
635
-    {
636
-        echo EE_Error::get_notices(); // already escaped
637
-    }
638
-
639
-
640
-    /**
641
-     * @param array $elements
642
-     * @return array
643
-     * @throws EE_Error
644
-     * @throws InvalidArgumentException
645
-     * @throws InvalidDataTypeException
646
-     * @throws InvalidInterfaceException
647
-     * @throws ReflectionException
648
-     */
649
-    public function dashboard_glance_items($elements)
650
-    {
651
-        $elements                        = is_array($elements) ? $elements : [$elements];
652
-        $events                          = EEM_Event::instance()->count();
653
-        $items['events']['url']          = EE_Admin_Page::add_query_args_and_nonce(
654
-            ['page' => 'espresso_events'],
655
-            admin_url('admin.php')
656
-        );
657
-        $items['events']['text']         = sprintf(
658
-            esc_html(
659
-                _n('%s Event', '%s Events', $events, 'event_espresso')
660
-            ),
661
-            number_format_i18n($events)
662
-        );
663
-        $items['events']['title']        = esc_html__('Click to view all Events', 'event_espresso');
664
-        $registrations                   = EEM_Registration::instance()->count(
665
-            [
666
-                [
667
-                    'STS_ID' => ['!=', RegStatus::INCOMPLETE],
668
-                ],
669
-            ]
670
-        );
671
-        $items['registrations']['url']   = EE_Admin_Page::add_query_args_and_nonce(
672
-            ['page' => 'espresso_registrations'],
673
-            admin_url('admin.php')
674
-        );
675
-        $items['registrations']['text']  = sprintf(
676
-            esc_html(
677
-                _n('%s Registration', '%s Registrations', $registrations, 'event_espresso')
678
-            ),
679
-            number_format_i18n($registrations)
680
-        );
681
-        $items['registrations']['title'] = esc_html__('Click to view all registrations', 'event_espresso');
682
-
683
-        $items = (array) apply_filters('FHEE__EE_Admin__dashboard_glance_items__items', $items);
684
-
685
-        foreach ($items as $type => $item_properties) {
686
-            $elements[] = sprintf(
687
-                '<a class="ee-dashboard-link-' . $type . '" href="%s" title="%s">%s</a>',
688
-                $item_properties['url'],
689
-                $item_properties['title'],
690
-                $item_properties['text']
691
-            );
692
-        }
693
-        return $elements;
694
-    }
695
-
696
-
697
-    /**
698
-     * check_for_invalid_datetime_formats
699
-     * if an admin changes their date or time format settings on the WP General Settings admin page, verify that
700
-     * their selected format can be parsed by PHP
701
-     *
702
-     * @param    $value
703
-     * @param    $option
704
-     * @return    string
705
-     */
706
-    public function check_for_invalid_datetime_formats($value, $option)
707
-    {
708
-        // check for date_format or time_format
709
-        switch ($option) {
710
-            case 'date_format':
711
-                $date_time_format = $value . ' ' . get_option('time_format');
712
-                break;
713
-            case 'time_format':
714
-                $date_time_format = get_option('date_format') . ' ' . $value;
715
-                break;
716
-            default:
717
-                $date_time_format = false;
718
-        }
719
-        // do we have a date_time format to check ?
720
-        if ($date_time_format) {
721
-            $error_msg = EEH_DTT_Helper::validate_format_string($date_time_format);
722
-
723
-            if (is_array($error_msg)) {
724
-                $msg = '<p>'
725
-                       . sprintf(
726
-                           esc_html__(
727
-                               'The following date time "%s" ( %s ) is difficult to be properly parsed by PHP for the following reasons:',
728
-                               'event_espresso'
729
-                           ),
730
-                           date($date_time_format),
731
-                           $date_time_format
732
-                       )
733
-                       . '</p><p><ul>';
734
-
735
-
736
-                foreach ($error_msg as $error) {
737
-                    $msg .= '<li>' . $error . '</li>';
738
-                }
739
-
740
-                $msg .= '</ul></p><p>'
741
-                        . sprintf(
742
-                            esc_html__(
743
-                                '%sPlease note that your date and time formats have been reset to "F j, Y" and "g:i a" respectively.%s',
744
-                                'event_espresso'
745
-                            ),
746
-                            '<span style="color:#D54E21;">',
747
-                            '</span>'
748
-                        )
749
-                        . '</p>';
750
-
751
-                // trigger WP settings error
752
-                add_settings_error(
753
-                    'date_format',
754
-                    'date_format',
755
-                    $msg
756
-                );
757
-
758
-                // set format to something valid
759
-                switch ($option) {
760
-                    case 'date_format':
761
-                        $value = 'F j, Y';
762
-                        break;
763
-                    case 'time_format':
764
-                        $value = 'g:i a';
765
-                        break;
766
-                }
767
-            }
768
-        }
769
-        return $value;
770
-    }
771
-
772
-
773
-    /**
774
-     * its_eSpresso - converts the less commonly used spelling of "Expresso" to "Espresso"
775
-     *
776
-     * @param $content
777
-     * @return    string
778
-     */
779
-    public function its_eSpresso($content)
780
-    {
781
-        return str_replace('[EXPRESSO_', '[ESPRESSO_', $content);
782
-    }
783
-
784
-
785
-    /**
786
-     * espresso_admin_footer
787
-     *
788
-     * @return    string
789
-     */
790
-    public function espresso_admin_footer()
791
-    {
792
-        return EEH_Template::powered_by_event_espresso('aln-cntr', '', ['utm_content' => 'admin_footer']);
793
-    }
794
-
795
-
796
-    /**
797
-     * Hooks into the "post states" filter in a wp post type list table.
798
-     *
799
-     * @param array   $post_states
800
-     * @param WP_Post $post
801
-     * @return array
802
-     * @throws InvalidArgumentException
803
-     * @throws InvalidDataTypeException
804
-     * @throws InvalidInterfaceException
805
-     */
806
-    public function displayStateForCriticalPages($post_states, $post)
807
-    {
808
-        $post_states = (array) $post_states;
809
-        if (! $post instanceof WP_Post || $post->post_type !== 'page') {
810
-            return $post_states;
811
-        }
812
-        /** @var EE_Core_Config $config */
813
-        $config = $this->loader->getShared('EE_Config')->core;
814
-        if (in_array($post->ID, $config->get_critical_pages_array(), true)) {
815
-            $post_states[] = sprintf(
816
-            /* Translators: Using company name - Event Espresso Critical Page */
817
-                esc_html__('%s Critical Page', 'event_espresso'),
818
-                'Event Espresso'
819
-            );
820
-        }
821
-        return $post_states;
822
-    }
823
-
824
-
825
-    /**
826
-     * Show documentation links on the plugins page
827
-     *
828
-     * @param mixed $meta Plugin Row Meta
829
-     * @param mixed $file Plugin Base file
830
-     * @return array
831
-     */
832
-    public function addLinksToPluginRowMeta($meta, $file)
833
-    {
834
-        if (EE_PLUGIN_BASENAME === $file) {
835
-            $row_meta = [
836
-                'docs' => '<a href="https://eventespresso.com/support/documentation/versioned-docs/?doc_ver=ee4"'
837
-                          . ' aria-label="'
838
-                          . esc_attr__('View Event Espresso documentation', 'event_espresso')
839
-                          . '">'
840
-                          . esc_html__('Docs', 'event_espresso')
841
-                          . '</a>',
842
-                'api'  => '<a href="https://github.com/eventespresso/event-espresso-core/tree/master/docs/C--REST-API"'
843
-                          . ' aria-label="'
844
-                          . esc_attr__('View Event Espresso API docs', 'event_espresso')
845
-                          . '">'
846
-                          . esc_html__('API docs', 'event_espresso')
847
-                          . '</a>',
848
-            ];
849
-            return array_merge($meta, $row_meta);
850
-        }
851
-        return (array) $meta;
852
-    }
853
-
854
-     /**************************************************************************************/
855
-     /************************************* DEPRECATED *************************************/
856
-     /**************************************************************************************/
857
-
858
-
859
-    /**
860
-     * This is the action hook for the AHEE__EE_Admin_Page__route_admin_request hook that fires off right before an
861
-     * EE_Admin_Page route is called.
862
-     *
863
-     * @return void
864
-     */
865
-    public function route_admin_request()
866
-    {
867
-    }
868
-
869
-
870
-    /**
871
-     * wp_loaded should fire on the WordPress wp_loaded hook.  This fires on a VERY late priority.
872
-     *
873
-     * @return void
874
-     */
875
-    public function wp_loaded()
876
-    {
877
-    }
878
-
879
-
880
-    /**
881
-     * static method for registering ee admin page.
882
-     * This method is deprecated in favor of the new location in EE_Register_Admin_Page::register.
883
-     *
884
-     * @param       $page_basename
885
-     * @param       $page_path
886
-     * @param array $config
887
-     * @return void
888
-     * @throws EE_Error
889
-     * @see        EE_Register_Admin_Page::register()
890
-     * @since      4.3.0
891
-     * @deprecated 4.3.0    Use EE_Register_Admin_Page::register() instead
892
-     */
893
-    public static function register_ee_admin_page($page_basename, $page_path, $config = [])
894
-    {
895
-        EE_Error::doing_it_wrong(
896
-            __METHOD__,
897
-            sprintf(
898
-                esc_html__(
899
-                    'Usage is deprecated.  Use EE_Register_Admin_Page::register() for registering the %s admin page.',
900
-                    'event_espresso'
901
-                ),
902
-                $page_basename
903
-            ),
904
-            '4.3'
905
-        );
906
-        if (class_exists('EE_Register_Admin_Page')) {
907
-            $config['page_path'] = $page_path;
908
-        }
909
-        EE_Register_Admin_Page::register($page_basename, $config);
910
-    }
911
-
912
-
913
-    /**
914
-     * @param int     $post_ID
915
-     * @param WP_Post $post
916
-     * @return void
917
-     * @deprecated 4.8.41
918
-     */
919
-    public static function parse_post_content_on_save($post_ID, $post)
920
-    {
921
-        EE_Error::doing_it_wrong(
922
-            __METHOD__,
923
-            esc_html__('Usage is deprecated', 'event_espresso'),
924
-            '4.8.41'
925
-        );
926
-    }
927
-
928
-
929
-    /**
930
-     * @param  $option
931
-     * @param  $old_value
932
-     * @param  $value
933
-     * @return void
934
-     * @deprecated 4.8.41
935
-     */
936
-    public function reset_page_for_posts_on_change($option, $old_value, $value)
937
-    {
938
-        EE_Error::doing_it_wrong(
939
-            __METHOD__,
940
-            esc_html__('Usage is deprecated', 'event_espresso'),
941
-            '4.8.41'
942
-        );
943
-    }
944
-
945
-
946
-    /**
947
-     * @return void
948
-     * @deprecated 4.9.27
949
-     */
950
-    public function get_persistent_admin_notices()
951
-    {
952
-        EE_Error::doing_it_wrong(
953
-            __METHOD__,
954
-            sprintf(
955
-                esc_html__('Usage is deprecated. Use "%1$s" instead.', 'event_espresso'),
956
-                '\EventEspresso\core\services\notifications\PersistentAdminNoticeManager'
957
-            ),
958
-            '4.9.27'
959
-        );
960
-    }
961
-
962
-
963
-    /**
964
-     * @throws InvalidInterfaceException
965
-     * @throws InvalidDataTypeException
966
-     * @throws DomainException
967
-     * @deprecated 4.9.27
968
-     */
969
-    public function dismiss_ee_nag_notice_callback()
970
-    {
971
-        EE_Error::doing_it_wrong(
972
-            __METHOD__,
973
-            sprintf(
974
-                esc_html__('Usage is deprecated. Use "%1$s" instead.', 'event_espresso'),
975
-                '\EventEspresso\core\services\notifications\PersistentAdminNoticeManager'
976
-            ),
977
-            '4.9.27'
978
-        );
979
-        $this->persistent_admin_notice_manager->dismissNotice();
980
-    }
981
-
982
-
983
-    /**
984
-     * @return void
985
-     * @deprecated 5.0.0.p
986
-     */
987
-    public function enqueue_admin_scripts()
988
-    {
989
-    }
990
-
991
-
992
-
993
-    /**
994
-     * @return RequestInterface
995
-     * @deprecated 5.0.0.p
996
-     */
997
-    public function get_request()
998
-    {
999
-        EE_Error::doing_it_wrong(
1000
-            __METHOD__,
1001
-            sprintf(
1002
-                esc_html__('Usage is deprecated. Use "%1$s" instead.', 'event_espresso'),
1003
-                'EventEspresso\core\services\request\Request'
1004
-            ),
1005
-            '5.0.0.p'
1006
-        );
1007
-        return $this->request;
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * @deprecated 5.0.0.p
1013
-     */
1014
-    public function hookIntoWpPluginsPage()
1015
-    {
1016
-    }
27
+	private static ?EE_Admin $_instance = null;
28
+
29
+	private ?PersistentAdminNoticeManager $persistent_admin_notice_manager = null;
30
+
31
+	protected LoaderInterface $loader;
32
+
33
+	protected RequestInterface $request;
34
+
35
+
36
+	/**
37
+	 * @singleton method used to instantiate class object
38
+	 * @param LoaderInterface|null  $loader
39
+	 * @param RequestInterface|null $request
40
+	 * @return EE_Admin|null
41
+	 * @throws EE_Error
42
+	 */
43
+	public static function instance(?LoaderInterface $loader = null, ?RequestInterface $request = null): ?EE_Admin
44
+	{
45
+		// check if class object is instantiated
46
+		if (! EE_Admin::$_instance instanceof EE_Admin) {
47
+			EE_Admin::$_instance = new EE_Admin($loader, $request);
48
+		}
49
+		return EE_Admin::$_instance;
50
+	}
51
+
52
+
53
+	/**
54
+	 * @return EE_Admin|null
55
+	 * @throws EE_Error
56
+	 */
57
+	public static function reset(): ?EE_Admin
58
+	{
59
+		EE_Admin::$_instance = null;
60
+		$loader = LoaderFactory::getLoader();
61
+		$request = $loader->getShared('EventEspresso\core\services\request\Request');
62
+		return EE_Admin::instance($loader, $request);
63
+	}
64
+
65
+
66
+	/**
67
+	 * @param LoaderInterface  $loader
68
+	 * @param RequestInterface $request
69
+	 * @throws EE_Error
70
+	 * @throws InvalidDataTypeException
71
+	 * @throws InvalidInterfaceException
72
+	 * @throws InvalidArgumentException
73
+	 */
74
+	protected function __construct(LoaderInterface $loader, RequestInterface $request)
75
+	{
76
+		$this->loader = $loader;
77
+		$this->request = $request;
78
+		// define global EE_Admin constants
79
+		$this->_define_all_constants();
80
+		// set autoloaders for our admin page classes based on included path information
81
+		EEH_Autoloader::register_autoloaders_for_each_file_in_folder(EE_ADMIN);
82
+		// reset Environment config (we only do this on admin page loads);
83
+		EE_Registry::instance()->CFG->environment->recheck_values();
84
+		// load EE_Request_Handler early
85
+		add_action('AHEE__EE_System__initialize_last', [$this, 'init']);
86
+		add_action('admin_init', [$this, 'admin_init'], 100);
87
+		if (! $this->request->isActivation() && ! $this->request->isAjax()) {
88
+			// admin hooks
89
+			add_action('admin_notices', [$this, 'display_admin_notices']);
90
+			add_action('network_admin_notices', [$this, 'display_admin_notices']);
91
+			add_filter('pre_update_option', [$this, 'check_for_invalid_datetime_formats'], 100, 2);
92
+			add_filter('plugin_action_links', [$this, 'filter_plugin_actions'], 10, 2);
93
+			add_filter('admin_footer_text', [$this, 'espresso_admin_footer']);
94
+			add_action('display_post_states', [$this, 'displayStateForCriticalPages'], 10, 2);
95
+			add_filter('plugin_row_meta', [$this, 'addLinksToPluginRowMeta'], 10, 2);
96
+		}
97
+		do_action('AHEE__EE_Admin__loaded');
98
+	}
99
+
100
+
101
+	/**
102
+	 * _define_all_constants
103
+	 * define constants that are set globally for all admin pages
104
+	 *
105
+	 * @return void
106
+	 */
107
+	private function _define_all_constants()
108
+	{
109
+		if (! defined('EE_ADMIN_URL')) {
110
+			define('EE_ADMIN_URL', EE_PLUGIN_DIR_URL . 'core/admin/');
111
+			define('EE_ADMIN_PAGES_URL', EE_PLUGIN_DIR_URL . 'admin_pages/');
112
+			define('EE_ADMIN_TEMPLATE', EE_ADMIN . 'templates/');
113
+			define('WP_ADMIN_PATH', ABSPATH . 'wp-admin/');
114
+			define('WP_AJAX_URL', admin_url('admin-ajax.php'));
115
+		}
116
+	}
117
+
118
+
119
+	/**
120
+	 * filter_plugin_actions - adds links to the Plugins page listing
121
+	 *
122
+	 * @param array  $links
123
+	 * @param string $plugin
124
+	 * @return    array
125
+	 */
126
+	public function filter_plugin_actions($links, $plugin)
127
+	{
128
+		// set $main_file in stone
129
+		static $main_file;
130
+		// if $main_file is not set yet
131
+		if (! $main_file) {
132
+			$main_file = EE_PLUGIN_BASENAME;
133
+		}
134
+		if ($plugin === $main_file) {
135
+			// compare current plugin to this one
136
+			if (MaintenanceStatus::isFullSite()) {
137
+				$maintenance_link = '<a href="admin.php?page=espresso_maintenance_settings"'
138
+									. ' title="Event Espresso is in maintenance mode.  Click this link to learn why.">'
139
+									. esc_html__('Maintenance Mode Active', 'event_espresso')
140
+									. '</a>';
141
+				array_unshift($links, $maintenance_link);
142
+			} else {
143
+				$org_settings_link = '<a href="admin.php?page=espresso_general_settings">'
144
+									 . esc_html__('Settings', 'event_espresso')
145
+									 . '</a>';
146
+				$events_link       = '<a href="admin.php?page=espresso_events">'
147
+									 . esc_html__('Events', 'event_espresso')
148
+									 . '</a>';
149
+				// add before other links
150
+				array_unshift($links, $org_settings_link, $events_link);
151
+			}
152
+		}
153
+		return $links;
154
+	}
155
+
156
+
157
+	/**
158
+	 * hide_admin_pages_except_maintenance_mode
159
+	 *
160
+	 * @param array $admin_page_folder_names
161
+	 * @return array
162
+	 */
163
+	public function hide_admin_pages_except_maintenance_mode($admin_page_folder_names = [])
164
+	{
165
+		return [
166
+			'maintenance' => EE_ADMIN_PAGES . 'maintenance/',
167
+			'about'       => EE_ADMIN_PAGES . 'about/',
168
+			'support'     => EE_ADMIN_PAGES . 'support/',
169
+		];
170
+	}
171
+
172
+
173
+	/**
174
+	 * init- should fire after shortcode, module,  addon, other plugin (default priority), and even
175
+	 * EE_Front_Controller's init phases have run
176
+	 *
177
+	 * @return void
178
+	 * @throws EE_Error
179
+	 * @throws InvalidArgumentException
180
+	 * @throws InvalidDataTypeException
181
+	 * @throws InvalidInterfaceException
182
+	 * @throws ReflectionException
183
+	 * @throws ServiceNotFoundException
184
+	 */
185
+	public function init()
186
+	{
187
+		// only enable most of the EE_Admin IF we're not in full maintenance mode
188
+		if (DbStatus::isOnline()) {
189
+			$this->initModelsReady();
190
+		}
191
+		// run the admin page factory but ONLY if:
192
+		// - it is a regular non ajax admin request
193
+		// - we are doing an ee admin ajax request
194
+		if ($this->request->isAdmin() || $this->request->isAdminAjax() || $this->request->isActivation()) {
195
+			try {
196
+				// this loads the controller for the admin pages which will setup routing etc
197
+				$admin_page_loader = $this->loader->getShared('EE_Admin_Page_Loader', [$this->loader]);
198
+				/** @var EE_Admin_Page_Loader $admin_page_loader */
199
+				$admin_page_loader->init();
200
+			} catch (EE_Error $e) {
201
+				$e->get_error();
202
+			}
203
+		}
204
+		if ($this->request->isAjax()) {
205
+			return;
206
+		}
207
+		add_filter('content_save_pre', [$this, 'its_eSpresso']);
208
+		// make sure our CPTs and custom taxonomy metaboxes get shown for first time users
209
+		add_action('admin_head', [$this, 'enable_hidden_ee_nav_menu_metaboxes']);
210
+		add_action('admin_head', [$this, 'register_custom_nav_menu_boxes']);
211
+		// exclude EE critical pages from all nav menus and wp_list_pages
212
+		add_filter('nav_menu_meta_box_object', [$this, 'remove_pages_from_nav_menu']);
213
+	}
214
+
215
+
216
+	/**
217
+	 * Gets the loader (and if it wasn't previously set, sets it)
218
+	 *
219
+	 * @return LoaderInterface
220
+	 * @throws InvalidArgumentException
221
+	 * @throws InvalidDataTypeException
222
+	 * @throws InvalidInterfaceException
223
+	 */
224
+	protected function getLoader()
225
+	{
226
+		return $this->loader;
227
+	}
228
+
229
+
230
+	/**
231
+	 * Method that's fired on admin requests (including admin ajax) but only when the models are usable
232
+	 * (ie, the site isn't in maintenance mode)
233
+	 *
234
+	 * @return void
235
+	 * @throws EE_Error
236
+	 * @throws ReflectionException
237
+	 * @since 4.9.63.p
238
+	 */
239
+	protected function initModelsReady()
240
+	{
241
+		$page = $this->request->getRequestParam('page', '');
242
+		// ok so we want to enable the entire admin
243
+		$this->persistent_admin_notice_manager = $this->loader->getShared(PersistentAdminNoticeManager::class);
244
+		$this->persistent_admin_notice_manager->setReturnUrl(
245
+			EE_Admin_Page::add_query_args_and_nonce(
246
+				[
247
+					'page'   => $page,
248
+					'action' => $this->request->getRequestParam('action', ''),
249
+				],
250
+				EE_ADMIN_URL
251
+			)
252
+		);
253
+		if ($page === 'pricing' || strpos($page, 'espresso') !== false) {
254
+			$this->persistent_admin_notice_manager->loadAdminNotices();
255
+		}
256
+		$this->maybeSetDatetimeWarningNotice();
257
+		// at a glance dashboard widget
258
+		add_filter('dashboard_glance_items', [$this, 'dashboard_glance_items']);
259
+		// filter for get_edit_post_link used on comments for custom post types
260
+		add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
261
+	}
262
+
263
+
264
+	/**
265
+	 *    get_persistent_admin_notices
266
+	 *
267
+	 * @access    public
268
+	 * @return void
269
+	 * @throws EE_Error
270
+	 * @throws InvalidArgumentException
271
+	 * @throws InvalidDataTypeException
272
+	 * @throws InvalidInterfaceException
273
+	 * @throws ReflectionException
274
+	 */
275
+	public function maybeSetDatetimeWarningNotice()
276
+	{
277
+		// add dismissible notice for datetime changes.  Only valid if site does not have a timezone_string set.
278
+		// @todo This needs to stay in core for a bit to catch anyone upgrading from a version without this to a version
279
+		// with this.  But after enough time (indeterminate at this point) we can just remove this notice.
280
+		// this was added with https://events.codebasehq.com/projects/event-espresso/tickets/10626
281
+		if (
282
+			apply_filters('FHEE__EE_Admin__maybeSetDatetimeWarningNotice', true)
283
+			&& ! get_option('timezone_string')
284
+			&& EEM_Event::instance()->count() > 0
285
+		) {
286
+			new PersistentAdminNotice(
287
+				'datetime_fix_notice',
288
+				sprintf(
289
+					esc_html__(
290
+						'%1$sImportant announcement related to your install of Event Espresso%2$s: There are some changes made to your site that could affect how dates display for your events and other related items with dates and times.  Read more about it %3$shere%4$s. If your dates and times are displaying incorrectly (incorrect offset), you can fix it using the tool on %5$sthis page%4$s.',
291
+						'event_espresso'
292
+					),
293
+					'<strong>',
294
+					'</strong>',
295
+					'<a href="https://eventespresso.com/2017/08/important-upcoming-changes-dates-times">',
296
+					'</a>',
297
+					'<a href="' . EE_Admin_Page::add_query_args_and_nonce(
298
+						[
299
+							'page'   => 'espresso_maintenance_settings',
300
+							'action' => 'datetime_tools',
301
+						],
302
+						admin_url('admin.php')
303
+					) . '">'
304
+				),
305
+				false,
306
+				'manage_options',
307
+				'datetime_fix_persistent_notice'
308
+			);
309
+		}
310
+	}
311
+
312
+
313
+	/**
314
+	 * this simply hooks into the nav menu setup of pages metabox and makes sure that we remove EE critical pages from
315
+	 * the list of options. the wp function "wp_nav_menu_item_post_type_meta_box" found in
316
+	 * wp-admin/includes/nav-menu.php looks for the "_default_query" property on the post_type object and it uses that
317
+	 * to override any queries found in the existing query for the given post type.  Note that _default_query is not a
318
+	 * normal property on the post_type object.  It's found ONLY in this particular context.
319
+	 *
320
+	 * @param WP_Post $post_type WP post type object
321
+	 * @return WP_Post
322
+	 * @throws InvalidArgumentException
323
+	 * @throws InvalidDataTypeException
324
+	 * @throws InvalidInterfaceException
325
+	 */
326
+	public function remove_pages_from_nav_menu($post_type)
327
+	{
328
+		// if this isn't the "pages" post type let's get out
329
+		if ($post_type->name !== 'page') {
330
+			return $post_type;
331
+		}
332
+		$critical_pages            = EE_Registry::instance()->CFG->core->get_critical_pages_array();
333
+		$post_type->_default_query = [
334
+			'post__not_in' => $critical_pages,
335
+		];
336
+		return $post_type;
337
+	}
338
+
339
+
340
+	/**
341
+	 * WP by default only shows three metaboxes in "nav-menus.php" for first times users.
342
+	 * We want to make sure our metaboxes get shown as well
343
+	 *
344
+	 * @return void
345
+	 */
346
+	public function enable_hidden_ee_nav_menu_metaboxes()
347
+	{
348
+		global $wp_meta_boxes, $pagenow;
349
+		if (! is_array($wp_meta_boxes) || $pagenow !== 'nav-menus.php') {
350
+			return;
351
+		}
352
+		$user = wp_get_current_user();
353
+		// has this been done yet?
354
+		if (get_user_option('ee_nav_menu_initialized', $user->ID)) {
355
+			return;
356
+		}
357
+
358
+		$hidden_meta_boxes  = get_user_option('metaboxhidden_nav-menus', $user->ID);
359
+		$initial_meta_boxes = apply_filters(
360
+			'FHEE__EE_Admin__enable_hidden_ee_nav_menu_boxes__initial_meta_boxes',
361
+			[
362
+				'nav-menu-theme-locations',
363
+				'add-page',
364
+				'add-custom-links',
365
+				'add-category',
366
+				'add-espresso_events',
367
+				'add-espresso_venues',
368
+				'add-espresso_event_categories',
369
+				'add-espresso_venue_categories',
370
+				'add-post-type-post',
371
+				'add-post-type-page',
372
+			]
373
+		);
374
+
375
+		if (is_array($hidden_meta_boxes)) {
376
+			foreach ($hidden_meta_boxes as $key => $meta_box_id) {
377
+				if (in_array($meta_box_id, $initial_meta_boxes, true)) {
378
+					unset($hidden_meta_boxes[ $key ]);
379
+				}
380
+			}
381
+		}
382
+		update_user_option($user->ID, 'metaboxhidden_nav-menus', $hidden_meta_boxes, true);
383
+		update_user_option($user->ID, 'ee_nav_menu_initialized', 1, true);
384
+	}
385
+
386
+
387
+	/**
388
+	 * This method simply registers custom nav menu boxes for "nav_menus.php route"
389
+	 * Currently EE is using this to make sure there are menu options for our CPT archive page routes.
390
+	 *
391
+	 * @return void
392
+	 * @todo   modify this so its more dynamic and automatic for all ee CPTs and setups and can also be hooked into by
393
+	 *         addons etc.
394
+	 */
395
+	public function register_custom_nav_menu_boxes()
396
+	{
397
+		add_meta_box(
398
+			'add-extra-nav-menu-pages',
399
+			esc_html__('Event Espresso Pages', 'event_espresso'),
400
+			[$this, 'ee_cpt_archive_pages'],
401
+			'nav-menus',
402
+			'side',
403
+			'core'
404
+		);
405
+		add_filter(
406
+			"postbox_classes_nav-menus_add-extra-nav-menu-pages",
407
+			function ($classes) {
408
+				$classes[] = 'ee-admin-container';
409
+				return $classes;
410
+			}
411
+		);
412
+	}
413
+
414
+
415
+	/**
416
+	 * Use this to edit the post link for our cpts so that the edit link points to the correct page.
417
+	 *
418
+	 * @param string $link the original link generated by wp
419
+	 * @param int    $id   post id
420
+	 * @return string  the (maybe) modified link
421
+	 * @since   4.3.0
422
+	 */
423
+	public function modify_edit_post_link($link, $id)
424
+	{
425
+		if (! $post = get_post($id)) {
426
+			return $link;
427
+		}
428
+		if ($post->post_type === EspressoPostType::ATTENDEES) {
429
+			$query_args = [
430
+				'action' => 'edit_attendee',
431
+				'post'   => $id,
432
+			];
433
+			return EEH_URL::add_query_args_and_nonce(
434
+				$query_args,
435
+				admin_url('admin.php?page=espresso_registrations')
436
+			);
437
+		}
438
+		return $link;
439
+	}
440
+
441
+
442
+	public function ee_cpt_archive_pages()
443
+	{
444
+		global $nav_menu_selected_id;
445
+		$removed_args = [
446
+			'action',
447
+			'customlink-tab',
448
+			'edit-menu-item',
449
+			'menu-item',
450
+			'page-tab',
451
+			'_wpnonce',
452
+		];
453
+		$nav_tab_link = $nav_menu_selected_id
454
+			? esc_url(
455
+				add_query_arg(
456
+					'extra-nav-menu-pages-tab',
457
+					'event-archives',
458
+					remove_query_arg($removed_args)
459
+				)
460
+			)
461
+			: '';
462
+		$select_all_link = esc_url(
463
+			add_query_arg(
464
+				[
465
+					'extra-nav-menu-pages-tab' => 'event-archives',
466
+					'selectall'                => 1,
467
+				],
468
+				remove_query_arg($removed_args)
469
+			)
470
+		);
471
+		$pages = $this->_get_extra_nav_menu_pages_items();
472
+		$args['walker'] = new Walker_Nav_Menu_Checklist(false);
473
+		$nav_menu_pages_items = walk_nav_menu_tree(
474
+			array_map(
475
+				[$this, '_setup_extra_nav_menu_pages_items'],
476
+				$pages
477
+			),
478
+			0,
479
+			(object) $args
480
+		);
481
+		EEH_Template::display_template(
482
+			EE_ADMIN_TEMPLATE . 'cpt_archive_page.template.php',
483
+			[
484
+				'nav_menu_selected_id' => $nav_menu_selected_id,
485
+				'nav_menu_pages_items' => $nav_menu_pages_items,
486
+				'nav_tab_link'         => $nav_tab_link,
487
+				'select_all_link'      => $select_all_link,
488
+			]
489
+		);
490
+	}
491
+
492
+
493
+	/**
494
+	 * Returns an array of event archive nav items.
495
+	 *
496
+	 * @return array
497
+	 * @todo  for now this method is just in place so when it gets abstracted further we can substitute in whatever
498
+	 *        method we use for getting the extra nav menu items
499
+	 */
500
+	private function _get_extra_nav_menu_pages_items()
501
+	{
502
+		$menuitems[] = [
503
+			'title'       => esc_html__('Event List', 'event_espresso'),
504
+			'url'         => get_post_type_archive_link(EspressoPostType::EVENTS),
505
+			'description' => esc_html__('Archive page for all events.', 'event_espresso'),
506
+		];
507
+		return apply_filters('FHEE__EE_Admin__get_extra_nav_menu_pages_items', $menuitems);
508
+	}
509
+
510
+
511
+	/**
512
+	 * Setup nav menu walker item for usage in the event archive nav menu metabox.  It receives a menu_item array with
513
+	 * the properties and converts it to the menu item object.
514
+	 *
515
+	 * @param $menu_item_values
516
+	 * @return stdClass
517
+	 * @see wp_setup_nav_menu_item() in wp-includes/nav-menu.php
518
+	 */
519
+	private function _setup_extra_nav_menu_pages_items($menu_item_values)
520
+	{
521
+		$menu_item = new stdClass();
522
+		$keys      = [
523
+			'ID'               => 0,
524
+			'db_id'            => 0,
525
+			'menu_item_parent' => 0,
526
+			'object_id'        => -1,
527
+			'post_parent'      => 0,
528
+			'type'             => 'custom',
529
+			'object'           => '',
530
+			'type_label'       => esc_html__('Extra Nav Menu Item', 'event_espresso'),
531
+			'title'            => '',
532
+			'url'              => '',
533
+			'target'           => '',
534
+			'attr_title'       => '',
535
+			'description'      => '',
536
+			'classes'          => [],
537
+			'xfn'              => '',
538
+		];
539
+		foreach ($keys as $key => $value) {
540
+			$menu_item->{$key} = $menu_item_values[ $key ] ?? $value;
541
+		}
542
+		return $menu_item;
543
+	}
544
+
545
+
546
+	/**
547
+	 * admin_init
548
+	 *
549
+	 * @return void
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidDataTypeException
552
+	 * @throws InvalidInterfaceException
553
+	 */
554
+	public function admin_init()
555
+	{
556
+		/**
557
+		 * our cpt models must be instantiated on WordPress post processing routes (wp-admin/post.php),
558
+		 * so any hooking into core WP routes is taken care of.  So in this next few lines of code:
559
+		 * - check if doing post processing.
560
+		 * - check if doing post processing of one of EE CPTs
561
+		 * - instantiate the corresponding EE CPT model for the post_type being processed.
562
+		 */
563
+		$action    = $this->request->getRequestParam('action');
564
+		$post_type = $this->request->getRequestParam('post_type');
565
+		if ($post_type && $action === 'editpost') {
566
+			/** @var CustomPostTypeDefinitions $custom_post_types */
567
+			$custom_post_types = $this->loader->getShared(CustomPostTypeDefinitions::class);
568
+			$custom_post_types->getCustomPostTypeModels($post_type);
569
+		}
570
+
571
+
572
+		if (! $this->request->isAjax()) {
573
+			/**
574
+			 * This code excludes EE critical pages anywhere `wp_dropdown_pages` is used to create a dropdown for selecting
575
+			 * critical pages.  The only place critical pages need included in a generated dropdown is on the "Critical
576
+			 * Pages" tab in the EE General Settings Admin page.
577
+			 * This is for user-proofing.
578
+			 */
579
+			add_filter('wp_dropdown_pages', [$this, 'modify_dropdown_pages']);
580
+			if (DbStatus::isOnline()) {
581
+				$this->adminInitModelsReady();
582
+			}
583
+		}
584
+	}
585
+
586
+
587
+	/**
588
+	 * Runs on admin_init but only if models are usable (ie, we're not in maintenance mode)
589
+	 */
590
+	protected function adminInitModelsReady()
591
+	{
592
+		if (function_exists('wp_add_privacy_policy_content')) {
593
+			$this->loader->getShared('EventEspresso\core\services\privacy\policy\PrivacyPolicyManager');
594
+		}
595
+	}
596
+
597
+
598
+	/**
599
+	 * Callback for wp_dropdown_pages hook to remove ee critical pages from the dropdown selection.
600
+	 *
601
+	 * @param string $output Current output.
602
+	 * @return string
603
+	 * @throws InvalidArgumentException
604
+	 * @throws InvalidDataTypeException
605
+	 * @throws InvalidInterfaceException
606
+	 */
607
+	public function modify_dropdown_pages($output)
608
+	{
609
+		// get critical pages
610
+		$critical_pages = EE_Registry::instance()->CFG->core->get_critical_pages_array();
611
+
612
+		// split current output by line break for easier parsing.
613
+		$split_output = explode("\n", $output);
614
+
615
+		// loop through to remove any critical pages from the array.
616
+		foreach ($critical_pages as $page_id) {
617
+			$needle = 'value="' . $page_id . '"';
618
+			foreach ($split_output as $key => $haystack) {
619
+				if (strpos($haystack, $needle) !== false) {
620
+					unset($split_output[ $key ]);
621
+				}
622
+			}
623
+		}
624
+		// replace output with the new contents
625
+		return implode("\n", $split_output);
626
+	}
627
+
628
+
629
+	/**
630
+	 * display_admin_notices
631
+	 *
632
+	 * @return void
633
+	 */
634
+	public function display_admin_notices()
635
+	{
636
+		echo EE_Error::get_notices(); // already escaped
637
+	}
638
+
639
+
640
+	/**
641
+	 * @param array $elements
642
+	 * @return array
643
+	 * @throws EE_Error
644
+	 * @throws InvalidArgumentException
645
+	 * @throws InvalidDataTypeException
646
+	 * @throws InvalidInterfaceException
647
+	 * @throws ReflectionException
648
+	 */
649
+	public function dashboard_glance_items($elements)
650
+	{
651
+		$elements                        = is_array($elements) ? $elements : [$elements];
652
+		$events                          = EEM_Event::instance()->count();
653
+		$items['events']['url']          = EE_Admin_Page::add_query_args_and_nonce(
654
+			['page' => 'espresso_events'],
655
+			admin_url('admin.php')
656
+		);
657
+		$items['events']['text']         = sprintf(
658
+			esc_html(
659
+				_n('%s Event', '%s Events', $events, 'event_espresso')
660
+			),
661
+			number_format_i18n($events)
662
+		);
663
+		$items['events']['title']        = esc_html__('Click to view all Events', 'event_espresso');
664
+		$registrations                   = EEM_Registration::instance()->count(
665
+			[
666
+				[
667
+					'STS_ID' => ['!=', RegStatus::INCOMPLETE],
668
+				],
669
+			]
670
+		);
671
+		$items['registrations']['url']   = EE_Admin_Page::add_query_args_and_nonce(
672
+			['page' => 'espresso_registrations'],
673
+			admin_url('admin.php')
674
+		);
675
+		$items['registrations']['text']  = sprintf(
676
+			esc_html(
677
+				_n('%s Registration', '%s Registrations', $registrations, 'event_espresso')
678
+			),
679
+			number_format_i18n($registrations)
680
+		);
681
+		$items['registrations']['title'] = esc_html__('Click to view all registrations', 'event_espresso');
682
+
683
+		$items = (array) apply_filters('FHEE__EE_Admin__dashboard_glance_items__items', $items);
684
+
685
+		foreach ($items as $type => $item_properties) {
686
+			$elements[] = sprintf(
687
+				'<a class="ee-dashboard-link-' . $type . '" href="%s" title="%s">%s</a>',
688
+				$item_properties['url'],
689
+				$item_properties['title'],
690
+				$item_properties['text']
691
+			);
692
+		}
693
+		return $elements;
694
+	}
695
+
696
+
697
+	/**
698
+	 * check_for_invalid_datetime_formats
699
+	 * if an admin changes their date or time format settings on the WP General Settings admin page, verify that
700
+	 * their selected format can be parsed by PHP
701
+	 *
702
+	 * @param    $value
703
+	 * @param    $option
704
+	 * @return    string
705
+	 */
706
+	public function check_for_invalid_datetime_formats($value, $option)
707
+	{
708
+		// check for date_format or time_format
709
+		switch ($option) {
710
+			case 'date_format':
711
+				$date_time_format = $value . ' ' . get_option('time_format');
712
+				break;
713
+			case 'time_format':
714
+				$date_time_format = get_option('date_format') . ' ' . $value;
715
+				break;
716
+			default:
717
+				$date_time_format = false;
718
+		}
719
+		// do we have a date_time format to check ?
720
+		if ($date_time_format) {
721
+			$error_msg = EEH_DTT_Helper::validate_format_string($date_time_format);
722
+
723
+			if (is_array($error_msg)) {
724
+				$msg = '<p>'
725
+					   . sprintf(
726
+						   esc_html__(
727
+							   'The following date time "%s" ( %s ) is difficult to be properly parsed by PHP for the following reasons:',
728
+							   'event_espresso'
729
+						   ),
730
+						   date($date_time_format),
731
+						   $date_time_format
732
+					   )
733
+					   . '</p><p><ul>';
734
+
735
+
736
+				foreach ($error_msg as $error) {
737
+					$msg .= '<li>' . $error . '</li>';
738
+				}
739
+
740
+				$msg .= '</ul></p><p>'
741
+						. sprintf(
742
+							esc_html__(
743
+								'%sPlease note that your date and time formats have been reset to "F j, Y" and "g:i a" respectively.%s',
744
+								'event_espresso'
745
+							),
746
+							'<span style="color:#D54E21;">',
747
+							'</span>'
748
+						)
749
+						. '</p>';
750
+
751
+				// trigger WP settings error
752
+				add_settings_error(
753
+					'date_format',
754
+					'date_format',
755
+					$msg
756
+				);
757
+
758
+				// set format to something valid
759
+				switch ($option) {
760
+					case 'date_format':
761
+						$value = 'F j, Y';
762
+						break;
763
+					case 'time_format':
764
+						$value = 'g:i a';
765
+						break;
766
+				}
767
+			}
768
+		}
769
+		return $value;
770
+	}
771
+
772
+
773
+	/**
774
+	 * its_eSpresso - converts the less commonly used spelling of "Expresso" to "Espresso"
775
+	 *
776
+	 * @param $content
777
+	 * @return    string
778
+	 */
779
+	public function its_eSpresso($content)
780
+	{
781
+		return str_replace('[EXPRESSO_', '[ESPRESSO_', $content);
782
+	}
783
+
784
+
785
+	/**
786
+	 * espresso_admin_footer
787
+	 *
788
+	 * @return    string
789
+	 */
790
+	public function espresso_admin_footer()
791
+	{
792
+		return EEH_Template::powered_by_event_espresso('aln-cntr', '', ['utm_content' => 'admin_footer']);
793
+	}
794
+
795
+
796
+	/**
797
+	 * Hooks into the "post states" filter in a wp post type list table.
798
+	 *
799
+	 * @param array   $post_states
800
+	 * @param WP_Post $post
801
+	 * @return array
802
+	 * @throws InvalidArgumentException
803
+	 * @throws InvalidDataTypeException
804
+	 * @throws InvalidInterfaceException
805
+	 */
806
+	public function displayStateForCriticalPages($post_states, $post)
807
+	{
808
+		$post_states = (array) $post_states;
809
+		if (! $post instanceof WP_Post || $post->post_type !== 'page') {
810
+			return $post_states;
811
+		}
812
+		/** @var EE_Core_Config $config */
813
+		$config = $this->loader->getShared('EE_Config')->core;
814
+		if (in_array($post->ID, $config->get_critical_pages_array(), true)) {
815
+			$post_states[] = sprintf(
816
+			/* Translators: Using company name - Event Espresso Critical Page */
817
+				esc_html__('%s Critical Page', 'event_espresso'),
818
+				'Event Espresso'
819
+			);
820
+		}
821
+		return $post_states;
822
+	}
823
+
824
+
825
+	/**
826
+	 * Show documentation links on the plugins page
827
+	 *
828
+	 * @param mixed $meta Plugin Row Meta
829
+	 * @param mixed $file Plugin Base file
830
+	 * @return array
831
+	 */
832
+	public function addLinksToPluginRowMeta($meta, $file)
833
+	{
834
+		if (EE_PLUGIN_BASENAME === $file) {
835
+			$row_meta = [
836
+				'docs' => '<a href="https://eventespresso.com/support/documentation/versioned-docs/?doc_ver=ee4"'
837
+						  . ' aria-label="'
838
+						  . esc_attr__('View Event Espresso documentation', 'event_espresso')
839
+						  . '">'
840
+						  . esc_html__('Docs', 'event_espresso')
841
+						  . '</a>',
842
+				'api'  => '<a href="https://github.com/eventespresso/event-espresso-core/tree/master/docs/C--REST-API"'
843
+						  . ' aria-label="'
844
+						  . esc_attr__('View Event Espresso API docs', 'event_espresso')
845
+						  . '">'
846
+						  . esc_html__('API docs', 'event_espresso')
847
+						  . '</a>',
848
+			];
849
+			return array_merge($meta, $row_meta);
850
+		}
851
+		return (array) $meta;
852
+	}
853
+
854
+	 /**************************************************************************************/
855
+	 /************************************* DEPRECATED *************************************/
856
+	 /**************************************************************************************/
857
+
858
+
859
+	/**
860
+	 * This is the action hook for the AHEE__EE_Admin_Page__route_admin_request hook that fires off right before an
861
+	 * EE_Admin_Page route is called.
862
+	 *
863
+	 * @return void
864
+	 */
865
+	public function route_admin_request()
866
+	{
867
+	}
868
+
869
+
870
+	/**
871
+	 * wp_loaded should fire on the WordPress wp_loaded hook.  This fires on a VERY late priority.
872
+	 *
873
+	 * @return void
874
+	 */
875
+	public function wp_loaded()
876
+	{
877
+	}
878
+
879
+
880
+	/**
881
+	 * static method for registering ee admin page.
882
+	 * This method is deprecated in favor of the new location in EE_Register_Admin_Page::register.
883
+	 *
884
+	 * @param       $page_basename
885
+	 * @param       $page_path
886
+	 * @param array $config
887
+	 * @return void
888
+	 * @throws EE_Error
889
+	 * @see        EE_Register_Admin_Page::register()
890
+	 * @since      4.3.0
891
+	 * @deprecated 4.3.0    Use EE_Register_Admin_Page::register() instead
892
+	 */
893
+	public static function register_ee_admin_page($page_basename, $page_path, $config = [])
894
+	{
895
+		EE_Error::doing_it_wrong(
896
+			__METHOD__,
897
+			sprintf(
898
+				esc_html__(
899
+					'Usage is deprecated.  Use EE_Register_Admin_Page::register() for registering the %s admin page.',
900
+					'event_espresso'
901
+				),
902
+				$page_basename
903
+			),
904
+			'4.3'
905
+		);
906
+		if (class_exists('EE_Register_Admin_Page')) {
907
+			$config['page_path'] = $page_path;
908
+		}
909
+		EE_Register_Admin_Page::register($page_basename, $config);
910
+	}
911
+
912
+
913
+	/**
914
+	 * @param int     $post_ID
915
+	 * @param WP_Post $post
916
+	 * @return void
917
+	 * @deprecated 4.8.41
918
+	 */
919
+	public static function parse_post_content_on_save($post_ID, $post)
920
+	{
921
+		EE_Error::doing_it_wrong(
922
+			__METHOD__,
923
+			esc_html__('Usage is deprecated', 'event_espresso'),
924
+			'4.8.41'
925
+		);
926
+	}
927
+
928
+
929
+	/**
930
+	 * @param  $option
931
+	 * @param  $old_value
932
+	 * @param  $value
933
+	 * @return void
934
+	 * @deprecated 4.8.41
935
+	 */
936
+	public function reset_page_for_posts_on_change($option, $old_value, $value)
937
+	{
938
+		EE_Error::doing_it_wrong(
939
+			__METHOD__,
940
+			esc_html__('Usage is deprecated', 'event_espresso'),
941
+			'4.8.41'
942
+		);
943
+	}
944
+
945
+
946
+	/**
947
+	 * @return void
948
+	 * @deprecated 4.9.27
949
+	 */
950
+	public function get_persistent_admin_notices()
951
+	{
952
+		EE_Error::doing_it_wrong(
953
+			__METHOD__,
954
+			sprintf(
955
+				esc_html__('Usage is deprecated. Use "%1$s" instead.', 'event_espresso'),
956
+				'\EventEspresso\core\services\notifications\PersistentAdminNoticeManager'
957
+			),
958
+			'4.9.27'
959
+		);
960
+	}
961
+
962
+
963
+	/**
964
+	 * @throws InvalidInterfaceException
965
+	 * @throws InvalidDataTypeException
966
+	 * @throws DomainException
967
+	 * @deprecated 4.9.27
968
+	 */
969
+	public function dismiss_ee_nag_notice_callback()
970
+	{
971
+		EE_Error::doing_it_wrong(
972
+			__METHOD__,
973
+			sprintf(
974
+				esc_html__('Usage is deprecated. Use "%1$s" instead.', 'event_espresso'),
975
+				'\EventEspresso\core\services\notifications\PersistentAdminNoticeManager'
976
+			),
977
+			'4.9.27'
978
+		);
979
+		$this->persistent_admin_notice_manager->dismissNotice();
980
+	}
981
+
982
+
983
+	/**
984
+	 * @return void
985
+	 * @deprecated 5.0.0.p
986
+	 */
987
+	public function enqueue_admin_scripts()
988
+	{
989
+	}
990
+
991
+
992
+
993
+	/**
994
+	 * @return RequestInterface
995
+	 * @deprecated 5.0.0.p
996
+	 */
997
+	public function get_request()
998
+	{
999
+		EE_Error::doing_it_wrong(
1000
+			__METHOD__,
1001
+			sprintf(
1002
+				esc_html__('Usage is deprecated. Use "%1$s" instead.', 'event_espresso'),
1003
+				'EventEspresso\core\services\request\Request'
1004
+			),
1005
+			'5.0.0.p'
1006
+		);
1007
+		return $this->request;
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * @deprecated 5.0.0.p
1013
+	 */
1014
+	public function hookIntoWpPluginsPage()
1015
+	{
1016
+	}
1017 1017
 }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page_CPT.core.php 2 patches
Indentation   +1416 added lines, -1416 removed lines patch added patch discarded remove patch
@@ -31,442 +31,442 @@  discard block
 block discarded – undo
31 31
  */
32 32
 abstract class EE_Admin_Page_CPT extends EE_Admin_Page
33 33
 {
34
-    /**
35
-     * @var EE_CPT_Base|null
36
-     */
37
-    protected $_cpt_model_obj;
38
-
39
-    protected ?WP_Post_Type $_cpt_object = null;
40
-
41
-
42
-    /**
43
-     * This property allows cpt classes to define multiple routes as cpt routes.
44
-     * //in this array we define what the custom post type for this route is.
45
-     * array(
46
-     * 'route_name' => 'custom_post_type_slug'
47
-     * )
48
-     *
49
-     * @var array
50
-     */
51
-    protected array $_cpt_routes = [];
52
-
53
-
54
-    /**
55
-     * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
56
-     * in this format:
57
-     * array(
58
-     * 'post_type_slug' => 'edit_route'
59
-     * )
60
-     *
61
-     * @var array
62
-     */
63
-    protected array $_cpt_edit_routes = [];
64
-
65
-
66
-    /**
67
-     * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
68
-     * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
69
-     * _cpt_model_names property should be in the following format: array(
70
-     * 'route_defined_by_action_param' => 'Model_Name')
71
-     *
72
-     * @var array $_cpt_model_names
73
-     */
74
-    protected array $_cpt_model_names = [];
75
-
76
-
77
-    /**
78
-     * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
79
-     * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
80
-     * the _register_autosave_containers() method so that we don't override any other containers already registered.
81
-     * Registration of containers should be done before load_page_dependencies() is run.
82
-     *
83
-     * @var array
84
-     */
85
-    protected array $_autosave_containers = [];
86
-
87
-    protected array $_autosave_fields     = [];
88
-
89
-    /**
90
-     * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
91
-     * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
92
-     *
93
-     * @var array
94
-     */
95
-    protected array $_pagenow_map = [];
96
-
97
-
98
-    /**
99
-     * This is the route that will be used for the edit post route.
100
-     *
101
-     * @var string
102
-     */
103
-    protected string $cpt_editpost_route = 'edit';
104
-
105
-
106
-    /**
107
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
108
-     * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
109
-     * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
110
-     * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
111
-     * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
112
-     *
113
-     * @abstract
114
-     * @param string  $post_id The ID of the cpt that was saved (so you can link relationally)
115
-     * @param WP_Post $post    The post object of the cpt that was saved.
116
-     * @return void
117
-     */
118
-    abstract protected function _insert_update_cpt_item($post_id, $post);
119
-
120
-
121
-    /**
122
-     * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
123
-     *
124
-     * @abstract
125
-     * @param string $post_id The ID of the cpt that was trashed
126
-     * @return void
127
-     */
128
-    abstract public function trash_cpt_item($post_id);
129
-
130
-
131
-    /**
132
-     * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
133
-     *
134
-     * @param string $post_id theID of the cpt that was untrashed
135
-     * @return void
136
-     */
137
-    abstract public function restore_cpt_item($post_id);
138
-
139
-
140
-    /**
141
-     * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
142
-     * from the db
143
-     *
144
-     * @param string $post_id the ID of the cpt that was deleted
145
-     * @return void
146
-     */
147
-    abstract public function delete_cpt_item($post_id);
148
-
149
-
150
-    /**
151
-     * @return LoaderInterface
152
-     * @throws InvalidArgumentException
153
-     * @throws InvalidDataTypeException
154
-     * @throws InvalidInterfaceException
155
-     */
156
-    protected function getLoader(): LoaderInterface
157
-    {
158
-        if (! $this->loader instanceof LoaderInterface) {
159
-            $this->loader = LoaderFactory::getLoader();
160
-        }
161
-        return $this->loader;
162
-    }
163
-
164
-
165
-    /**
166
-     * Just utilizing the method EE_Admin exposes for doing things before page setup.
167
-     *
168
-     * @return void
169
-     */
170
-    protected function _before_page_setup()
171
-    {
172
-        $this->raw_req_action = $this->request->getRequestParam('action');
173
-        $this->raw_req_page   = $this->request->getRequestParam('page');
174
-        $this->_cpt_routes    = array_merge(
175
-            [
176
-                'create_new' => $this->page_slug,
177
-                'edit'       => $this->page_slug,
178
-                'trash'      => $this->page_slug,
179
-            ],
180
-            $this->_cpt_routes
181
-        );
182
-        $cpt_route_action     = $this->_cpt_routes[ $this->raw_req_action ] ?? null;
183
-        // let's see if the current route has a value for cpt_object_slug. if it does, we use that instead of the page
184
-        $page              = $this->raw_req_page ?: $this->page_slug;
185
-        $page              = $cpt_route_action ?: $page;
186
-        $this->_cpt_object = get_post_type_object($page);
187
-        // tweak pagenow for page loading.
188
-        if (empty($this->_pagenow_map)) {
189
-            $this->_pagenow_map = [
190
-                'create_new' => 'post-new.php',
191
-                'edit'       => 'post.php',
192
-                'trash'      => 'post.php',
193
-            ];
194
-        }
195
-        add_action('current_screen', [$this, 'modify_pagenow']);
196
-        // TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
197
-        // get current page from autosave
198
-        $current_page        = $this->request->getRequestParam('ee_autosave_data[ee-cpt-hidden-inputs][current_page]');
199
-        $this->_current_page = $this->request->getRequestParam('current_page', $current_page);
200
-    }
201
-
202
-
203
-    /**
204
-     * Simply ensure that we simulate the correct post route for cpt screens
205
-     *
206
-     * @param WP_Screen|null $current_screen
207
-     * @return void
208
-     */
209
-    public function modify_pagenow(?WP_Screen $current_screen)
210
-    {
211
-        // possibly reset pagenow.
212
-        if (
213
-            $this->page_slug === $this->raw_req_page
214
-            && isset($this->_pagenow_map[ $this->raw_req_action ])
215
-        ) {
216
-            global $pagenow, $hook_suffix;
217
-            $pagenow     = $this->_pagenow_map[ $this->raw_req_action ];
218
-            $hook_suffix = $pagenow;
219
-        }
220
-    }
221
-
222
-
223
-    /**
224
-     * This method is used to register additional autosave containers to the _autosave_containers property.
225
-     *
226
-     * @param array $ids  an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
227
-     *                    you would send along the id of a metabox container.
228
-     * @return void
229
-     * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
230
-     *                    automatically register the id for the post metabox as a container.
231
-     */
232
-    protected function _register_autosave_containers($ids)
233
-    {
234
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
235
-    }
236
-
237
-
238
-    /**
239
-     * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
240
-     * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
241
-     */
242
-    protected function _set_autosave_containers()
243
-    {
244
-        global $wp_meta_boxes;
245
-        $containers = [];
246
-        if (empty($wp_meta_boxes)) {
247
-            return;
248
-        }
249
-        $current_metaboxes = $wp_meta_boxes[ $this->page_slug ] ?? [];
250
-        foreach ($current_metaboxes as $box_context) {
251
-            foreach ($box_context as $box_details) {
252
-                foreach ($box_details as $box) {
253
-                    if (
254
-                        is_array($box) && is_array($box['callback'])
255
-                        && (
256
-                            $box['callback'][0] instanceof EE_Admin_Page
257
-                            || $box['callback'][0] instanceof EE_Admin_Hooks
258
-                        )
259
-                    ) {
260
-                        $containers[] = $box['id'];
261
-                    }
262
-                }
263
-            }
264
-        }
265
-        $this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
266
-        // add hidden inputs container
267
-        $this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
268
-    }
269
-
270
-
271
-    protected function _load_autosave_scripts_styles()
272
-    {
273
-        /*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
+	 * @var EE_CPT_Base|null
36
+	 */
37
+	protected $_cpt_model_obj;
38
+
39
+	protected ?WP_Post_Type $_cpt_object = null;
40
+
41
+
42
+	/**
43
+	 * This property allows cpt classes to define multiple routes as cpt routes.
44
+	 * //in this array we define what the custom post type for this route is.
45
+	 * array(
46
+	 * 'route_name' => 'custom_post_type_slug'
47
+	 * )
48
+	 *
49
+	 * @var array
50
+	 */
51
+	protected array $_cpt_routes = [];
52
+
53
+
54
+	/**
55
+	 * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
56
+	 * in this format:
57
+	 * array(
58
+	 * 'post_type_slug' => 'edit_route'
59
+	 * )
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected array $_cpt_edit_routes = [];
64
+
65
+
66
+	/**
67
+	 * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
68
+	 * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
69
+	 * _cpt_model_names property should be in the following format: array(
70
+	 * 'route_defined_by_action_param' => 'Model_Name')
71
+	 *
72
+	 * @var array $_cpt_model_names
73
+	 */
74
+	protected array $_cpt_model_names = [];
75
+
76
+
77
+	/**
78
+	 * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
79
+	 * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
80
+	 * the _register_autosave_containers() method so that we don't override any other containers already registered.
81
+	 * Registration of containers should be done before load_page_dependencies() is run.
82
+	 *
83
+	 * @var array
84
+	 */
85
+	protected array $_autosave_containers = [];
86
+
87
+	protected array $_autosave_fields     = [];
88
+
89
+	/**
90
+	 * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
91
+	 * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
92
+	 *
93
+	 * @var array
94
+	 */
95
+	protected array $_pagenow_map = [];
96
+
97
+
98
+	/**
99
+	 * This is the route that will be used for the edit post route.
100
+	 *
101
+	 * @var string
102
+	 */
103
+	protected string $cpt_editpost_route = 'edit';
104
+
105
+
106
+	/**
107
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
108
+	 * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
109
+	 * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
110
+	 * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
111
+	 * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
112
+	 *
113
+	 * @abstract
114
+	 * @param string  $post_id The ID of the cpt that was saved (so you can link relationally)
115
+	 * @param WP_Post $post    The post object of the cpt that was saved.
116
+	 * @return void
117
+	 */
118
+	abstract protected function _insert_update_cpt_item($post_id, $post);
119
+
120
+
121
+	/**
122
+	 * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
123
+	 *
124
+	 * @abstract
125
+	 * @param string $post_id The ID of the cpt that was trashed
126
+	 * @return void
127
+	 */
128
+	abstract public function trash_cpt_item($post_id);
129
+
130
+
131
+	/**
132
+	 * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
133
+	 *
134
+	 * @param string $post_id theID of the cpt that was untrashed
135
+	 * @return void
136
+	 */
137
+	abstract public function restore_cpt_item($post_id);
138
+
139
+
140
+	/**
141
+	 * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
142
+	 * from the db
143
+	 *
144
+	 * @param string $post_id the ID of the cpt that was deleted
145
+	 * @return void
146
+	 */
147
+	abstract public function delete_cpt_item($post_id);
148
+
149
+
150
+	/**
151
+	 * @return LoaderInterface
152
+	 * @throws InvalidArgumentException
153
+	 * @throws InvalidDataTypeException
154
+	 * @throws InvalidInterfaceException
155
+	 */
156
+	protected function getLoader(): LoaderInterface
157
+	{
158
+		if (! $this->loader instanceof LoaderInterface) {
159
+			$this->loader = LoaderFactory::getLoader();
160
+		}
161
+		return $this->loader;
162
+	}
163
+
164
+
165
+	/**
166
+	 * Just utilizing the method EE_Admin exposes for doing things before page setup.
167
+	 *
168
+	 * @return void
169
+	 */
170
+	protected function _before_page_setup()
171
+	{
172
+		$this->raw_req_action = $this->request->getRequestParam('action');
173
+		$this->raw_req_page   = $this->request->getRequestParam('page');
174
+		$this->_cpt_routes    = array_merge(
175
+			[
176
+				'create_new' => $this->page_slug,
177
+				'edit'       => $this->page_slug,
178
+				'trash'      => $this->page_slug,
179
+			],
180
+			$this->_cpt_routes
181
+		);
182
+		$cpt_route_action     = $this->_cpt_routes[ $this->raw_req_action ] ?? null;
183
+		// let's see if the current route has a value for cpt_object_slug. if it does, we use that instead of the page
184
+		$page              = $this->raw_req_page ?: $this->page_slug;
185
+		$page              = $cpt_route_action ?: $page;
186
+		$this->_cpt_object = get_post_type_object($page);
187
+		// tweak pagenow for page loading.
188
+		if (empty($this->_pagenow_map)) {
189
+			$this->_pagenow_map = [
190
+				'create_new' => 'post-new.php',
191
+				'edit'       => 'post.php',
192
+				'trash'      => 'post.php',
193
+			];
194
+		}
195
+		add_action('current_screen', [$this, 'modify_pagenow']);
196
+		// TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
197
+		// get current page from autosave
198
+		$current_page        = $this->request->getRequestParam('ee_autosave_data[ee-cpt-hidden-inputs][current_page]');
199
+		$this->_current_page = $this->request->getRequestParam('current_page', $current_page);
200
+	}
201
+
202
+
203
+	/**
204
+	 * Simply ensure that we simulate the correct post route for cpt screens
205
+	 *
206
+	 * @param WP_Screen|null $current_screen
207
+	 * @return void
208
+	 */
209
+	public function modify_pagenow(?WP_Screen $current_screen)
210
+	{
211
+		// possibly reset pagenow.
212
+		if (
213
+			$this->page_slug === $this->raw_req_page
214
+			&& isset($this->_pagenow_map[ $this->raw_req_action ])
215
+		) {
216
+			global $pagenow, $hook_suffix;
217
+			$pagenow     = $this->_pagenow_map[ $this->raw_req_action ];
218
+			$hook_suffix = $pagenow;
219
+		}
220
+	}
221
+
222
+
223
+	/**
224
+	 * This method is used to register additional autosave containers to the _autosave_containers property.
225
+	 *
226
+	 * @param array $ids  an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
227
+	 *                    you would send along the id of a metabox container.
228
+	 * @return void
229
+	 * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
230
+	 *                    automatically register the id for the post metabox as a container.
231
+	 */
232
+	protected function _register_autosave_containers($ids)
233
+	{
234
+		$this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
235
+	}
236
+
237
+
238
+	/**
239
+	 * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
240
+	 * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
241
+	 */
242
+	protected function _set_autosave_containers()
243
+	{
244
+		global $wp_meta_boxes;
245
+		$containers = [];
246
+		if (empty($wp_meta_boxes)) {
247
+			return;
248
+		}
249
+		$current_metaboxes = $wp_meta_boxes[ $this->page_slug ] ?? [];
250
+		foreach ($current_metaboxes as $box_context) {
251
+			foreach ($box_context as $box_details) {
252
+				foreach ($box_details as $box) {
253
+					if (
254
+						is_array($box) && is_array($box['callback'])
255
+						&& (
256
+							$box['callback'][0] instanceof EE_Admin_Page
257
+							|| $box['callback'][0] instanceof EE_Admin_Hooks
258
+						)
259
+					) {
260
+						$containers[] = $box['id'];
261
+					}
262
+				}
263
+			}
264
+		}
265
+		$this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
266
+		// add hidden inputs container
267
+		$this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
268
+	}
269
+
270
+
271
+	protected function _load_autosave_scripts_styles()
272
+	{
273
+		/*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 );
274 274
         wp_enqueue_script('cpt-autosave');/**/ // todo re-enable when we start doing autosave again in 4.2
275 275
 
276
-        // todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
277
-        wp_localize_script(
278
-            'event_editor_js',
279
-            'EE_AUTOSAVE_IDS',
280
-            $this->_autosave_containers
281
-        );
282
-
283
-        wp_localize_script(
284
-            'event_editor_js',
285
-            'UNSAVED_DATA_MSG',
286
-            [
287
-                'eventMsg'     => sprintf(
288
-                    wp_strip_all_tags(
289
-                        __(
290
-                            "The changes you made to this %s will be lost if you navigate away from this page.",
291
-                            'event_espresso'
292
-                        )
293
-                    ),
294
-                    $this->_cpt_object->labels->singular_name
295
-                ),
296
-                'inputChanged' => 0,
297
-            ]
298
-        );
299
-    }
300
-
301
-
302
-    /**
303
-     * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
304
-     *
305
-     * @return void
306
-     * @throws EE_Error
307
-     * @throws ReflectionException
308
-     * @throws Throwable
309
-     */
310
-    protected function _load_page_dependencies()
311
-    {
312
-        // we only add stuff if this is a cpt_route!
313
-        if (! $this->_cpt_route) {
314
-            parent::_load_page_dependencies();
315
-            return;
316
-        }
317
-        // now let's do some automatic filters into the wp_system
318
-        // and we'll check to make sure the CHILD class
319
-        // automatically has the required methods in place.
320
-        // the following filters are for setting all the redirects
321
-        // on DEFAULT WP custom post type actions
322
-        // let's add a hidden input to the post-edit form
323
-        // so we know when we have to trigger our custom redirects!
324
-        // Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
325
-        add_action('edit_form_after_title', [$this, 'cpt_post_form_hidden_input']);
326
-        // inject our Admin page nav tabs...
327
-        // let's make sure the nav tabs are set if they aren't already
328
-        // if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
329
-        add_action('edit_form_top', [$this, 'inject_nav_tabs']);
330
-        // modify the post_updated messages array
331
-        add_action('post_updated_messages', [$this, 'post_update_messages']);
332
-        // This basically allows us to change the title of the "publish" metabox area
333
-        // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
334
-        $screen = $this->_cpt_routes[ $this->_req_action ];
335
-        if (! empty($this->_labels['publishbox'])) {
336
-            $this->addMetaBox(
337
-                'submitdiv',
338
-                $this->getPublishBoxTitle(),
339
-                'post_submit_meta_box',
340
-                $screen,
341
-                'side',
342
-                'core'
343
-            );
344
-        }
345
-        // let's add page_templates metabox if this cpt added support for it.
346
-        if ($this->_supports_page_templates($this->_cpt_object->name)) {
347
-            $this->addMetaBox(
348
-                'page_templates',
349
-                esc_html__('Page Template', 'event_espresso'),
350
-                [$this, 'page_template_meta_box'],
351
-                $screen,
352
-                'side'
353
-            );
354
-        }
355
-        // add preview button
356
-        // add_filter('get_sample_permalink_html', [PreviewButton::class, 'addButton'], 5, 2);
357
-        PreviewButton::addEventEditorPermalinkButton(5);
358
-        // add shortlink button to cpt edit screens.
359
-        //  We can do this as a universal thing BECAUSE, cpts use the same format for shortlinks as posts!
360
-        // add_filter('get_sample_permalink_html', [EventShortlinkButton::class, 'addButton'], 10, 2);
361
-        EventShortlinkButton::addEventEditorPermalinkButton();
362
-        // this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
363
-        // add_filter('get_sample_permalink_html', [TicketSelectorShortcodeButton::class, 'addButton'], 12, 2);
364
-        TicketSelectorShortcodeButton::addEventEditorPermalinkButton(12);
365
-        // insert our own post_stati dropdown
366
-        add_action('post_submitbox_misc_actions', [$this, 'custom_post_stati_dropdown']);
367
-        // This allows adding additional information to the publish post submitbox on the wp post edit form
368
-        if (method_exists($this, 'extra_misc_actions_publish_box')) {
369
-            add_action('post_submitbox_misc_actions', [$this, 'extra_misc_actions_publish_box']);
370
-        }
371
-        // This allows for adding additional stuff after the title field on the wp post edit form.
372
-        // This is also before the wp_editor for post description field.
373
-        if (method_exists($this, 'edit_form_after_title')) {
374
-            add_action('edit_form_after_title', [$this, 'edit_form_after_title']);
375
-        }
376
-        /**
377
-         * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
378
-         */
379
-        add_filter('clean_url', [$this, 'switch_core_wp_urls_with_ours']);
380
-        parent::_load_page_dependencies();
381
-        // notice we are ALSO going to load the pagenow hook set for this route
382
-        // (see _before_page_setup for the reset of the pagenow global ).
383
-        // This is for any plugins that are doing things properly
384
-        // and hooking into the load page hook for core wp cpt routes.
385
-        global $pagenow;
386
-        add_action('load-' . $pagenow, [$this, 'modify_current_screen'], 20);
387
-        do_action('load-' . $pagenow);
388
-        add_action('admin_enqueue_scripts', [$this, 'setup_autosave_hooks'], 30);
389
-        // we route REALLY early.
390
-        try {
391
-            $this->_route_admin_request();
392
-        } catch (EE_Error $e) {
393
-            $e->get_error();
394
-        }
395
-    }
396
-
397
-
398
-    /**
399
-     * Since we don't want users going to default core wp routes, this will check any wp urls run through the
400
-     * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
401
-     * route instead.
402
-     *
403
-     * @param string $good_protocol_url The escaped url.
404
-     * @return string possibly a new url for our route.
405
-     */
406
-    public function switch_core_wp_urls_with_ours(string $good_protocol_url): string
407
-    {
408
-        $routes_to_match = [
409
-            0 => [
410
-                'edit.php?post_type=espresso_attendees',
411
-                'admin.php?page=espresso_registrations&action=contact_list',
412
-            ],
413
-            1 => [
414
-                'edit.php?post_type=' . $this->_cpt_object->name,
415
-                'admin.php?page=' . $this->_cpt_object->name,
416
-            ],
417
-        ];
418
-        foreach ($routes_to_match as $route_matches) {
419
-            if (strpos($good_protocol_url, $route_matches[0]) !== false) {
420
-                return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
421
-            }
422
-        }
423
-        return $good_protocol_url;
424
-    }
425
-
426
-
427
-    /**
428
-     * Determine whether the current cpt supports page templates or not.
429
-     *
430
-     * @param string $cpt_name The cpt slug we're checking on.
431
-     * @return bool True supported, false not.
432
-     * @throws InvalidArgumentException
433
-     * @throws InvalidDataTypeException
434
-     * @throws InvalidInterfaceException
435
-     */
436
-    private function _supports_page_templates(string $cpt_name): bool
437
-    {
438
-        /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
439
-        $custom_post_types = $this->loader->getShared(
440
-            'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
441
-        );
442
-        $cpt_args          = $custom_post_types->getDefinitions();
443
-        $cpt_args          = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : [];
444
-        $cpt_has_support   = ! empty($cpt_args['page_templates']);
445
-
446
-        $post_templates = wp_get_theme()->get_post_templates();
447
-        // if there are $post_templates for this cpt, then we return false for this method because
448
-        // that means we aren't going to load our page template manager and leave that up to the native
449
-        // cpt template manager.
450
-        return ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
451
-    }
452
-
453
-
454
-    /**
455
-     * Callback for the page_templates metabox selector.
456
-     *
457
-     * @return void
458
-     */
459
-    public function page_template_meta_box()
460
-    {
461
-        global $post;
462
-        $template = '';
463
-
464
-        $page_template_count = count(get_page_templates());
465
-        if ($page_template_count) {
466
-            $page_template = get_post_meta($post->ID, '_wp_page_template', true);
467
-            $template      = ! empty($page_template) ? $page_template : '';
468
-        }
469
-        ?>
276
+		// todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
277
+		wp_localize_script(
278
+			'event_editor_js',
279
+			'EE_AUTOSAVE_IDS',
280
+			$this->_autosave_containers
281
+		);
282
+
283
+		wp_localize_script(
284
+			'event_editor_js',
285
+			'UNSAVED_DATA_MSG',
286
+			[
287
+				'eventMsg'     => sprintf(
288
+					wp_strip_all_tags(
289
+						__(
290
+							"The changes you made to this %s will be lost if you navigate away from this page.",
291
+							'event_espresso'
292
+						)
293
+					),
294
+					$this->_cpt_object->labels->singular_name
295
+				),
296
+				'inputChanged' => 0,
297
+			]
298
+		);
299
+	}
300
+
301
+
302
+	/**
303
+	 * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
304
+	 *
305
+	 * @return void
306
+	 * @throws EE_Error
307
+	 * @throws ReflectionException
308
+	 * @throws Throwable
309
+	 */
310
+	protected function _load_page_dependencies()
311
+	{
312
+		// we only add stuff if this is a cpt_route!
313
+		if (! $this->_cpt_route) {
314
+			parent::_load_page_dependencies();
315
+			return;
316
+		}
317
+		// now let's do some automatic filters into the wp_system
318
+		// and we'll check to make sure the CHILD class
319
+		// automatically has the required methods in place.
320
+		// the following filters are for setting all the redirects
321
+		// on DEFAULT WP custom post type actions
322
+		// let's add a hidden input to the post-edit form
323
+		// so we know when we have to trigger our custom redirects!
324
+		// Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
325
+		add_action('edit_form_after_title', [$this, 'cpt_post_form_hidden_input']);
326
+		// inject our Admin page nav tabs...
327
+		// let's make sure the nav tabs are set if they aren't already
328
+		// if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
329
+		add_action('edit_form_top', [$this, 'inject_nav_tabs']);
330
+		// modify the post_updated messages array
331
+		add_action('post_updated_messages', [$this, 'post_update_messages']);
332
+		// This basically allows us to change the title of the "publish" metabox area
333
+		// on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
334
+		$screen = $this->_cpt_routes[ $this->_req_action ];
335
+		if (! empty($this->_labels['publishbox'])) {
336
+			$this->addMetaBox(
337
+				'submitdiv',
338
+				$this->getPublishBoxTitle(),
339
+				'post_submit_meta_box',
340
+				$screen,
341
+				'side',
342
+				'core'
343
+			);
344
+		}
345
+		// let's add page_templates metabox if this cpt added support for it.
346
+		if ($this->_supports_page_templates($this->_cpt_object->name)) {
347
+			$this->addMetaBox(
348
+				'page_templates',
349
+				esc_html__('Page Template', 'event_espresso'),
350
+				[$this, 'page_template_meta_box'],
351
+				$screen,
352
+				'side'
353
+			);
354
+		}
355
+		// add preview button
356
+		// add_filter('get_sample_permalink_html', [PreviewButton::class, 'addButton'], 5, 2);
357
+		PreviewButton::addEventEditorPermalinkButton(5);
358
+		// add shortlink button to cpt edit screens.
359
+		//  We can do this as a universal thing BECAUSE, cpts use the same format for shortlinks as posts!
360
+		// add_filter('get_sample_permalink_html', [EventShortlinkButton::class, 'addButton'], 10, 2);
361
+		EventShortlinkButton::addEventEditorPermalinkButton();
362
+		// this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
363
+		// add_filter('get_sample_permalink_html', [TicketSelectorShortcodeButton::class, 'addButton'], 12, 2);
364
+		TicketSelectorShortcodeButton::addEventEditorPermalinkButton(12);
365
+		// insert our own post_stati dropdown
366
+		add_action('post_submitbox_misc_actions', [$this, 'custom_post_stati_dropdown']);
367
+		// This allows adding additional information to the publish post submitbox on the wp post edit form
368
+		if (method_exists($this, 'extra_misc_actions_publish_box')) {
369
+			add_action('post_submitbox_misc_actions', [$this, 'extra_misc_actions_publish_box']);
370
+		}
371
+		// This allows for adding additional stuff after the title field on the wp post edit form.
372
+		// This is also before the wp_editor for post description field.
373
+		if (method_exists($this, 'edit_form_after_title')) {
374
+			add_action('edit_form_after_title', [$this, 'edit_form_after_title']);
375
+		}
376
+		/**
377
+		 * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
378
+		 */
379
+		add_filter('clean_url', [$this, 'switch_core_wp_urls_with_ours']);
380
+		parent::_load_page_dependencies();
381
+		// notice we are ALSO going to load the pagenow hook set for this route
382
+		// (see _before_page_setup for the reset of the pagenow global ).
383
+		// This is for any plugins that are doing things properly
384
+		// and hooking into the load page hook for core wp cpt routes.
385
+		global $pagenow;
386
+		add_action('load-' . $pagenow, [$this, 'modify_current_screen'], 20);
387
+		do_action('load-' . $pagenow);
388
+		add_action('admin_enqueue_scripts', [$this, 'setup_autosave_hooks'], 30);
389
+		// we route REALLY early.
390
+		try {
391
+			$this->_route_admin_request();
392
+		} catch (EE_Error $e) {
393
+			$e->get_error();
394
+		}
395
+	}
396
+
397
+
398
+	/**
399
+	 * Since we don't want users going to default core wp routes, this will check any wp urls run through the
400
+	 * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
401
+	 * route instead.
402
+	 *
403
+	 * @param string $good_protocol_url The escaped url.
404
+	 * @return string possibly a new url for our route.
405
+	 */
406
+	public function switch_core_wp_urls_with_ours(string $good_protocol_url): string
407
+	{
408
+		$routes_to_match = [
409
+			0 => [
410
+				'edit.php?post_type=espresso_attendees',
411
+				'admin.php?page=espresso_registrations&action=contact_list',
412
+			],
413
+			1 => [
414
+				'edit.php?post_type=' . $this->_cpt_object->name,
415
+				'admin.php?page=' . $this->_cpt_object->name,
416
+			],
417
+		];
418
+		foreach ($routes_to_match as $route_matches) {
419
+			if (strpos($good_protocol_url, $route_matches[0]) !== false) {
420
+				return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
421
+			}
422
+		}
423
+		return $good_protocol_url;
424
+	}
425
+
426
+
427
+	/**
428
+	 * Determine whether the current cpt supports page templates or not.
429
+	 *
430
+	 * @param string $cpt_name The cpt slug we're checking on.
431
+	 * @return bool True supported, false not.
432
+	 * @throws InvalidArgumentException
433
+	 * @throws InvalidDataTypeException
434
+	 * @throws InvalidInterfaceException
435
+	 */
436
+	private function _supports_page_templates(string $cpt_name): bool
437
+	{
438
+		/** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
439
+		$custom_post_types = $this->loader->getShared(
440
+			'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
441
+		);
442
+		$cpt_args          = $custom_post_types->getDefinitions();
443
+		$cpt_args          = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : [];
444
+		$cpt_has_support   = ! empty($cpt_args['page_templates']);
445
+
446
+		$post_templates = wp_get_theme()->get_post_templates();
447
+		// if there are $post_templates for this cpt, then we return false for this method because
448
+		// that means we aren't going to load our page template manager and leave that up to the native
449
+		// cpt template manager.
450
+		return ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
451
+	}
452
+
453
+
454
+	/**
455
+	 * Callback for the page_templates metabox selector.
456
+	 *
457
+	 * @return void
458
+	 */
459
+	public function page_template_meta_box()
460
+	{
461
+		global $post;
462
+		$template = '';
463
+
464
+		$page_template_count = count(get_page_templates());
465
+		if ($page_template_count) {
466
+			$page_template = get_post_meta($post->ID, '_wp_page_template', true);
467
+			$template      = ! empty($page_template) ? $page_template : '';
468
+		}
469
+		?>
470 470
         <p><strong><?php esc_html_e('Template', 'event_espresso') ?></strong></p>
471 471
         <label class="screen-reader-text" for="page_template">
472 472
             <?php esc_html_e('Page Template', 'event_espresso') ?>
@@ -476,457 +476,457 @@  discard block
 block discarded – undo
476 476
             <?php page_template_dropdown($template); ?>
477 477
         </select>
478 478
         <?php
479
-    }
480
-
481
-
482
-    /**
483
-     * if this post is a draft or scheduled post then we provide a preview button for user to click
484
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
485
-     *
486
-     * @param string $return    the current html
487
-     * @param int    $id        the post id for the page
488
-     * @return string            The new html string for the permalink area
489
-     * @deprecated 5.0.0.p
490
-     * @see PreviewButton::addButton()
491
-     */
492
-    public function preview_button_html(string $return, int $id): string
493
-    {
494
-        return PreviewButton::addButton($return, $id);
495
-    }
496
-
497
-
498
-    /**
499
-     * add our custom post status dropdown on the wp post page for this cpt
500
-     *
501
-     * @return void
502
-     */
503
-    public function custom_post_stati_dropdown()
504
-    {
505
-        $statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
506
-        $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
507
-            ? $statuses[ $this->_cpt_model_obj->status() ]
508
-            : '';
509
-        $template_args    = [
510
-            'cur_status'            => $this->_cpt_model_obj->status(),
511
-            'statuses'              => $statuses,
512
-            'cur_status_label'      => $cur_status_label,
513
-            'localized_status_save' => sprintf(esc_html__('Save %s', 'event_espresso'), $cur_status_label),
514
-        ];
515
-        // we'll add a trash post status (WP doesn't add one for some reason)
516
-        if ($this->_cpt_model_obj->status() === 'trash') {
517
-            $template_args['cur_status_label'] = esc_html__('Trashed', 'event_espresso');
518
-            $statuses['trash']                 = esc_html__('Trashed', 'event_espresso');
519
-            $template_args['statuses']         = $statuses;
520
-        }
521
-
522
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
523
-        EEH_Template::display_template($template, $template_args);
524
-    }
525
-
526
-
527
-    public function setup_autosave_hooks()
528
-    {
529
-        $this->_set_autosave_containers();
530
-        $this->_load_autosave_scripts_styles();
531
-    }
532
-
533
-
534
-    /**
535
-     * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a post object (available
536
-     * in request data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
537
-     * for the nonce in here, but then this method looks for two things:
538
-     * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
539
-     * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
540
-     * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
541
-     * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
542
-     * template args.
543
-     *    1. $template_args['error'] = IF there is an error you can add the message in here.
544
-     *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
545
-     *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
546
-     *    $this->_template_args['data']['items'] = array(
547
-     *        'event-datetime-ids' => '1,2,3';
548
-     *    );
549
-     *    Keep in mind the following things:
550
-     *    - "where" index is for the input with the id as that string.
551
-     *    - "what" index is what will be used for the value of that input.
552
-     *
553
-     * @return void
554
-     * @throws EE_Error
555
-     */
556
-    public function do_extra_autosave_stuff()
557
-    {
558
-        // next let's check for the autosave nonce (we'll use _verify_nonce )
559
-        $nonce = $this->request->getRequestParam('autosavenonce');
560
-        $this->_verify_nonce($nonce, 'autosave');
561
-        // make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
562
-        if (! defined('DOING_AUTOSAVE')) {
563
-            define('DOING_AUTOSAVE', true);
564
-        }
565
-        // if we made it here then the nonce checked out.  Let's run our methods and actions
566
-        $autosave = "_ee_autosave_$this->_current_view";
567
-        if (method_exists($this, $autosave)) {
568
-            $this->$autosave();
569
-        } else {
570
-            $this->_template_args['success'] = true;
571
-        }
572
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
573
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
574
-        // now let's return json
575
-        $this->_return_json();
576
-    }
577
-
578
-
579
-    /**
580
-     * This takes care of setting up default routes and pages that utilize the core WP admin pages.
581
-     * Child classes can override the defaults (in cases for adding metaboxes etc.)
582
-     * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
583
-     *
584
-     * @return void
585
-     * @throws EE_Error
586
-     * @throws ReflectionException
587
-     */
588
-    protected function _extend_page_config_for_cpt()
589
-    {
590
-        // before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
591
-        if ($this->raw_req_page !== $this->page_slug) {
592
-            return;
593
-        }
594
-        // set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
595
-        if (! empty($this->_cpt_object)) {
596
-            $this->_page_routes = array_merge(
597
-                [
598
-                    'create_new' => [$this, '_create_new_cpt_item'],
599
-                    'edit'       => [$this, '_edit_cpt_item'],
600
-                ],
601
-                $this->_page_routes
602
-            );
603
-            $this->_page_config = array_merge(
604
-                [
605
-                    'create_new' => [
606
-                        'nav'           => [
607
-                            'label' => $this->_cpt_object->labels->add_new_item,
608
-                            'order' => 5,
609
-                        ],
610
-                        'require_nonce' => false,
611
-                    ],
612
-                    'edit'       => [
613
-                        'nav'           => [
614
-                            'label'      => $this->_cpt_object->labels->edit_item,
615
-                            'order'      => 5,
616
-                            'persistent' => false,
617
-                            'url'        => '',
618
-                        ],
619
-                        'require_nonce' => false,
620
-                    ],
621
-                ],
622
-                $this->_page_config
623
-            );
624
-        }
625
-        // load the next section only if this is a matching cpt route as set in the cpt routes array.
626
-        if (! isset($this->_cpt_routes[ $this->_req_action ])) {
627
-            return;
628
-        }
629
-        $this->_cpt_route = true;
630
-        // $this->_cpt_route = isset($this->_cpt_routes[ $this->_req_action ]);
631
-        // add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
632
-        if (empty($this->_cpt_object)) {
633
-            $msg = sprintf(
634
-                esc_html__(
635
-                    '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).',
636
-                    'event_espresso'
637
-                ),
638
-                $this->page_slug,
639
-                $this->_req_action,
640
-                get_class($this)
641
-            );
642
-            throw new EE_Error($msg);
643
-        }
644
-        $this->_set_model_object($this->request->getRequestParam('post', 0, DataType::INT));
645
-    }
646
-
647
-
648
-    /**
649
-     * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
650
-     *
651
-     * @param int    $id       The id to retrieve the model object for. If empty we set a default object.
652
-     * @param bool   $ignore_route_check
653
-     * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
654
-     * @throws EE_Error
655
-     * @throws InvalidArgumentException
656
-     * @throws InvalidDataTypeException
657
-     * @throws InvalidInterfaceException
658
-     * @throws ReflectionException
659
-     */
660
-    protected function _set_model_object(int $id = 0, bool $ignore_route_check = false, string $req_type = '')
661
-    {
662
-        $model = null;
663
-        if (
664
-            empty($this->_cpt_model_names)
665
-            || (
666
-                ! $ignore_route_check
667
-                && ! isset($this->_cpt_routes[ $this->_req_action ])
668
-            )
669
-            || (
670
-                $this->_cpt_model_obj instanceof EE_CPT_Base
671
-                && $this->_cpt_model_obj->ID() === $id
672
-            )
673
-        ) {
674
-            // 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.
675
-            return;
676
-        }
677
-        // if ignore_route_check is true, then get the model name via CustomPostTypeDefinitions
678
-        if ($ignore_route_check) {
679
-            $post_type = get_post_type($id);
680
-            /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
681
-            $custom_post_types = $this->loader->getShared(
682
-                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
683
-            );
684
-            $model_names       = $custom_post_types->getCustomPostTypeModelNames($post_type);
685
-            if (isset($model_names[ $post_type ])) {
686
-                $model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
687
-            }
688
-        } else {
689
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
690
-        }
691
-        if ($model instanceof EEM_Base) {
692
-            $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
693
-        }
694
-        do_action(
695
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
696
-            $this->_cpt_model_obj,
697
-            $req_type
698
-        );
699
-    }
700
-
701
-
702
-    /**
703
-     * admin_init_global
704
-     * This runs all the code that we want executed within the WP admin_init hook.
705
-     * This method executes for ALL EE Admin pages.
706
-     *
707
-     * @return void
708
-     */
709
-    public function admin_init_global()
710
-    {
711
-        $post_ID = $this->request->getRequestParam('post', 0, DataType::INT);
712
-        // its possible this is a new save so let's catch that instead
713
-        $post_ID        = $this->request->getRequestParam('post_ID', $post_ID, DataType::INT);
714
-        $post           = get_post($post_ID);
715
-        $post_type      = $post instanceof WP_Post ? $post->post_type : false;
716
-        $current_route  = $this->request->getRequestParam('current_route', 'shouldneverwork');
717
-        $route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
718
-            ? $this->_cpt_routes[ $current_route ]
719
-            : '';
720
-        add_filter('get_delete_post_link', [$this, 'modify_delete_post_link'], 10, 2);
721
-        add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
722
-        if ($post_type === $route_to_check) {
723
-            add_filter('redirect_post_location', [$this, 'cpt_post_location_redirect'], 10, 2);
724
-        }
725
-        // now let's filter redirect if we're on a revision page and the revision is for an event CPT.
726
-        $revision = $this->request->getRequestParam('revision');
727
-        if (! empty($revision)) {
728
-            $action = $this->request->getRequestParam('action');
729
-            // doing a restore?
730
-            if (! empty($action) && $action === 'restore') {
731
-                // get post for revision
732
-                $rev_post   = get_post($revision);
733
-                $rev_parent = get_post($rev_post->post_parent);
734
-                // only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
735
-                if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
736
-                    add_filter('wp_redirect', [$this, 'revision_redirect']);
737
-                    // restores of revisions
738
-                    add_action('wp_restore_post_revision', [$this, 'restore_revision'], 10, 2);
739
-                }
740
-            }
741
-        }
742
-        // 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!
743
-        if ($post_type && $post_type === $route_to_check) {
744
-            // $post_id, $post
745
-            add_action('save_post', [$this, 'insert_update'], 10, 3);
746
-            // $post_id
747
-            add_action('trashed_post', [$this, 'before_trash_cpt_item']);
748
-            add_action('trashed_post', [$this, 'dont_permanently_delete_ee_cpts']);
749
-            add_action('untrashed_post', [$this, 'before_restore_cpt_item']);
750
-            add_action('after_delete_post', [$this, 'before_delete_cpt_item']);
751
-        }
752
-    }
753
-
754
-
755
-    /**
756
-     * Callback for the WordPress trashed_post hook.
757
-     * Execute some basic checks before calling the trash_cpt_item declared in the child class.
758
-     *
759
-     * @param int $post_id
760
-     * @throws EE_Error
761
-     * @throws ReflectionException
762
-     */
763
-    public function before_trash_cpt_item(int $post_id)
764
-    {
765
-        $this->_set_model_object($post_id, true, 'trash');
766
-        // if our cpt object isn't existent then get out immediately.
767
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
768
-            return;
769
-        }
770
-        $this->trash_cpt_item($post_id);
771
-    }
772
-
773
-
774
-    /**
775
-     * Callback for the WordPress untrashed_post hook.
776
-     * Execute some basic checks before calling the restore_cpt_method in the child class.
777
-     *
778
-     * @param $post_id
779
-     * @throws EE_Error
780
-     * @throws ReflectionException
781
-     */
782
-    public function before_restore_cpt_item($post_id)
783
-    {
784
-        $this->_set_model_object($post_id, true, 'restore');
785
-        // if our cpt object isn't existent then get out immediately.
786
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
787
-            return;
788
-        }
789
-        $this->restore_cpt_item($post_id);
790
-    }
791
-
792
-
793
-    /**
794
-     * Callback for the WordPress after_delete_post hook.
795
-     * Execute some basic checks before calling the delete_cpt_item method in the child class.
796
-     *
797
-     * @param $post_id
798
-     * @throws EE_Error
799
-     * @throws ReflectionException
800
-     */
801
-    public function before_delete_cpt_item($post_id)
802
-    {
803
-        $this->_set_model_object($post_id, true, 'delete');
804
-        // if our cpt object isn't existent then get out immediately.
805
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
806
-            return;
807
-        }
808
-        $this->delete_cpt_item($post_id);
809
-    }
810
-
811
-
812
-    /**
813
-     * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
814
-     * accordingly.
815
-     *
816
-     * @return void
817
-     * @throws EE_Error
818
-     * @throws ReflectionException
819
-     */
820
-    public function verify_cpt_object()
821
-    {
822
-        $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
823
-        // verify event object
824
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
825
-            throw new EE_Error(
826
-                sprintf(
827
-                    esc_html__(
828
-                        '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',
829
-                        'event_espresso'
830
-                    ),
831
-                    $label
832
-                )
833
-            );
834
-        }
835
-        // if auto-draft then throw an error
836
-        if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
837
-            EE_Error::overwrite_errors();
838
-            EE_Error::add_error(
839
-                sprintf(
840
-                    esc_html__(
841
-                        '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.',
842
-                        'event_espresso'
843
-                    ),
844
-                    $label
845
-                ),
846
-                __FILE__,
847
-                __FUNCTION__,
848
-                __LINE__
849
-            );
850
-        }
851
-    }
852
-
853
-
854
-    /**
855
-     * admin_footer_scripts_global
856
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
857
-     * will apply on ALL EE_Admin pages.
858
-     *
859
-     * @return void
860
-     */
861
-    public function admin_footer_scripts_global()
862
-    {
863
-        $this->_add_admin_page_ajax_loading_img();
864
-        $this->_add_admin_page_overlay();
865
-    }
866
-
867
-
868
-    /**
869
-     * add in any global scripts for cpt routes
870
-     *
871
-     * @return void
872
-     */
873
-    public function load_global_scripts_styles()
874
-    {
875
-        parent::load_global_scripts_styles();
876
-        if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
877
-            // setup custom post status object for localize script but only if we've got a cpt object
878
-            $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
879
-            if (! empty($statuses)) {
880
-                // get ALL statuses!
881
-                $statuses = $this->_cpt_model_obj->get_all_post_statuses();
882
-                // setup object
883
-                $ee_cpt_statuses = [];
884
-                foreach ($statuses as $status => $label) {
885
-                    $ee_cpt_statuses[ $status ] = [
886
-                        'label'      => $label,
887
-                        'save_label' => sprintf(
888
-                            wp_strip_all_tags(__('Save as %s', 'event_espresso')),
889
-                            $label
890
-                        ),
891
-                    ];
892
-                }
893
-                wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
894
-            }
895
-        }
896
-    }
897
-
898
-
899
-    /**
900
-     * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
901
-     * insert/updates
902
-     *
903
-     * @param int     $post_id ID of post being updated
904
-     * @param WP_Post $post    Post object from WP
905
-     * @param bool    $update  Whether this is an update or a new save.
906
-     * @return void
907
-     * @throws EE_Error
908
-     * @throws ReflectionException
909
-     */
910
-    public function insert_update(int $post_id, WP_Post $post, bool $update)
911
-    {
912
-        // make sure that if this is a revision OR trash action that we don't do any updates!
913
-        $action = $this->request->getRequestParam('action');
914
-        if ($action === 'restore' || $action === 'trash') {
915
-            return;
916
-        }
917
-        $this->_set_model_object($post_id, true, 'insert_update');
918
-        // if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
919
-        if (
920
-            $update
921
-            && (
922
-                ! $this->_cpt_model_obj instanceof EE_CPT_Base
923
-                || $this->_cpt_model_obj->ID() !== $post_id
924
-            )
925
-        ) {
926
-            return;
927
-        }
928
-        // check for autosave and update our req_data property accordingly.
929
-        /*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
479
+	}
480
+
481
+
482
+	/**
483
+	 * if this post is a draft or scheduled post then we provide a preview button for user to click
484
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
485
+	 *
486
+	 * @param string $return    the current html
487
+	 * @param int    $id        the post id for the page
488
+	 * @return string            The new html string for the permalink area
489
+	 * @deprecated 5.0.0.p
490
+	 * @see PreviewButton::addButton()
491
+	 */
492
+	public function preview_button_html(string $return, int $id): string
493
+	{
494
+		return PreviewButton::addButton($return, $id);
495
+	}
496
+
497
+
498
+	/**
499
+	 * add our custom post status dropdown on the wp post page for this cpt
500
+	 *
501
+	 * @return void
502
+	 */
503
+	public function custom_post_stati_dropdown()
504
+	{
505
+		$statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
506
+		$cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
507
+			? $statuses[ $this->_cpt_model_obj->status() ]
508
+			: '';
509
+		$template_args    = [
510
+			'cur_status'            => $this->_cpt_model_obj->status(),
511
+			'statuses'              => $statuses,
512
+			'cur_status_label'      => $cur_status_label,
513
+			'localized_status_save' => sprintf(esc_html__('Save %s', 'event_espresso'), $cur_status_label),
514
+		];
515
+		// we'll add a trash post status (WP doesn't add one for some reason)
516
+		if ($this->_cpt_model_obj->status() === 'trash') {
517
+			$template_args['cur_status_label'] = esc_html__('Trashed', 'event_espresso');
518
+			$statuses['trash']                 = esc_html__('Trashed', 'event_espresso');
519
+			$template_args['statuses']         = $statuses;
520
+		}
521
+
522
+		$template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
523
+		EEH_Template::display_template($template, $template_args);
524
+	}
525
+
526
+
527
+	public function setup_autosave_hooks()
528
+	{
529
+		$this->_set_autosave_containers();
530
+		$this->_load_autosave_scripts_styles();
531
+	}
532
+
533
+
534
+	/**
535
+	 * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a post object (available
536
+	 * in request data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
537
+	 * for the nonce in here, but then this method looks for two things:
538
+	 * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
539
+	 * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
540
+	 * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
541
+	 * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
542
+	 * template args.
543
+	 *    1. $template_args['error'] = IF there is an error you can add the message in here.
544
+	 *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
545
+	 *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
546
+	 *    $this->_template_args['data']['items'] = array(
547
+	 *        'event-datetime-ids' => '1,2,3';
548
+	 *    );
549
+	 *    Keep in mind the following things:
550
+	 *    - "where" index is for the input with the id as that string.
551
+	 *    - "what" index is what will be used for the value of that input.
552
+	 *
553
+	 * @return void
554
+	 * @throws EE_Error
555
+	 */
556
+	public function do_extra_autosave_stuff()
557
+	{
558
+		// next let's check for the autosave nonce (we'll use _verify_nonce )
559
+		$nonce = $this->request->getRequestParam('autosavenonce');
560
+		$this->_verify_nonce($nonce, 'autosave');
561
+		// make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
562
+		if (! defined('DOING_AUTOSAVE')) {
563
+			define('DOING_AUTOSAVE', true);
564
+		}
565
+		// if we made it here then the nonce checked out.  Let's run our methods and actions
566
+		$autosave = "_ee_autosave_$this->_current_view";
567
+		if (method_exists($this, $autosave)) {
568
+			$this->$autosave();
569
+		} else {
570
+			$this->_template_args['success'] = true;
571
+		}
572
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
573
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
574
+		// now let's return json
575
+		$this->_return_json();
576
+	}
577
+
578
+
579
+	/**
580
+	 * This takes care of setting up default routes and pages that utilize the core WP admin pages.
581
+	 * Child classes can override the defaults (in cases for adding metaboxes etc.)
582
+	 * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
583
+	 *
584
+	 * @return void
585
+	 * @throws EE_Error
586
+	 * @throws ReflectionException
587
+	 */
588
+	protected function _extend_page_config_for_cpt()
589
+	{
590
+		// before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
591
+		if ($this->raw_req_page !== $this->page_slug) {
592
+			return;
593
+		}
594
+		// set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
595
+		if (! empty($this->_cpt_object)) {
596
+			$this->_page_routes = array_merge(
597
+				[
598
+					'create_new' => [$this, '_create_new_cpt_item'],
599
+					'edit'       => [$this, '_edit_cpt_item'],
600
+				],
601
+				$this->_page_routes
602
+			);
603
+			$this->_page_config = array_merge(
604
+				[
605
+					'create_new' => [
606
+						'nav'           => [
607
+							'label' => $this->_cpt_object->labels->add_new_item,
608
+							'order' => 5,
609
+						],
610
+						'require_nonce' => false,
611
+					],
612
+					'edit'       => [
613
+						'nav'           => [
614
+							'label'      => $this->_cpt_object->labels->edit_item,
615
+							'order'      => 5,
616
+							'persistent' => false,
617
+							'url'        => '',
618
+						],
619
+						'require_nonce' => false,
620
+					],
621
+				],
622
+				$this->_page_config
623
+			);
624
+		}
625
+		// load the next section only if this is a matching cpt route as set in the cpt routes array.
626
+		if (! isset($this->_cpt_routes[ $this->_req_action ])) {
627
+			return;
628
+		}
629
+		$this->_cpt_route = true;
630
+		// $this->_cpt_route = isset($this->_cpt_routes[ $this->_req_action ]);
631
+		// add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
632
+		if (empty($this->_cpt_object)) {
633
+			$msg = sprintf(
634
+				esc_html__(
635
+					'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).',
636
+					'event_espresso'
637
+				),
638
+				$this->page_slug,
639
+				$this->_req_action,
640
+				get_class($this)
641
+			);
642
+			throw new EE_Error($msg);
643
+		}
644
+		$this->_set_model_object($this->request->getRequestParam('post', 0, DataType::INT));
645
+	}
646
+
647
+
648
+	/**
649
+	 * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
650
+	 *
651
+	 * @param int    $id       The id to retrieve the model object for. If empty we set a default object.
652
+	 * @param bool   $ignore_route_check
653
+	 * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
654
+	 * @throws EE_Error
655
+	 * @throws InvalidArgumentException
656
+	 * @throws InvalidDataTypeException
657
+	 * @throws InvalidInterfaceException
658
+	 * @throws ReflectionException
659
+	 */
660
+	protected function _set_model_object(int $id = 0, bool $ignore_route_check = false, string $req_type = '')
661
+	{
662
+		$model = null;
663
+		if (
664
+			empty($this->_cpt_model_names)
665
+			|| (
666
+				! $ignore_route_check
667
+				&& ! isset($this->_cpt_routes[ $this->_req_action ])
668
+			)
669
+			|| (
670
+				$this->_cpt_model_obj instanceof EE_CPT_Base
671
+				&& $this->_cpt_model_obj->ID() === $id
672
+			)
673
+		) {
674
+			// 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.
675
+			return;
676
+		}
677
+		// if ignore_route_check is true, then get the model name via CustomPostTypeDefinitions
678
+		if ($ignore_route_check) {
679
+			$post_type = get_post_type($id);
680
+			/** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
681
+			$custom_post_types = $this->loader->getShared(
682
+				'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
683
+			);
684
+			$model_names       = $custom_post_types->getCustomPostTypeModelNames($post_type);
685
+			if (isset($model_names[ $post_type ])) {
686
+				$model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
687
+			}
688
+		} else {
689
+			$model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
690
+		}
691
+		if ($model instanceof EEM_Base) {
692
+			$this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
693
+		}
694
+		do_action(
695
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
696
+			$this->_cpt_model_obj,
697
+			$req_type
698
+		);
699
+	}
700
+
701
+
702
+	/**
703
+	 * admin_init_global
704
+	 * This runs all the code that we want executed within the WP admin_init hook.
705
+	 * This method executes for ALL EE Admin pages.
706
+	 *
707
+	 * @return void
708
+	 */
709
+	public function admin_init_global()
710
+	{
711
+		$post_ID = $this->request->getRequestParam('post', 0, DataType::INT);
712
+		// its possible this is a new save so let's catch that instead
713
+		$post_ID        = $this->request->getRequestParam('post_ID', $post_ID, DataType::INT);
714
+		$post           = get_post($post_ID);
715
+		$post_type      = $post instanceof WP_Post ? $post->post_type : false;
716
+		$current_route  = $this->request->getRequestParam('current_route', 'shouldneverwork');
717
+		$route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
718
+			? $this->_cpt_routes[ $current_route ]
719
+			: '';
720
+		add_filter('get_delete_post_link', [$this, 'modify_delete_post_link'], 10, 2);
721
+		add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
722
+		if ($post_type === $route_to_check) {
723
+			add_filter('redirect_post_location', [$this, 'cpt_post_location_redirect'], 10, 2);
724
+		}
725
+		// now let's filter redirect if we're on a revision page and the revision is for an event CPT.
726
+		$revision = $this->request->getRequestParam('revision');
727
+		if (! empty($revision)) {
728
+			$action = $this->request->getRequestParam('action');
729
+			// doing a restore?
730
+			if (! empty($action) && $action === 'restore') {
731
+				// get post for revision
732
+				$rev_post   = get_post($revision);
733
+				$rev_parent = get_post($rev_post->post_parent);
734
+				// only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
735
+				if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
736
+					add_filter('wp_redirect', [$this, 'revision_redirect']);
737
+					// restores of revisions
738
+					add_action('wp_restore_post_revision', [$this, 'restore_revision'], 10, 2);
739
+				}
740
+			}
741
+		}
742
+		// 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!
743
+		if ($post_type && $post_type === $route_to_check) {
744
+			// $post_id, $post
745
+			add_action('save_post', [$this, 'insert_update'], 10, 3);
746
+			// $post_id
747
+			add_action('trashed_post', [$this, 'before_trash_cpt_item']);
748
+			add_action('trashed_post', [$this, 'dont_permanently_delete_ee_cpts']);
749
+			add_action('untrashed_post', [$this, 'before_restore_cpt_item']);
750
+			add_action('after_delete_post', [$this, 'before_delete_cpt_item']);
751
+		}
752
+	}
753
+
754
+
755
+	/**
756
+	 * Callback for the WordPress trashed_post hook.
757
+	 * Execute some basic checks before calling the trash_cpt_item declared in the child class.
758
+	 *
759
+	 * @param int $post_id
760
+	 * @throws EE_Error
761
+	 * @throws ReflectionException
762
+	 */
763
+	public function before_trash_cpt_item(int $post_id)
764
+	{
765
+		$this->_set_model_object($post_id, true, 'trash');
766
+		// if our cpt object isn't existent then get out immediately.
767
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
768
+			return;
769
+		}
770
+		$this->trash_cpt_item($post_id);
771
+	}
772
+
773
+
774
+	/**
775
+	 * Callback for the WordPress untrashed_post hook.
776
+	 * Execute some basic checks before calling the restore_cpt_method in the child class.
777
+	 *
778
+	 * @param $post_id
779
+	 * @throws EE_Error
780
+	 * @throws ReflectionException
781
+	 */
782
+	public function before_restore_cpt_item($post_id)
783
+	{
784
+		$this->_set_model_object($post_id, true, 'restore');
785
+		// if our cpt object isn't existent then get out immediately.
786
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
787
+			return;
788
+		}
789
+		$this->restore_cpt_item($post_id);
790
+	}
791
+
792
+
793
+	/**
794
+	 * Callback for the WordPress after_delete_post hook.
795
+	 * Execute some basic checks before calling the delete_cpt_item method in the child class.
796
+	 *
797
+	 * @param $post_id
798
+	 * @throws EE_Error
799
+	 * @throws ReflectionException
800
+	 */
801
+	public function before_delete_cpt_item($post_id)
802
+	{
803
+		$this->_set_model_object($post_id, true, 'delete');
804
+		// if our cpt object isn't existent then get out immediately.
805
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
806
+			return;
807
+		}
808
+		$this->delete_cpt_item($post_id);
809
+	}
810
+
811
+
812
+	/**
813
+	 * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
814
+	 * accordingly.
815
+	 *
816
+	 * @return void
817
+	 * @throws EE_Error
818
+	 * @throws ReflectionException
819
+	 */
820
+	public function verify_cpt_object()
821
+	{
822
+		$label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
823
+		// verify event object
824
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
825
+			throw new EE_Error(
826
+				sprintf(
827
+					esc_html__(
828
+						'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',
829
+						'event_espresso'
830
+					),
831
+					$label
832
+				)
833
+			);
834
+		}
835
+		// if auto-draft then throw an error
836
+		if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
837
+			EE_Error::overwrite_errors();
838
+			EE_Error::add_error(
839
+				sprintf(
840
+					esc_html__(
841
+						'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.',
842
+						'event_espresso'
843
+					),
844
+					$label
845
+				),
846
+				__FILE__,
847
+				__FUNCTION__,
848
+				__LINE__
849
+			);
850
+		}
851
+	}
852
+
853
+
854
+	/**
855
+	 * admin_footer_scripts_global
856
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
857
+	 * will apply on ALL EE_Admin pages.
858
+	 *
859
+	 * @return void
860
+	 */
861
+	public function admin_footer_scripts_global()
862
+	{
863
+		$this->_add_admin_page_ajax_loading_img();
864
+		$this->_add_admin_page_overlay();
865
+	}
866
+
867
+
868
+	/**
869
+	 * add in any global scripts for cpt routes
870
+	 *
871
+	 * @return void
872
+	 */
873
+	public function load_global_scripts_styles()
874
+	{
875
+		parent::load_global_scripts_styles();
876
+		if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
877
+			// setup custom post status object for localize script but only if we've got a cpt object
878
+			$statuses = $this->_cpt_model_obj->get_custom_post_statuses();
879
+			if (! empty($statuses)) {
880
+				// get ALL statuses!
881
+				$statuses = $this->_cpt_model_obj->get_all_post_statuses();
882
+				// setup object
883
+				$ee_cpt_statuses = [];
884
+				foreach ($statuses as $status => $label) {
885
+					$ee_cpt_statuses[ $status ] = [
886
+						'label'      => $label,
887
+						'save_label' => sprintf(
888
+							wp_strip_all_tags(__('Save as %s', 'event_espresso')),
889
+							$label
890
+						),
891
+					];
892
+				}
893
+				wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
894
+			}
895
+		}
896
+	}
897
+
898
+
899
+	/**
900
+	 * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
901
+	 * insert/updates
902
+	 *
903
+	 * @param int     $post_id ID of post being updated
904
+	 * @param WP_Post $post    Post object from WP
905
+	 * @param bool    $update  Whether this is an update or a new save.
906
+	 * @return void
907
+	 * @throws EE_Error
908
+	 * @throws ReflectionException
909
+	 */
910
+	public function insert_update(int $post_id, WP_Post $post, bool $update)
911
+	{
912
+		// make sure that if this is a revision OR trash action that we don't do any updates!
913
+		$action = $this->request->getRequestParam('action');
914
+		if ($action === 'restore' || $action === 'trash') {
915
+			return;
916
+		}
917
+		$this->_set_model_object($post_id, true, 'insert_update');
918
+		// if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
919
+		if (
920
+			$update
921
+			&& (
922
+				! $this->_cpt_model_obj instanceof EE_CPT_Base
923
+				|| $this->_cpt_model_obj->ID() !== $post_id
924
+			)
925
+		) {
926
+			return;
927
+		}
928
+		// check for autosave and update our req_data property accordingly.
929
+		/*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
930 930
             foreach( (array) $this->_req_data['ee_autosave_data'] as $id => $values ) {
931 931
 
932 932
                 foreach ( (array) $values as $key => $value ) {
@@ -936,548 +936,548 @@  discard block
 block discarded – undo
936 936
 
937 937
         }/**/ // TODO reactivate after autosave is implemented in 4.2
938 938
 
939
-        // take care of updating any selected page_template IF this cpt supports it.
940
-
941
-        $page_template = $this->request->getRequestParam('page_template');
942
-        if ($this->_supports_page_templates($post->post_type) && ! empty($page_template)) {
943
-            // wp version aware.
944
-            if (RecommendedVersions::compareWordPressVersion('4.7')) {
945
-                $page_templates = wp_get_theme()->get_page_templates();
946
-            } else {
947
-                $post->page_template = $page_template;
948
-                $page_templates      = wp_get_theme()->get_page_templates($post);
949
-            }
950
-            if ($page_template !== 'default' && ! isset($page_templates[ $page_template ])) {
951
-                EE_Error::add_error(
952
-                    esc_html__('Invalid Page Template.', 'event_espresso'),
953
-                    __FILE__,
954
-                    __FUNCTION__,
955
-                    __LINE__
956
-                );
957
-            } else {
958
-                update_post_meta($post_id, '_wp_page_template', $page_template);
959
-            }
960
-        }
961
-        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
962
-            return;
963
-        } //TODO we'll remove this after reimplementing autosave in 4.2
964
-        $this->_insert_update_cpt_item($post_id, $post);
965
-    }
966
-
967
-
968
-    /**
969
-     * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
970
-     * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
971
-     * so we don't have to check for our CPT.
972
-     *
973
-     * @param int $post_id ID of the post
974
-     * @return void
975
-     */
976
-    public function dont_permanently_delete_ee_cpts(int $post_id)
977
-    {
978
-        // only do this if we're actually processing one of our CPTs
979
-        // if our cpt object isn't existent then get out immediately.
980
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
981
-            return;
982
-        }
983
-        delete_post_meta($post_id, '_wp_trash_meta_status');
984
-        delete_post_meta($post_id, '_wp_trash_meta_time');
985
-        // our cpts may have comments so let's take care of that too
986
-        delete_post_meta($post_id, '_wp_trash_meta_comments_status');
987
-    }
988
-
989
-
990
-    /**
991
-     * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
992
-     * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
993
-     * in them. We also have our OWN action in here so addons can hook into the restore process easily.
994
-     *
995
-     * @param int $post_id     ID of cpt item
996
-     * @param int $revision_id ID of revision being restored
997
-     * @return void
998
-     */
999
-    public function restore_revision(int $post_id, int $revision_id)
1000
-    {
1001
-        $this->_restore_cpt_item($post_id, $revision_id);
1002
-        // global action
1003
-        do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1004
-        // class specific action so you can limit hooking into a specific page.
1005
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1006
-    }
1007
-
1008
-
1009
-    /**
1010
-     * @param int $post_id     ID of cpt item
1011
-     * @param int $revision_id ID of revision for item
1012
-     * @return void
1013
-     * @see restore_revision() for details
1014
-     */
1015
-    abstract protected function _restore_cpt_item(int $post_id, int $revision_id);
1016
-
1017
-
1018
-    /**
1019
-     * Execution of this method is added to the end of the load_page_dependencies method in the parent
1020
-     * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1021
-     * To fix we have to reset the current_screen using the page_slug
1022
-     * (which is identical - or should be - to our registered_post_type id.)
1023
-     * Also, since the core WP file loads the admin_header.php for WP
1024
-     * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1025
-     * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1026
-     *
1027
-     * @return void
1028
-     * @throws EE_Error
1029
-     * @throws ReflectionException
1030
-     */
1031
-    public function modify_current_screen()
1032
-    {
1033
-        // ONLY do this if the current page_route IS a cpt route
1034
-        if (! $this->_cpt_route) {
1035
-            return;
1036
-        }
1037
-        // routing things REALLY early b/c this is a cpt admin page
1038
-        set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1039
-        $this->_current_screen       = get_current_screen();
1040
-        $this->_current_screen->base = 'event-espresso';
1041
-        $this->_add_help_tabs(); // we make sure we add any help tabs back in!
1042
-        /*try {
939
+		// take care of updating any selected page_template IF this cpt supports it.
940
+
941
+		$page_template = $this->request->getRequestParam('page_template');
942
+		if ($this->_supports_page_templates($post->post_type) && ! empty($page_template)) {
943
+			// wp version aware.
944
+			if (RecommendedVersions::compareWordPressVersion('4.7')) {
945
+				$page_templates = wp_get_theme()->get_page_templates();
946
+			} else {
947
+				$post->page_template = $page_template;
948
+				$page_templates      = wp_get_theme()->get_page_templates($post);
949
+			}
950
+			if ($page_template !== 'default' && ! isset($page_templates[ $page_template ])) {
951
+				EE_Error::add_error(
952
+					esc_html__('Invalid Page Template.', 'event_espresso'),
953
+					__FILE__,
954
+					__FUNCTION__,
955
+					__LINE__
956
+				);
957
+			} else {
958
+				update_post_meta($post_id, '_wp_page_template', $page_template);
959
+			}
960
+		}
961
+		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
962
+			return;
963
+		} //TODO we'll remove this after reimplementing autosave in 4.2
964
+		$this->_insert_update_cpt_item($post_id, $post);
965
+	}
966
+
967
+
968
+	/**
969
+	 * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
970
+	 * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
971
+	 * so we don't have to check for our CPT.
972
+	 *
973
+	 * @param int $post_id ID of the post
974
+	 * @return void
975
+	 */
976
+	public function dont_permanently_delete_ee_cpts(int $post_id)
977
+	{
978
+		// only do this if we're actually processing one of our CPTs
979
+		// if our cpt object isn't existent then get out immediately.
980
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
981
+			return;
982
+		}
983
+		delete_post_meta($post_id, '_wp_trash_meta_status');
984
+		delete_post_meta($post_id, '_wp_trash_meta_time');
985
+		// our cpts may have comments so let's take care of that too
986
+		delete_post_meta($post_id, '_wp_trash_meta_comments_status');
987
+	}
988
+
989
+
990
+	/**
991
+	 * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
992
+	 * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
993
+	 * in them. We also have our OWN action in here so addons can hook into the restore process easily.
994
+	 *
995
+	 * @param int $post_id     ID of cpt item
996
+	 * @param int $revision_id ID of revision being restored
997
+	 * @return void
998
+	 */
999
+	public function restore_revision(int $post_id, int $revision_id)
1000
+	{
1001
+		$this->_restore_cpt_item($post_id, $revision_id);
1002
+		// global action
1003
+		do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1004
+		// class specific action so you can limit hooking into a specific page.
1005
+		do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1006
+	}
1007
+
1008
+
1009
+	/**
1010
+	 * @param int $post_id     ID of cpt item
1011
+	 * @param int $revision_id ID of revision for item
1012
+	 * @return void
1013
+	 * @see restore_revision() for details
1014
+	 */
1015
+	abstract protected function _restore_cpt_item(int $post_id, int $revision_id);
1016
+
1017
+
1018
+	/**
1019
+	 * Execution of this method is added to the end of the load_page_dependencies method in the parent
1020
+	 * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1021
+	 * To fix we have to reset the current_screen using the page_slug
1022
+	 * (which is identical - or should be - to our registered_post_type id.)
1023
+	 * Also, since the core WP file loads the admin_header.php for WP
1024
+	 * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1025
+	 * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1026
+	 *
1027
+	 * @return void
1028
+	 * @throws EE_Error
1029
+	 * @throws ReflectionException
1030
+	 */
1031
+	public function modify_current_screen()
1032
+	{
1033
+		// ONLY do this if the current page_route IS a cpt route
1034
+		if (! $this->_cpt_route) {
1035
+			return;
1036
+		}
1037
+		// routing things REALLY early b/c this is a cpt admin page
1038
+		set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1039
+		$this->_current_screen       = get_current_screen();
1040
+		$this->_current_screen->base = 'event-espresso';
1041
+		$this->_add_help_tabs(); // we make sure we add any help tabs back in!
1042
+		/*try {
1043 1043
             $this->_route_admin_request();
1044 1044
         } catch ( EE_Error $e ) {
1045 1045
             $e->get_error();
1046 1046
         }/**/
1047
-    }
1048
-
1049
-
1050
-    /**
1051
-     * This allows child classes to modify the default editor title that appears when people add a new or edit an
1052
-     * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1053
-     * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1054
-     * default to be.
1055
-     *
1056
-     * @param string|null $title The new title (or existing if there is no editor_title defined)
1057
-     * @return string|null
1058
-     */
1059
-    public function add_custom_editor_default_title(?string $title): ?string
1060
-    {
1061
-        return $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ] ?? $title;
1062
-    }
1063
-
1064
-
1065
-    /**
1066
-     * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1067
-     *
1068
-     * @param string $shortlink   The already generated shortlink
1069
-     * @param int    $id          Post ID for this item
1070
-     * @return string
1071
-     * @deprecated 5.0.0.p
1072
-     * @see EventShortlinkButton::addButton()
1073
-     */
1074
-    public function add_shortlink_button_to_editor(string $shortlink, int $id): string
1075
-    {
1076
-        return EventShortlinkButton::addButton($shortlink, $id);
1077
-    }
1078
-
1079
-
1080
-    /**
1081
-     * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1082
-     * already run in modify_current_screen())
1083
-     *
1084
-     * @return void
1085
-     * @throws EE_Error
1086
-     * @throws ReflectionException
1087
-     * @throws Throwable
1088
-     */
1089
-    public function route_admin_request()
1090
-    {
1091
-        if ($this->_cpt_route) {
1092
-            return;
1093
-        }
1094
-        try {
1095
-            $this->_route_admin_request();
1096
-        } catch (EE_Error $e) {
1097
-            $e->get_error();
1098
-        }
1099
-    }
1100
-
1101
-
1102
-    /**
1103
-     * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1104
-     *
1105
-     * @return void
1106
-     */
1107
-    public function cpt_post_form_hidden_input()
1108
-    {
1109
-        // we're also going to add the route value and the current page so we can direct autosave parsing correctly
1110
-        echo '
1047
+	}
1048
+
1049
+
1050
+	/**
1051
+	 * This allows child classes to modify the default editor title that appears when people add a new or edit an
1052
+	 * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1053
+	 * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1054
+	 * default to be.
1055
+	 *
1056
+	 * @param string|null $title The new title (or existing if there is no editor_title defined)
1057
+	 * @return string|null
1058
+	 */
1059
+	public function add_custom_editor_default_title(?string $title): ?string
1060
+	{
1061
+		return $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ] ?? $title;
1062
+	}
1063
+
1064
+
1065
+	/**
1066
+	 * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1067
+	 *
1068
+	 * @param string $shortlink   The already generated shortlink
1069
+	 * @param int    $id          Post ID for this item
1070
+	 * @return string
1071
+	 * @deprecated 5.0.0.p
1072
+	 * @see EventShortlinkButton::addButton()
1073
+	 */
1074
+	public function add_shortlink_button_to_editor(string $shortlink, int $id): string
1075
+	{
1076
+		return EventShortlinkButton::addButton($shortlink, $id);
1077
+	}
1078
+
1079
+
1080
+	/**
1081
+	 * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1082
+	 * already run in modify_current_screen())
1083
+	 *
1084
+	 * @return void
1085
+	 * @throws EE_Error
1086
+	 * @throws ReflectionException
1087
+	 * @throws Throwable
1088
+	 */
1089
+	public function route_admin_request()
1090
+	{
1091
+		if ($this->_cpt_route) {
1092
+			return;
1093
+		}
1094
+		try {
1095
+			$this->_route_admin_request();
1096
+		} catch (EE_Error $e) {
1097
+			$e->get_error();
1098
+		}
1099
+	}
1100
+
1101
+
1102
+	/**
1103
+	 * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1104
+	 *
1105
+	 * @return void
1106
+	 */
1107
+	public function cpt_post_form_hidden_input()
1108
+	{
1109
+		// we're also going to add the route value and the current page so we can direct autosave parsing correctly
1110
+		echo '
1111 1111
         <input type="hidden" name="ee_cpt_item_redirect_url" value="' . esc_url_raw($this->_admin_base_url) . '"/>
1112 1112
         <div id="ee-cpt-hidden-inputs">
1113 1113
             <input type="hidden" id="current_route" name="current_route" value="' . esc_attr($this->_current_view) . '"/>
1114 1114
             <input type="hidden" id="current_page" name="current_page" value="' . esc_attr($this->page_slug) . '"/>
1115 1115
         </div>';
1116
-    }
1117
-
1118
-
1119
-    /**
1120
-     * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1121
-     *
1122
-     * @param string $location Original location url
1123
-     * @return string           new (or original) url to redirect to.
1124
-     * @throws EE_Error
1125
-     */
1126
-    public function revision_redirect(string $location): string
1127
-    {
1128
-        // get revision
1129
-        $revision = $this->request->getRequestParam('revision');
1130
-        // can't do anything without revision so let's get out if not present
1131
-        if (empty($revision)) {
1132
-            return $location;
1133
-        }
1134
-        // get rev_post_data
1135
-        $rev        = get_post($revision);
1136
-        $admin_url  = $this->_admin_base_url;
1137
-        $query_args = [
1138
-            'action'   => 'edit',
1139
-            'post'     => $rev->post_parent,
1140
-            'revision' => $revision,
1141
-            'message'  => 5,
1142
-        ];
1143
-        $this->_process_notices($query_args, true);
1144
-        return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $admin_url);
1145
-    }
1146
-
1147
-
1148
-    /**
1149
-     * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1150
-     *
1151
-     * @param string|null $link     the original generated link
1152
-     * @param int    $id            post id
1153
-     * @return string               the link
1154
-     */
1155
-    public function modify_edit_post_link(?string $link, int $id): ?string
1156
-    {
1157
-        $post = get_post($id);
1158
-        $action = $this->request->getRequestParam('action');
1159
-        if (
1160
-            empty($action)
1161
-            || ! isset($this->_cpt_routes[ $action ])
1162
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1163
-        ) {
1164
-            return $link;
1165
-        }
1166
-        $query_args = [
1167
-            'action' => $this->_cpt_edit_routes[ $post->post_type ] ?? 'edit',
1168
-            'post'   => $id,
1169
-        ];
1170
-        return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1171
-    }
1172
-
1173
-
1174
-    /**
1175
-     * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1176
-     * our routes.
1177
-     *
1178
-     * @param string|null $delete_link      original delete link
1179
-     * @param int    $post_id               id of cpt object
1180
-     * @return null|string                  new delete link
1181
-     * @throws EE_Error
1182
-     * @throws ReflectionException
1183
-     */
1184
-    public function modify_delete_post_link(?string $delete_link, int $post_id): ?string
1185
-    {
1186
-        $post = get_post($post_id);
1187
-        $action = $this->request->getRequestParam('action');
1188
-        if (
1189
-            ! $post instanceof WP_Post
1190
-            || empty($action)
1191
-            || ! isset($this->_cpt_routes[ $action ])
1192
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1193
-        ) {
1194
-            return $delete_link;
1195
-        }
1196
-        $this->_set_model_object($post->ID, true);
1197
-
1198
-        // returns something like `trash_event` or `trash_attendee` or `trash_venue`
1199
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1200
-
1201
-        return EE_Admin_Page::add_query_args_and_nonce(
1202
-            [
1203
-                'page'   => $this->request->getRequestParam('page'),
1204
-                'action' => $action,
1205
-                $this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name() => $post->ID,
1206
-            ],
1207
-            admin_url()
1208
-        );
1209
-    }
1210
-
1211
-
1212
-    /**
1213
-     * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1214
-     * so that we can hijack the default redirect locations for wp custom post types
1215
-     * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1216
-     *
1217
-     * @param string $location This is the incoming currently set redirect location
1218
-     * @param string $post_id  This is the 'ID' value of the wp_posts table
1219
-     * @return string           the new location to redirect to
1220
-     * @throws EE_Error
1221
-     */
1222
-    public function cpt_post_location_redirect(string $location, string $post_id): string
1223
-    {
1224
-        // we DO have a match so let's setup the url
1225
-        // we have to get the post to determine our route
1226
-        $post       = get_post($post_id);
1227
-        $edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1228
-        // shared query_args
1229
-        $query_args = ['action' => $edit_route, 'post' => $post_id];
1230
-
1231
-        $save = $this->request->getRequestParam('save');
1232
-        $publish = $this->request->getRequestParam('publish');
1233
-        $add_meta = $this->request->getRequestParam('addmeta');
1234
-        $delete_meta = $this->request->getRequestParam('deletemeta');
1235
-        if ($save || $publish) {
1236
-            $status = get_post_status($post_id);
1237
-            if ($publish) {
1238
-                switch ($status) {
1239
-                    case 'pending':
1240
-                        $message = 8;
1241
-                        break;
1242
-                    case 'future':
1243
-                        $message = 9;
1244
-                        break;
1245
-                    default:
1246
-                        $message = 6;
1247
-                }
1248
-            } else {
1249
-                $message = 'draft' === $status ? 10 : 1;
1250
-            }
1251
-        } elseif ($add_meta) {
1252
-            $message = 2;
1253
-        } elseif ($delete_meta) {
1254
-            $message = 3;
1255
-        } elseif ($this->request->getRequestParam('action') === 'post-quickpress-save-cont') {
1256
-            $message = 7;
1257
-        } else {
1258
-            $message = 4;
1259
-        }
1260
-        // change the message if the post type is not viewable on the frontend
1261
-        $this->_cpt_object = get_post_type_object($post->post_type);
1262
-
1263
-        $query_args['message'] = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1264
-        $this->_process_notices($query_args, true);
1265
-        return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1266
-    }
1267
-
1268
-
1269
-    /**
1270
-     * This method is called to inject nav tabs on core WP cpt pages
1271
-     *
1272
-     * @return void
1273
-     * @throws EE_Error
1274
-     */
1275
-    public function inject_nav_tabs()
1276
-    {
1277
-        echo wp_kses($this->_get_main_nav_tabs(), AllowedTags::getWithFormTags());
1278
-    }
1279
-
1280
-
1281
-    /**
1282
-     * This just sets up the post update messages when an update form is loaded
1283
-     *
1284
-     * @param array $messages the original messages array
1285
-     * @return array           the new messages array
1286
-     */
1287
-    public function post_update_messages(array $messages): array
1288
-    {
1289
-        global $post;
1290
-        $id       = $this->request->getRequestParam('post');
1291
-        $id       = empty($id) && is_object($post) ? $post->ID : null;
1292
-        $revision = $this->request->getRequestParam('revision', 0, 'int');
1293
-
1294
-        $messages[ $post->post_type ] = [
1295
-            0  => '', // Unused. Messages start at index 1.
1296
-            1  => sprintf(
1297
-                esc_html__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1298
-                $this->_cpt_object->labels->singular_name,
1299
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1300
-                '</a>'
1301
-            ),
1302
-            2  => esc_html__('Custom field updated', 'event_espresso'),
1303
-            3  => esc_html__('Custom field deleted.', 'event_espresso'),
1304
-            4  => sprintf(esc_html__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1305
-            5  => $revision
1306
-                ? sprintf(
1307
-                    esc_html__('%s restored to revision from %s', 'event_espresso'),
1308
-                    $this->_cpt_object->labels->singular_name,
1309
-                    wp_post_revision_title($revision, false)
1310
-                )
1311
-                : false,
1312
-            6  => sprintf(
1313
-                esc_html__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1314
-                $this->_cpt_object->labels->singular_name,
1315
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1316
-                '</a>'
1317
-            ),
1318
-            7  => sprintf(esc_html__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1319
-            8  => sprintf(
1320
-                esc_html__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1321
-                $this->_cpt_object->labels->singular_name,
1322
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1323
-                '</a>'
1324
-            ),
1325
-            9  => sprintf(
1326
-                esc_html__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1327
-                $this->_cpt_object->labels->singular_name,
1328
-                '<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1329
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1330
-                '</a>'
1331
-            ),
1332
-            10 => sprintf(
1333
-                esc_html__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1334
-                $this->_cpt_object->labels->singular_name,
1335
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1336
-                '</a>'
1337
-            ),
1338
-        ];
1339
-        return $messages;
1340
-    }
1341
-
1342
-
1343
-    /**
1344
-     * default method for the 'create_new' route for cpt admin pages.
1345
-     * For reference what to include in here, see wp-admin/post-new.php
1346
-     *
1347
-     * @return void
1348
-     */
1349
-    protected function _create_new_cpt_item()
1350
-    {
1351
-        // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1352
-        global $post, $title, $post_type, $post_type_object;
1353
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1354
-        $post_type_object = $this->_cpt_object;
1355
-        $title            = $post_type_object->labels->add_new_item;
1356
-        $post             = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1357
-        add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1358
-        // modify the default editor title field with default title.
1359
-        add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1360
-        $this->loadEditorTemplate();
1361
-    }
1362
-
1363
-
1364
-    /**
1365
-     * Enqueues auto-save and loads the editor template
1366
-     *
1367
-     * @param bool $creating
1368
-     */
1369
-    private function loadEditorTemplate(bool $creating = true)
1370
-    {
1371
-        if ($this->admin_config && ! $this->admin_config->useAdvancedEditor()) {
1372
-            add_filter('admin_body_class', function ($classes) {
1373
-                $classes .= ' espresso-legacy-editor';
1374
-                return $classes;
1375
-            });
1376
-        }
1377
-
1378
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1379
-        // these vars are used by the template
1380
-        $editing = true;
1381
-        $post_ID = $post->ID;
1382
-        if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1383
-            // only enqueue autosave when creating event (necessary to get permalink/url generated)
1384
-            // otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1385
-            $action = $this->request->getRequestParam('action');
1386
-            if ($creating) {
1387
-                wp_enqueue_script('autosave');
1388
-            } elseif (
1389
-                isset($this->_cpt_routes[ $action ])
1390
-                && ! isset($this->_labels['hide_add_button_on_cpt_route'][ $action ])
1391
-            ) {
1392
-                $create_new_action = apply_filters(
1393
-                    'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1394
-                    'create_new',
1395
-                    $this
1396
-                );
1397
-                $post_new_file     = EE_Admin_Page::add_query_args_and_nonce(
1398
-                    [
1399
-                        'action' => $create_new_action,
1400
-                        'page'   => $this->page_slug,
1401
-                    ],
1402
-                    'admin.php'
1403
-                );
1404
-            }
1405
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1406
-        }
1407
-    }
1408
-
1409
-
1410
-    public function add_new_admin_page_global()
1411
-    {
1412
-        $admin_page = $this->request->getRequestParam('post', 0, DataType::INT) !== 0
1413
-            ? 'post-php'
1414
-            : 'post-new-php';
1415
-        ?>
1116
+	}
1117
+
1118
+
1119
+	/**
1120
+	 * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1121
+	 *
1122
+	 * @param string $location Original location url
1123
+	 * @return string           new (or original) url to redirect to.
1124
+	 * @throws EE_Error
1125
+	 */
1126
+	public function revision_redirect(string $location): string
1127
+	{
1128
+		// get revision
1129
+		$revision = $this->request->getRequestParam('revision');
1130
+		// can't do anything without revision so let's get out if not present
1131
+		if (empty($revision)) {
1132
+			return $location;
1133
+		}
1134
+		// get rev_post_data
1135
+		$rev        = get_post($revision);
1136
+		$admin_url  = $this->_admin_base_url;
1137
+		$query_args = [
1138
+			'action'   => 'edit',
1139
+			'post'     => $rev->post_parent,
1140
+			'revision' => $revision,
1141
+			'message'  => 5,
1142
+		];
1143
+		$this->_process_notices($query_args, true);
1144
+		return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $admin_url);
1145
+	}
1146
+
1147
+
1148
+	/**
1149
+	 * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1150
+	 *
1151
+	 * @param string|null $link     the original generated link
1152
+	 * @param int    $id            post id
1153
+	 * @return string               the link
1154
+	 */
1155
+	public function modify_edit_post_link(?string $link, int $id): ?string
1156
+	{
1157
+		$post = get_post($id);
1158
+		$action = $this->request->getRequestParam('action');
1159
+		if (
1160
+			empty($action)
1161
+			|| ! isset($this->_cpt_routes[ $action ])
1162
+			|| $post->post_type !== $this->_cpt_routes[ $action ]
1163
+		) {
1164
+			return $link;
1165
+		}
1166
+		$query_args = [
1167
+			'action' => $this->_cpt_edit_routes[ $post->post_type ] ?? 'edit',
1168
+			'post'   => $id,
1169
+		];
1170
+		return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1171
+	}
1172
+
1173
+
1174
+	/**
1175
+	 * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1176
+	 * our routes.
1177
+	 *
1178
+	 * @param string|null $delete_link      original delete link
1179
+	 * @param int    $post_id               id of cpt object
1180
+	 * @return null|string                  new delete link
1181
+	 * @throws EE_Error
1182
+	 * @throws ReflectionException
1183
+	 */
1184
+	public function modify_delete_post_link(?string $delete_link, int $post_id): ?string
1185
+	{
1186
+		$post = get_post($post_id);
1187
+		$action = $this->request->getRequestParam('action');
1188
+		if (
1189
+			! $post instanceof WP_Post
1190
+			|| empty($action)
1191
+			|| ! isset($this->_cpt_routes[ $action ])
1192
+			|| $post->post_type !== $this->_cpt_routes[ $action ]
1193
+		) {
1194
+			return $delete_link;
1195
+		}
1196
+		$this->_set_model_object($post->ID, true);
1197
+
1198
+		// returns something like `trash_event` or `trash_attendee` or `trash_venue`
1199
+		$action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1200
+
1201
+		return EE_Admin_Page::add_query_args_and_nonce(
1202
+			[
1203
+				'page'   => $this->request->getRequestParam('page'),
1204
+				'action' => $action,
1205
+				$this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name() => $post->ID,
1206
+			],
1207
+			admin_url()
1208
+		);
1209
+	}
1210
+
1211
+
1212
+	/**
1213
+	 * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1214
+	 * so that we can hijack the default redirect locations for wp custom post types
1215
+	 * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1216
+	 *
1217
+	 * @param string $location This is the incoming currently set redirect location
1218
+	 * @param string $post_id  This is the 'ID' value of the wp_posts table
1219
+	 * @return string           the new location to redirect to
1220
+	 * @throws EE_Error
1221
+	 */
1222
+	public function cpt_post_location_redirect(string $location, string $post_id): string
1223
+	{
1224
+		// we DO have a match so let's setup the url
1225
+		// we have to get the post to determine our route
1226
+		$post       = get_post($post_id);
1227
+		$edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1228
+		// shared query_args
1229
+		$query_args = ['action' => $edit_route, 'post' => $post_id];
1230
+
1231
+		$save = $this->request->getRequestParam('save');
1232
+		$publish = $this->request->getRequestParam('publish');
1233
+		$add_meta = $this->request->getRequestParam('addmeta');
1234
+		$delete_meta = $this->request->getRequestParam('deletemeta');
1235
+		if ($save || $publish) {
1236
+			$status = get_post_status($post_id);
1237
+			if ($publish) {
1238
+				switch ($status) {
1239
+					case 'pending':
1240
+						$message = 8;
1241
+						break;
1242
+					case 'future':
1243
+						$message = 9;
1244
+						break;
1245
+					default:
1246
+						$message = 6;
1247
+				}
1248
+			} else {
1249
+				$message = 'draft' === $status ? 10 : 1;
1250
+			}
1251
+		} elseif ($add_meta) {
1252
+			$message = 2;
1253
+		} elseif ($delete_meta) {
1254
+			$message = 3;
1255
+		} elseif ($this->request->getRequestParam('action') === 'post-quickpress-save-cont') {
1256
+			$message = 7;
1257
+		} else {
1258
+			$message = 4;
1259
+		}
1260
+		// change the message if the post type is not viewable on the frontend
1261
+		$this->_cpt_object = get_post_type_object($post->post_type);
1262
+
1263
+		$query_args['message'] = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1264
+		$this->_process_notices($query_args, true);
1265
+		return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1266
+	}
1267
+
1268
+
1269
+	/**
1270
+	 * This method is called to inject nav tabs on core WP cpt pages
1271
+	 *
1272
+	 * @return void
1273
+	 * @throws EE_Error
1274
+	 */
1275
+	public function inject_nav_tabs()
1276
+	{
1277
+		echo wp_kses($this->_get_main_nav_tabs(), AllowedTags::getWithFormTags());
1278
+	}
1279
+
1280
+
1281
+	/**
1282
+	 * This just sets up the post update messages when an update form is loaded
1283
+	 *
1284
+	 * @param array $messages the original messages array
1285
+	 * @return array           the new messages array
1286
+	 */
1287
+	public function post_update_messages(array $messages): array
1288
+	{
1289
+		global $post;
1290
+		$id       = $this->request->getRequestParam('post');
1291
+		$id       = empty($id) && is_object($post) ? $post->ID : null;
1292
+		$revision = $this->request->getRequestParam('revision', 0, 'int');
1293
+
1294
+		$messages[ $post->post_type ] = [
1295
+			0  => '', // Unused. Messages start at index 1.
1296
+			1  => sprintf(
1297
+				esc_html__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1298
+				$this->_cpt_object->labels->singular_name,
1299
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1300
+				'</a>'
1301
+			),
1302
+			2  => esc_html__('Custom field updated', 'event_espresso'),
1303
+			3  => esc_html__('Custom field deleted.', 'event_espresso'),
1304
+			4  => sprintf(esc_html__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1305
+			5  => $revision
1306
+				? sprintf(
1307
+					esc_html__('%s restored to revision from %s', 'event_espresso'),
1308
+					$this->_cpt_object->labels->singular_name,
1309
+					wp_post_revision_title($revision, false)
1310
+				)
1311
+				: false,
1312
+			6  => sprintf(
1313
+				esc_html__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1314
+				$this->_cpt_object->labels->singular_name,
1315
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1316
+				'</a>'
1317
+			),
1318
+			7  => sprintf(esc_html__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1319
+			8  => sprintf(
1320
+				esc_html__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1321
+				$this->_cpt_object->labels->singular_name,
1322
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1323
+				'</a>'
1324
+			),
1325
+			9  => sprintf(
1326
+				esc_html__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1327
+				$this->_cpt_object->labels->singular_name,
1328
+				'<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1329
+				'<a target="_blank" href="' . esc_url(get_permalink($id)),
1330
+				'</a>'
1331
+			),
1332
+			10 => sprintf(
1333
+				esc_html__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1334
+				$this->_cpt_object->labels->singular_name,
1335
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1336
+				'</a>'
1337
+			),
1338
+		];
1339
+		return $messages;
1340
+	}
1341
+
1342
+
1343
+	/**
1344
+	 * default method for the 'create_new' route for cpt admin pages.
1345
+	 * For reference what to include in here, see wp-admin/post-new.php
1346
+	 *
1347
+	 * @return void
1348
+	 */
1349
+	protected function _create_new_cpt_item()
1350
+	{
1351
+		// gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1352
+		global $post, $title, $post_type, $post_type_object;
1353
+		$post_type        = $this->_cpt_routes[ $this->_req_action ];
1354
+		$post_type_object = $this->_cpt_object;
1355
+		$title            = $post_type_object->labels->add_new_item;
1356
+		$post             = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1357
+		add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1358
+		// modify the default editor title field with default title.
1359
+		add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1360
+		$this->loadEditorTemplate();
1361
+	}
1362
+
1363
+
1364
+	/**
1365
+	 * Enqueues auto-save and loads the editor template
1366
+	 *
1367
+	 * @param bool $creating
1368
+	 */
1369
+	private function loadEditorTemplate(bool $creating = true)
1370
+	{
1371
+		if ($this->admin_config && ! $this->admin_config->useAdvancedEditor()) {
1372
+			add_filter('admin_body_class', function ($classes) {
1373
+				$classes .= ' espresso-legacy-editor';
1374
+				return $classes;
1375
+			});
1376
+		}
1377
+
1378
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1379
+		// these vars are used by the template
1380
+		$editing = true;
1381
+		$post_ID = $post->ID;
1382
+		if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1383
+			// only enqueue autosave when creating event (necessary to get permalink/url generated)
1384
+			// otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1385
+			$action = $this->request->getRequestParam('action');
1386
+			if ($creating) {
1387
+				wp_enqueue_script('autosave');
1388
+			} elseif (
1389
+				isset($this->_cpt_routes[ $action ])
1390
+				&& ! isset($this->_labels['hide_add_button_on_cpt_route'][ $action ])
1391
+			) {
1392
+				$create_new_action = apply_filters(
1393
+					'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1394
+					'create_new',
1395
+					$this
1396
+				);
1397
+				$post_new_file     = EE_Admin_Page::add_query_args_and_nonce(
1398
+					[
1399
+						'action' => $create_new_action,
1400
+						'page'   => $this->page_slug,
1401
+					],
1402
+					'admin.php'
1403
+				);
1404
+			}
1405
+			include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1406
+		}
1407
+	}
1408
+
1409
+
1410
+	public function add_new_admin_page_global()
1411
+	{
1412
+		$admin_page = $this->request->getRequestParam('post', 0, DataType::INT) !== 0
1413
+			? 'post-php'
1414
+			: 'post-new-php';
1415
+		?>
1416 1416
         <script type="text/javascript">
1417 1417
             adminpage = '<?php echo esc_js($admin_page); ?>';
1418 1418
         </script>
1419 1419
         <?php
1420
-    }
1421
-
1422
-
1423
-    /**
1424
-     * default method for the 'edit' route for cpt admin pages
1425
-     * For reference on what to put in here, refer to wp-admin/post.php
1426
-     *
1427
-     * @return void
1428
-     */
1429
-    protected function _edit_cpt_item()
1430
-    {
1431
-        global $post, $post_type, $post_type_object, $title;
1432
-        $post_id = $this->request->getRequestParam('post', 0, DataType::INT);
1433
-        $post    = $post_id ? get_post($post_id, OBJECT, 'edit') : null;
1434
-        if (empty($post)) {
1435
-            wp_die(
1436
-                esc_html__(
1437
-                    "You attempted to edit an item that doesn't exist. Perhaps it was deleted?",
1438
-                    'event_espresso'
1439
-                )
1440
-            );
1441
-        }
1442
-
1443
-        $post_lock = $this->request->getRequestParam('get-post-lock');
1444
-        if ($post_lock) {
1445
-            wp_set_post_lock($post_id);
1446
-            EEH_URL::safeRedirectAndExit(get_edit_post_link($post_id, 'url'));
1447
-        }
1448
-
1449
-        // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1450
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1451
-        $post_type_object = $this->_cpt_object;
1452
-        $title = $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1453
-                 ?? $post_type_object->labels->edit_item;
1454
-
1455
-        if (! wp_check_post_lock($post->ID)) {
1456
-            wp_set_post_lock($post->ID);
1457
-        }
1458
-        add_action('admin_footer', '_admin_notice_post_locked');
1459
-        if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1460
-            wp_enqueue_script('admin-comments');
1461
-            enqueue_comment_hotkeys_js();
1462
-        }
1463
-        add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1464
-        // modify the default editor title field with default title.
1465
-        add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1466
-        $this->loadEditorTemplate(false);
1467
-    }
1468
-
1469
-
1470
-
1471
-    /**
1472
-     * some getters
1473
-     */
1474
-    /**
1475
-     * This returns the protected _cpt_model_obj property
1476
-     *
1477
-     * @return EE_CPT_Base|null
1478
-     */
1479
-    public function get_cpt_model_obj(): ?EE_CPT_Base
1480
-    {
1481
-        return $this->_cpt_model_obj;
1482
-    }
1420
+	}
1421
+
1422
+
1423
+	/**
1424
+	 * default method for the 'edit' route for cpt admin pages
1425
+	 * For reference on what to put in here, refer to wp-admin/post.php
1426
+	 *
1427
+	 * @return void
1428
+	 */
1429
+	protected function _edit_cpt_item()
1430
+	{
1431
+		global $post, $post_type, $post_type_object, $title;
1432
+		$post_id = $this->request->getRequestParam('post', 0, DataType::INT);
1433
+		$post    = $post_id ? get_post($post_id, OBJECT, 'edit') : null;
1434
+		if (empty($post)) {
1435
+			wp_die(
1436
+				esc_html__(
1437
+					"You attempted to edit an item that doesn't exist. Perhaps it was deleted?",
1438
+					'event_espresso'
1439
+				)
1440
+			);
1441
+		}
1442
+
1443
+		$post_lock = $this->request->getRequestParam('get-post-lock');
1444
+		if ($post_lock) {
1445
+			wp_set_post_lock($post_id);
1446
+			EEH_URL::safeRedirectAndExit(get_edit_post_link($post_id, 'url'));
1447
+		}
1448
+
1449
+		// template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1450
+		$post_type        = $this->_cpt_routes[ $this->_req_action ];
1451
+		$post_type_object = $this->_cpt_object;
1452
+		$title = $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1453
+				 ?? $post_type_object->labels->edit_item;
1454
+
1455
+		if (! wp_check_post_lock($post->ID)) {
1456
+			wp_set_post_lock($post->ID);
1457
+		}
1458
+		add_action('admin_footer', '_admin_notice_post_locked');
1459
+		if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1460
+			wp_enqueue_script('admin-comments');
1461
+			enqueue_comment_hotkeys_js();
1462
+		}
1463
+		add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1464
+		// modify the default editor title field with default title.
1465
+		add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1466
+		$this->loadEditorTemplate(false);
1467
+	}
1468
+
1469
+
1470
+
1471
+	/**
1472
+	 * some getters
1473
+	 */
1474
+	/**
1475
+	 * This returns the protected _cpt_model_obj property
1476
+	 *
1477
+	 * @return EE_CPT_Base|null
1478
+	 */
1479
+	public function get_cpt_model_obj(): ?EE_CPT_Base
1480
+	{
1481
+		return $this->_cpt_model_obj;
1482
+	}
1483 1483
 }
Please login to merge, or discard this patch.
Spacing   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
      */
156 156
     protected function getLoader(): LoaderInterface
157 157
     {
158
-        if (! $this->loader instanceof LoaderInterface) {
158
+        if ( ! $this->loader instanceof LoaderInterface) {
159 159
             $this->loader = LoaderFactory::getLoader();
160 160
         }
161 161
         return $this->loader;
@@ -179,7 +179,7 @@  discard block
 block discarded – undo
179 179
             ],
180 180
             $this->_cpt_routes
181 181
         );
182
-        $cpt_route_action     = $this->_cpt_routes[ $this->raw_req_action ] ?? null;
182
+        $cpt_route_action = $this->_cpt_routes[$this->raw_req_action] ?? null;
183 183
         // let's see if the current route has a value for cpt_object_slug. if it does, we use that instead of the page
184 184
         $page              = $this->raw_req_page ?: $this->page_slug;
185 185
         $page              = $cpt_route_action ?: $page;
@@ -211,10 +211,10 @@  discard block
 block discarded – undo
211 211
         // possibly reset pagenow.
212 212
         if (
213 213
             $this->page_slug === $this->raw_req_page
214
-            && isset($this->_pagenow_map[ $this->raw_req_action ])
214
+            && isset($this->_pagenow_map[$this->raw_req_action])
215 215
         ) {
216 216
             global $pagenow, $hook_suffix;
217
-            $pagenow     = $this->_pagenow_map[ $this->raw_req_action ];
217
+            $pagenow     = $this->_pagenow_map[$this->raw_req_action];
218 218
             $hook_suffix = $pagenow;
219 219
         }
220 220
     }
@@ -246,7 +246,7 @@  discard block
 block discarded – undo
246 246
         if (empty($wp_meta_boxes)) {
247 247
             return;
248 248
         }
249
-        $current_metaboxes = $wp_meta_boxes[ $this->page_slug ] ?? [];
249
+        $current_metaboxes = $wp_meta_boxes[$this->page_slug] ?? [];
250 250
         foreach ($current_metaboxes as $box_context) {
251 251
             foreach ($box_context as $box_details) {
252 252
                 foreach ($box_details as $box) {
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
     protected function _load_page_dependencies()
311 311
     {
312 312
         // we only add stuff if this is a cpt_route!
313
-        if (! $this->_cpt_route) {
313
+        if ( ! $this->_cpt_route) {
314 314
             parent::_load_page_dependencies();
315 315
             return;
316 316
         }
@@ -331,8 +331,8 @@  discard block
 block discarded – undo
331 331
         add_action('post_updated_messages', [$this, 'post_update_messages']);
332 332
         // This basically allows us to change the title of the "publish" metabox area
333 333
         // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
334
-        $screen = $this->_cpt_routes[ $this->_req_action ];
335
-        if (! empty($this->_labels['publishbox'])) {
334
+        $screen = $this->_cpt_routes[$this->_req_action];
335
+        if ( ! empty($this->_labels['publishbox'])) {
336 336
             $this->addMetaBox(
337 337
                 'submitdiv',
338 338
                 $this->getPublishBoxTitle(),
@@ -383,8 +383,8 @@  discard block
 block discarded – undo
383 383
         // This is for any plugins that are doing things properly
384 384
         // and hooking into the load page hook for core wp cpt routes.
385 385
         global $pagenow;
386
-        add_action('load-' . $pagenow, [$this, 'modify_current_screen'], 20);
387
-        do_action('load-' . $pagenow);
386
+        add_action('load-'.$pagenow, [$this, 'modify_current_screen'], 20);
387
+        do_action('load-'.$pagenow);
388 388
         add_action('admin_enqueue_scripts', [$this, 'setup_autosave_hooks'], 30);
389 389
         // we route REALLY early.
390 390
         try {
@@ -411,8 +411,8 @@  discard block
 block discarded – undo
411 411
                 'admin.php?page=espresso_registrations&action=contact_list',
412 412
             ],
413 413
             1 => [
414
-                'edit.php?post_type=' . $this->_cpt_object->name,
415
-                'admin.php?page=' . $this->_cpt_object->name,
414
+                'edit.php?post_type='.$this->_cpt_object->name,
415
+                'admin.php?page='.$this->_cpt_object->name,
416 416
             ],
417 417
         ];
418 418
         foreach ($routes_to_match as $route_matches) {
@@ -440,14 +440,14 @@  discard block
 block discarded – undo
440 440
             'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
441 441
         );
442 442
         $cpt_args          = $custom_post_types->getDefinitions();
443
-        $cpt_args          = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : [];
443
+        $cpt_args          = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : [];
444 444
         $cpt_has_support   = ! empty($cpt_args['page_templates']);
445 445
 
446 446
         $post_templates = wp_get_theme()->get_post_templates();
447 447
         // if there are $post_templates for this cpt, then we return false for this method because
448 448
         // that means we aren't going to load our page template manager and leave that up to the native
449 449
         // cpt template manager.
450
-        return ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
450
+        return ! isset($post_templates[$cpt_name]) ? $cpt_has_support : false;
451 451
     }
452 452
 
453 453
 
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
     {
505 505
         $statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
506 506
         $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
507
-            ? $statuses[ $this->_cpt_model_obj->status() ]
507
+            ? $statuses[$this->_cpt_model_obj->status()]
508 508
             : '';
509 509
         $template_args    = [
510 510
             'cur_status'            => $this->_cpt_model_obj->status(),
@@ -519,7 +519,7 @@  discard block
 block discarded – undo
519 519
             $template_args['statuses']         = $statuses;
520 520
         }
521 521
 
522
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
522
+        $template = EE_ADMIN_TEMPLATE.'status_dropdown.template.php';
523 523
         EEH_Template::display_template($template, $template_args);
524 524
     }
525 525
 
@@ -559,7 +559,7 @@  discard block
 block discarded – undo
559 559
         $nonce = $this->request->getRequestParam('autosavenonce');
560 560
         $this->_verify_nonce($nonce, 'autosave');
561 561
         // make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
562
-        if (! defined('DOING_AUTOSAVE')) {
562
+        if ( ! defined('DOING_AUTOSAVE')) {
563 563
             define('DOING_AUTOSAVE', true);
564 564
         }
565 565
         // if we made it here then the nonce checked out.  Let's run our methods and actions
@@ -570,7 +570,7 @@  discard block
 block discarded – undo
570 570
             $this->_template_args['success'] = true;
571 571
         }
572 572
         do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
573
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
573
+        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_'.get_class($this), $this);
574 574
         // now let's return json
575 575
         $this->_return_json();
576 576
     }
@@ -592,7 +592,7 @@  discard block
 block discarded – undo
592 592
             return;
593 593
         }
594 594
         // set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
595
-        if (! empty($this->_cpt_object)) {
595
+        if ( ! empty($this->_cpt_object)) {
596 596
             $this->_page_routes = array_merge(
597 597
                 [
598 598
                     'create_new' => [$this, '_create_new_cpt_item'],
@@ -623,7 +623,7 @@  discard block
 block discarded – undo
623 623
             );
624 624
         }
625 625
         // load the next section only if this is a matching cpt route as set in the cpt routes array.
626
-        if (! isset($this->_cpt_routes[ $this->_req_action ])) {
626
+        if ( ! isset($this->_cpt_routes[$this->_req_action])) {
627 627
             return;
628 628
         }
629 629
         $this->_cpt_route = true;
@@ -664,7 +664,7 @@  discard block
 block discarded – undo
664 664
             empty($this->_cpt_model_names)
665 665
             || (
666 666
                 ! $ignore_route_check
667
-                && ! isset($this->_cpt_routes[ $this->_req_action ])
667
+                && ! isset($this->_cpt_routes[$this->_req_action])
668 668
             )
669 669
             || (
670 670
                 $this->_cpt_model_obj instanceof EE_CPT_Base
@@ -682,11 +682,11 @@  discard block
 block discarded – undo
682 682
                 'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
683 683
             );
684 684
             $model_names       = $custom_post_types->getCustomPostTypeModelNames($post_type);
685
-            if (isset($model_names[ $post_type ])) {
686
-                $model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
685
+            if (isset($model_names[$post_type])) {
686
+                $model = EE_Registry::instance()->load_model($model_names[$post_type]);
687 687
             }
688 688
         } else {
689
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
689
+            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
690 690
         }
691 691
         if ($model instanceof EEM_Base) {
692 692
             $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
@@ -714,8 +714,8 @@  discard block
 block discarded – undo
714 714
         $post           = get_post($post_ID);
715 715
         $post_type      = $post instanceof WP_Post ? $post->post_type : false;
716 716
         $current_route  = $this->request->getRequestParam('current_route', 'shouldneverwork');
717
-        $route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
718
-            ? $this->_cpt_routes[ $current_route ]
717
+        $route_to_check = $post_type && isset($this->_cpt_routes[$current_route])
718
+            ? $this->_cpt_routes[$current_route]
719 719
             : '';
720 720
         add_filter('get_delete_post_link', [$this, 'modify_delete_post_link'], 10, 2);
721 721
         add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
@@ -724,10 +724,10 @@  discard block
 block discarded – undo
724 724
         }
725 725
         // now let's filter redirect if we're on a revision page and the revision is for an event CPT.
726 726
         $revision = $this->request->getRequestParam('revision');
727
-        if (! empty($revision)) {
727
+        if ( ! empty($revision)) {
728 728
             $action = $this->request->getRequestParam('action');
729 729
             // doing a restore?
730
-            if (! empty($action) && $action === 'restore') {
730
+            if ( ! empty($action) && $action === 'restore') {
731 731
                 // get post for revision
732 732
                 $rev_post   = get_post($revision);
733 733
                 $rev_parent = get_post($rev_post->post_parent);
@@ -764,7 +764,7 @@  discard block
 block discarded – undo
764 764
     {
765 765
         $this->_set_model_object($post_id, true, 'trash');
766 766
         // if our cpt object isn't existent then get out immediately.
767
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
767
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
768 768
             return;
769 769
         }
770 770
         $this->trash_cpt_item($post_id);
@@ -783,7 +783,7 @@  discard block
 block discarded – undo
783 783
     {
784 784
         $this->_set_model_object($post_id, true, 'restore');
785 785
         // if our cpt object isn't existent then get out immediately.
786
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
786
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
787 787
             return;
788 788
         }
789 789
         $this->restore_cpt_item($post_id);
@@ -802,7 +802,7 @@  discard block
 block discarded – undo
802 802
     {
803 803
         $this->_set_model_object($post_id, true, 'delete');
804 804
         // if our cpt object isn't existent then get out immediately.
805
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
805
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
806 806
             return;
807 807
         }
808 808
         $this->delete_cpt_item($post_id);
@@ -821,7 +821,7 @@  discard block
 block discarded – undo
821 821
     {
822 822
         $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
823 823
         // verify event object
824
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
824
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
825 825
             throw new EE_Error(
826 826
                 sprintf(
827 827
                     esc_html__(
@@ -876,13 +876,13 @@  discard block
 block discarded – undo
876 876
         if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
877 877
             // setup custom post status object for localize script but only if we've got a cpt object
878 878
             $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
879
-            if (! empty($statuses)) {
879
+            if ( ! empty($statuses)) {
880 880
                 // get ALL statuses!
881 881
                 $statuses = $this->_cpt_model_obj->get_all_post_statuses();
882 882
                 // setup object
883 883
                 $ee_cpt_statuses = [];
884 884
                 foreach ($statuses as $status => $label) {
885
-                    $ee_cpt_statuses[ $status ] = [
885
+                    $ee_cpt_statuses[$status] = [
886 886
                         'label'      => $label,
887 887
                         'save_label' => sprintf(
888 888
                             wp_strip_all_tags(__('Save as %s', 'event_espresso')),
@@ -947,7 +947,7 @@  discard block
 block discarded – undo
947 947
                 $post->page_template = $page_template;
948 948
                 $page_templates      = wp_get_theme()->get_page_templates($post);
949 949
             }
950
-            if ($page_template !== 'default' && ! isset($page_templates[ $page_template ])) {
950
+            if ($page_template !== 'default' && ! isset($page_templates[$page_template])) {
951 951
                 EE_Error::add_error(
952 952
                     esc_html__('Invalid Page Template.', 'event_espresso'),
953 953
                     __FILE__,
@@ -977,7 +977,7 @@  discard block
 block discarded – undo
977 977
     {
978 978
         // only do this if we're actually processing one of our CPTs
979 979
         // if our cpt object isn't existent then get out immediately.
980
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
980
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
981 981
             return;
982 982
         }
983 983
         delete_post_meta($post_id, '_wp_trash_meta_status');
@@ -1002,7 +1002,7 @@  discard block
 block discarded – undo
1002 1002
         // global action
1003 1003
         do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1004 1004
         // class specific action so you can limit hooking into a specific page.
1005
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1005
+        do_action('AHEE_EE_Admin_Page_CPT_'.get_class($this).'__restore_revision', $post_id, $revision_id);
1006 1006
     }
1007 1007
 
1008 1008
 
@@ -1031,11 +1031,11 @@  discard block
 block discarded – undo
1031 1031
     public function modify_current_screen()
1032 1032
     {
1033 1033
         // ONLY do this if the current page_route IS a cpt route
1034
-        if (! $this->_cpt_route) {
1034
+        if ( ! $this->_cpt_route) {
1035 1035
             return;
1036 1036
         }
1037 1037
         // routing things REALLY early b/c this is a cpt admin page
1038
-        set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1038
+        set_current_screen($this->_cpt_routes[$this->_req_action]);
1039 1039
         $this->_current_screen       = get_current_screen();
1040 1040
         $this->_current_screen->base = 'event-espresso';
1041 1041
         $this->_add_help_tabs(); // we make sure we add any help tabs back in!
@@ -1058,7 +1058,7 @@  discard block
 block discarded – undo
1058 1058
      */
1059 1059
     public function add_custom_editor_default_title(?string $title): ?string
1060 1060
     {
1061
-        return $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ] ?? $title;
1061
+        return $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]] ?? $title;
1062 1062
     }
1063 1063
 
1064 1064
 
@@ -1108,10 +1108,10 @@  discard block
 block discarded – undo
1108 1108
     {
1109 1109
         // we're also going to add the route value and the current page so we can direct autosave parsing correctly
1110 1110
         echo '
1111
-        <input type="hidden" name="ee_cpt_item_redirect_url" value="' . esc_url_raw($this->_admin_base_url) . '"/>
1111
+        <input type="hidden" name="ee_cpt_item_redirect_url" value="' . esc_url_raw($this->_admin_base_url).'"/>
1112 1112
         <div id="ee-cpt-hidden-inputs">
1113
-            <input type="hidden" id="current_route" name="current_route" value="' . esc_attr($this->_current_view) . '"/>
1114
-            <input type="hidden" id="current_page" name="current_page" value="' . esc_attr($this->page_slug) . '"/>
1113
+            <input type="hidden" id="current_route" name="current_route" value="' . esc_attr($this->_current_view).'"/>
1114
+            <input type="hidden" id="current_page" name="current_page" value="' . esc_attr($this->page_slug).'"/>
1115 1115
         </div>';
1116 1116
     }
1117 1117
 
@@ -1158,13 +1158,13 @@  discard block
 block discarded – undo
1158 1158
         $action = $this->request->getRequestParam('action');
1159 1159
         if (
1160 1160
             empty($action)
1161
-            || ! isset($this->_cpt_routes[ $action ])
1162
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1161
+            || ! isset($this->_cpt_routes[$action])
1162
+            || $post->post_type !== $this->_cpt_routes[$action]
1163 1163
         ) {
1164 1164
             return $link;
1165 1165
         }
1166 1166
         $query_args = [
1167
-            'action' => $this->_cpt_edit_routes[ $post->post_type ] ?? 'edit',
1167
+            'action' => $this->_cpt_edit_routes[$post->post_type] ?? 'edit',
1168 1168
             'post'   => $id,
1169 1169
         ];
1170 1170
         return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
@@ -1188,15 +1188,15 @@  discard block
 block discarded – undo
1188 1188
         if (
1189 1189
             ! $post instanceof WP_Post
1190 1190
             || empty($action)
1191
-            || ! isset($this->_cpt_routes[ $action ])
1192
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1191
+            || ! isset($this->_cpt_routes[$action])
1192
+            || $post->post_type !== $this->_cpt_routes[$action]
1193 1193
         ) {
1194 1194
             return $delete_link;
1195 1195
         }
1196 1196
         $this->_set_model_object($post->ID, true);
1197 1197
 
1198 1198
         // returns something like `trash_event` or `trash_attendee` or `trash_venue`
1199
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1199
+        $action = 'trash_'.str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1200 1200
 
1201 1201
         return EE_Admin_Page::add_query_args_and_nonce(
1202 1202
             [
@@ -1224,7 +1224,7 @@  discard block
 block discarded – undo
1224 1224
         // we DO have a match so let's setup the url
1225 1225
         // we have to get the post to determine our route
1226 1226
         $post       = get_post($post_id);
1227
-        $edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1227
+        $edit_route = $this->_cpt_edit_routes[$post->post_type];
1228 1228
         // shared query_args
1229 1229
         $query_args = ['action' => $edit_route, 'post' => $post_id];
1230 1230
 
@@ -1291,12 +1291,12 @@  discard block
 block discarded – undo
1291 1291
         $id       = empty($id) && is_object($post) ? $post->ID : null;
1292 1292
         $revision = $this->request->getRequestParam('revision', 0, 'int');
1293 1293
 
1294
-        $messages[ $post->post_type ] = [
1294
+        $messages[$post->post_type] = [
1295 1295
             0  => '', // Unused. Messages start at index 1.
1296 1296
             1  => sprintf(
1297 1297
                 esc_html__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1298 1298
                 $this->_cpt_object->labels->singular_name,
1299
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1299
+                '<a href="'.esc_url(get_permalink($id)).'">',
1300 1300
                 '</a>'
1301 1301
             ),
1302 1302
             2  => esc_html__('Custom field updated', 'event_espresso'),
@@ -1312,27 +1312,27 @@  discard block
 block discarded – undo
1312 1312
             6  => sprintf(
1313 1313
                 esc_html__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1314 1314
                 $this->_cpt_object->labels->singular_name,
1315
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1315
+                '<a href="'.esc_url(get_permalink($id)).'">',
1316 1316
                 '</a>'
1317 1317
             ),
1318 1318
             7  => sprintf(esc_html__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1319 1319
             8  => sprintf(
1320 1320
                 esc_html__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1321 1321
                 $this->_cpt_object->labels->singular_name,
1322
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1322
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))).'">',
1323 1323
                 '</a>'
1324 1324
             ),
1325 1325
             9  => sprintf(
1326 1326
                 esc_html__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1327 1327
                 $this->_cpt_object->labels->singular_name,
1328
-                '<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1329
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1328
+                '<strong>'.date_i18n('M j, Y @ G:i', strtotime($post->post_date)).'</strong>',
1329
+                '<a target="_blank" href="'.esc_url(get_permalink($id)),
1330 1330
                 '</a>'
1331 1331
             ),
1332 1332
             10 => sprintf(
1333 1333
                 esc_html__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1334 1334
                 $this->_cpt_object->labels->singular_name,
1335
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1335
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1336 1336
                 '</a>'
1337 1337
             ),
1338 1338
         ];
@@ -1350,10 +1350,10 @@  discard block
 block discarded – undo
1350 1350
     {
1351 1351
         // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1352 1352
         global $post, $title, $post_type, $post_type_object;
1353
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1353
+        $post_type        = $this->_cpt_routes[$this->_req_action];
1354 1354
         $post_type_object = $this->_cpt_object;
1355 1355
         $title            = $post_type_object->labels->add_new_item;
1356
-        $post             = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1356
+        $post             = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1357 1357
         add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1358 1358
         // modify the default editor title field with default title.
1359 1359
         add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
@@ -1369,7 +1369,7 @@  discard block
 block discarded – undo
1369 1369
     private function loadEditorTemplate(bool $creating = true)
1370 1370
     {
1371 1371
         if ($this->admin_config && ! $this->admin_config->useAdvancedEditor()) {
1372
-            add_filter('admin_body_class', function ($classes) {
1372
+            add_filter('admin_body_class', function($classes) {
1373 1373
                 $classes .= ' espresso-legacy-editor';
1374 1374
                 return $classes;
1375 1375
             });
@@ -1386,15 +1386,15 @@  discard block
 block discarded – undo
1386 1386
             if ($creating) {
1387 1387
                 wp_enqueue_script('autosave');
1388 1388
             } elseif (
1389
-                isset($this->_cpt_routes[ $action ])
1390
-                && ! isset($this->_labels['hide_add_button_on_cpt_route'][ $action ])
1389
+                isset($this->_cpt_routes[$action])
1390
+                && ! isset($this->_labels['hide_add_button_on_cpt_route'][$action])
1391 1391
             ) {
1392 1392
                 $create_new_action = apply_filters(
1393 1393
                     'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1394 1394
                     'create_new',
1395 1395
                     $this
1396 1396
                 );
1397
-                $post_new_file     = EE_Admin_Page::add_query_args_and_nonce(
1397
+                $post_new_file = EE_Admin_Page::add_query_args_and_nonce(
1398 1398
                     [
1399 1399
                         'action' => $create_new_action,
1400 1400
                         'page'   => $this->page_slug,
@@ -1402,7 +1402,7 @@  discard block
 block discarded – undo
1402 1402
                     'admin.php'
1403 1403
                 );
1404 1404
             }
1405
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1405
+            include_once WP_ADMIN_PATH.'edit-form-advanced.php';
1406 1406
         }
1407 1407
     }
1408 1408
 
@@ -1447,16 +1447,16 @@  discard block
 block discarded – undo
1447 1447
         }
1448 1448
 
1449 1449
         // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1450
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1450
+        $post_type        = $this->_cpt_routes[$this->_req_action];
1451 1451
         $post_type_object = $this->_cpt_object;
1452
-        $title = $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1452
+        $title = $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]]
1453 1453
                  ?? $post_type_object->labels->edit_item;
1454 1454
 
1455
-        if (! wp_check_post_lock($post->ID)) {
1455
+        if ( ! wp_check_post_lock($post->ID)) {
1456 1456
             wp_set_post_lock($post->ID);
1457 1457
         }
1458 1458
         add_action('admin_footer', '_admin_notice_post_locked');
1459
-        if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1459
+        if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1460 1460
             wp_enqueue_script('admin-comments');
1461 1461
             enqueue_comment_hotkeys_js();
1462 1462
         }
Please login to merge, or discard this patch.
core/helpers/EEH_Form_Fields.helper.php 1 patch
Indentation   +2059 added lines, -2059 removed lines patch added patch discarded remove patch
@@ -27,1056 +27,1056 @@  discard block
 block discarded – undo
27 27
  */
28 28
 class EEH_Form_Fields
29 29
 {
30
-    /**
31
-     *  Generates HTML for the forms used on admin pages
32
-     *
33
-     *
34
-     * @static
35
-     * @access public
36
-     * @param array $input_vars - array of input field details
37
-     *                          format:
38
-     *                          $template_form_fields['field-id'] = array(
39
-     *                          'name' => 'name_attribute',
40
-     *                          'label' => esc_html__('Field Label', 'event_espresso'), //or false
41
-     *                          'input' => 'hidden', //field input type can be 'text', 'select', 'textarea', 'hidden',
42
-     *                          'checkbox', 'wp_editor'
43
-     *                          'type' => 'int', //what "type" the value is (i.e. string, int etc)
44
-     *                          'required' => false, //boolean for whether the field is required
45
-     *                          'validation' => true, //boolean, whether to validate the field (todo)
46
-     *                          'value' => 'some_value_for_field', //what value is used for field
47
-     *                          'format' => '%d', //what format the value is (%d, %f, or %s)
48
-     *                          'db-col' => 'column_in_db' //used to indicate which column the field corresponds with
49
-     *                          in the db
50
-     *                          'options' => optiona, optionb || array('value' => 'label', '') //if the input type is
51
-     *                          "select", this allows you to set the args for the different <option> tags.
52
-     *                          'tabindex' => 1 //this allows you to set the tabindex for the field.
53
-     *                          'append_content' => '' //this allows you to send in html content to append to the
54
-     *                          field.
55
-     *                          )
56
-     * @param string|null $form_id    - used for defining unique identifiers for the form.
57
-     * @return string
58
-     * @todo   : at some point we can break this down into other static methods to abstract it a bit better.
59
-     */
60
-    public static function get_form_fields(array $input_vars = [], ?string $form_id = '')
61
-    {
62
-
63
-        if (empty($input_vars)) {
64
-            EE_Error::add_error(
65
-                esc_html__('missing required variables for the form field generator', 'event_espresso'),
66
-                __FILE__,
67
-                __FUNCTION__,
68
-                __LINE__
69
-            );
70
-            return false;
71
-        }
72
-
73
-        $output        = "";
74
-        $inputs        = [];
75
-        $hidden_inputs = [];
76
-
77
-        // cycle thru inputs
78
-        foreach ($input_vars as $input_key => $input_value) {
79
-            $defaults = [
80
-                'append_content' => '',
81
-                'css_class'      => '',
82
-                'cols'           => 80,
83
-                'db-col'         => 'column_in_db',
84
-                'format'         => '%d',
85
-                'input'          => 'hidden',
86
-                'label'          => esc_html__('No label', 'event_espresso'),
87
-                'name'           => $input_key,
88
-                'options'        => [],
89
-                'required'       => false,
90
-                'tabindex'       => 0,
91
-                'rows'           => 10,
92
-                'type'           => 'int',
93
-                'validation'     => true,
94
-                'value'          => 'some_value_for_field',
95
-            ];
96
-
97
-            $input_value = wp_parse_args($input_value, $defaults);
98
-
99
-            $append_content = $input_value['append_content'];
100
-            $css_class      = $input_value['css_class'];
101
-            $cols           = $input_value['cols'];
102
-            $label          = $input_value['label'];
103
-            $name           = $input_value['name'];
104
-            $options        = $input_value['options'];
105
-            $required       = $input_value['required'];
106
-            $tab_index      = $input_value['tabindex'];
107
-            $rows           = $input_value['rows'];
108
-            $type           = $input_value['input'];
109
-            $value          = $input_value['value'];
110
-
111
-            $id    = $form_id ? $form_id . '-' . $input_key : $input_key;
112
-            $class = $required ? 'required ' . $css_class : $css_class;
113
-
114
-            // what type of input are we dealing with ?
115
-            switch ($type) {
116
-                case 'checkbox':
117
-                case 'radio':
118
-                    $field = self::adminMulti($value, $class, $id, $name, $required, $tab_index, $type, 1, $label);
119
-                    $field .= $append_content ?: '';
120
-                    break;
121
-
122
-                case 'hidden':
123
-                    $field           = null;
124
-                    $hidden_inputs[] = self::adminHidden($css_class, $id, $name, $value);
125
-                    break;
126
-
127
-                case 'select':
128
-                    $options = is_array($options) ? $options : explode(',', $options);
129
-                    $field   = self::adminLabel($id, $label, $required);
130
-                    $field   .= self::adminSelect($value, $class, $id, $name, $required, $tab_index, $options);
131
-                    $field   .= $append_content ?: '';
132
-                    break;
133
-
134
-                case 'textarea':
135
-                    $field = self::adminLabel($id, $label, $required);
136
-                    $field .= self::adminTextarea($class, $cols, $id, $name, $required, $rows, $tab_index, $value);
137
-                    $field .= $append_content ?: '';
138
-                    break;
139
-
140
-                case 'wp_editor':
141
-                    $label = esc_html($label);
142
-                    $field = "<h4>{$label}</h4>";
143
-                    $field .= $append_content ?: '';
144
-                    $field .= self::adminWpEditor(
145
-                        $class,
146
-                        $id,
147
-                        $name,
148
-                        $rows,
149
-                        $tab_index,
150
-                        $value
151
-                    );
152
-                    break;
153
-
154
-                default:
155
-                    $field = self::adminLabel($id, $label, $required);
156
-                    $field .= self::adminText($class, $id, $name, $required, $tab_index, $value);
157
-                    $field .= $append_content ?: '';
158
-            }
159
-            if ($field) {
160
-                $inputs[] = $field;
161
-            }
162
-        } // end foreach( $input_vars as $input_key => $input_value )
163
-
164
-        if (! empty($inputs)) {
165
-            $glue   = "
30
+	/**
31
+	 *  Generates HTML for the forms used on admin pages
32
+	 *
33
+	 *
34
+	 * @static
35
+	 * @access public
36
+	 * @param array $input_vars - array of input field details
37
+	 *                          format:
38
+	 *                          $template_form_fields['field-id'] = array(
39
+	 *                          'name' => 'name_attribute',
40
+	 *                          'label' => esc_html__('Field Label', 'event_espresso'), //or false
41
+	 *                          'input' => 'hidden', //field input type can be 'text', 'select', 'textarea', 'hidden',
42
+	 *                          'checkbox', 'wp_editor'
43
+	 *                          'type' => 'int', //what "type" the value is (i.e. string, int etc)
44
+	 *                          'required' => false, //boolean for whether the field is required
45
+	 *                          'validation' => true, //boolean, whether to validate the field (todo)
46
+	 *                          'value' => 'some_value_for_field', //what value is used for field
47
+	 *                          'format' => '%d', //what format the value is (%d, %f, or %s)
48
+	 *                          'db-col' => 'column_in_db' //used to indicate which column the field corresponds with
49
+	 *                          in the db
50
+	 *                          'options' => optiona, optionb || array('value' => 'label', '') //if the input type is
51
+	 *                          "select", this allows you to set the args for the different <option> tags.
52
+	 *                          'tabindex' => 1 //this allows you to set the tabindex for the field.
53
+	 *                          'append_content' => '' //this allows you to send in html content to append to the
54
+	 *                          field.
55
+	 *                          )
56
+	 * @param string|null $form_id    - used for defining unique identifiers for the form.
57
+	 * @return string
58
+	 * @todo   : at some point we can break this down into other static methods to abstract it a bit better.
59
+	 */
60
+	public static function get_form_fields(array $input_vars = [], ?string $form_id = '')
61
+	{
62
+
63
+		if (empty($input_vars)) {
64
+			EE_Error::add_error(
65
+				esc_html__('missing required variables for the form field generator', 'event_espresso'),
66
+				__FILE__,
67
+				__FUNCTION__,
68
+				__LINE__
69
+			);
70
+			return false;
71
+		}
72
+
73
+		$output        = "";
74
+		$inputs        = [];
75
+		$hidden_inputs = [];
76
+
77
+		// cycle thru inputs
78
+		foreach ($input_vars as $input_key => $input_value) {
79
+			$defaults = [
80
+				'append_content' => '',
81
+				'css_class'      => '',
82
+				'cols'           => 80,
83
+				'db-col'         => 'column_in_db',
84
+				'format'         => '%d',
85
+				'input'          => 'hidden',
86
+				'label'          => esc_html__('No label', 'event_espresso'),
87
+				'name'           => $input_key,
88
+				'options'        => [],
89
+				'required'       => false,
90
+				'tabindex'       => 0,
91
+				'rows'           => 10,
92
+				'type'           => 'int',
93
+				'validation'     => true,
94
+				'value'          => 'some_value_for_field',
95
+			];
96
+
97
+			$input_value = wp_parse_args($input_value, $defaults);
98
+
99
+			$append_content = $input_value['append_content'];
100
+			$css_class      = $input_value['css_class'];
101
+			$cols           = $input_value['cols'];
102
+			$label          = $input_value['label'];
103
+			$name           = $input_value['name'];
104
+			$options        = $input_value['options'];
105
+			$required       = $input_value['required'];
106
+			$tab_index      = $input_value['tabindex'];
107
+			$rows           = $input_value['rows'];
108
+			$type           = $input_value['input'];
109
+			$value          = $input_value['value'];
110
+
111
+			$id    = $form_id ? $form_id . '-' . $input_key : $input_key;
112
+			$class = $required ? 'required ' . $css_class : $css_class;
113
+
114
+			// what type of input are we dealing with ?
115
+			switch ($type) {
116
+				case 'checkbox':
117
+				case 'radio':
118
+					$field = self::adminMulti($value, $class, $id, $name, $required, $tab_index, $type, 1, $label);
119
+					$field .= $append_content ?: '';
120
+					break;
121
+
122
+				case 'hidden':
123
+					$field           = null;
124
+					$hidden_inputs[] = self::adminHidden($css_class, $id, $name, $value);
125
+					break;
126
+
127
+				case 'select':
128
+					$options = is_array($options) ? $options : explode(',', $options);
129
+					$field   = self::adminLabel($id, $label, $required);
130
+					$field   .= self::adminSelect($value, $class, $id, $name, $required, $tab_index, $options);
131
+					$field   .= $append_content ?: '';
132
+					break;
133
+
134
+				case 'textarea':
135
+					$field = self::adminLabel($id, $label, $required);
136
+					$field .= self::adminTextarea($class, $cols, $id, $name, $required, $rows, $tab_index, $value);
137
+					$field .= $append_content ?: '';
138
+					break;
139
+
140
+				case 'wp_editor':
141
+					$label = esc_html($label);
142
+					$field = "<h4>{$label}</h4>";
143
+					$field .= $append_content ?: '';
144
+					$field .= self::adminWpEditor(
145
+						$class,
146
+						$id,
147
+						$name,
148
+						$rows,
149
+						$tab_index,
150
+						$value
151
+					);
152
+					break;
153
+
154
+				default:
155
+					$field = self::adminLabel($id, $label, $required);
156
+					$field .= self::adminText($class, $id, $name, $required, $tab_index, $value);
157
+					$field .= $append_content ?: '';
158
+			}
159
+			if ($field) {
160
+				$inputs[] = $field;
161
+			}
162
+		} // end foreach( $input_vars as $input_key => $input_value )
163
+
164
+		if (! empty($inputs)) {
165
+			$glue   = "
166 166
                 </li>
167 167
                 <li>
168 168
                     ";
169
-            $inputs = implode($glue, $inputs);
170
-            $output = "
169
+			$inputs = implode($glue, $inputs);
170
+			$output = "
171 171
             <ul>
172 172
                 <li>
173 173
                 {$inputs}
174 174
                 </li>
175 175
             </ul>
176 176
             ";
177
-        }
178
-        return $output . implode("\n", $hidden_inputs);
179
-    }
180
-
181
-
182
-    /**
183
-     * form_fields_array
184
-     * This utility function assembles form fields from a given structured array with field information.
185
-     * //TODO: This is an alternate generator that we may want to use instead.
186
-     *
187
-     * @param array $fields structured array of fields to assemble in the following format:
188
-     *                      [field_name] => array(
189
-     *                      ['label'] => 'label for field',
190
-     *                      ['labels'] => array('label_1', 'label_2'); //optional - if the field type is a multi select
191
-     *                      type of field you can indicated the labels for each option via this index
192
-     *                      ['extra_desc'] => 'extra description for the field', //optional
193
-     *                      ['type'] => 'textarea'|'text'|'wp_editor'|'checkbox'|'radio'|'hidden'|'select', //defaults
194
-     *                      to text
195
-     *                      ['value'] => 'value that goes in the field', //(if multi then this is an array of values
196
-     *                      and the 'default' paramater will be used for what is selected)
197
-     *                      ['default'] => 'default if the field type is multi (i.e. select or radios or checkboxes)',
198
-     *                      ['class'] => 'name-of-class(es)-for-input',
199
-     *                      ['classes'] => array('class_1', 'class_2'); //optional - if the field type is a multi
200
-     *                      select type of field you can indicate the css class for each option via this index.
201
-     *                      ['id'] => 'css-id-for-input') //defaults to 'field_name'
202
-     *                      ['unique_id'] => 1 //defaults to empty string.  This is useful for when the fields
203
-     *                      generated are going to be used in a loop and you want to make sure that the field
204
-     *                      identifiers are unique from each other.
205
-     *                      ['dimensions'] => array(100,300), //defaults to empty array.  This is used by field types
206
-     *                      such as textarea to indicate cols/rows.
207
-     *                      ['tabindex'] => '' //this allows you to set the tabindex for the field.
208
-     *                      ['wpeditor_args'] => array() //if the type of field is wpeditor then this can optionally
209
-     *                      contain an array of arguments for the editor setup.
210
-     *
211
-     * @return array         an array of inputs for form indexed by field name, and in the following structure:
212
-     *     [field_name] => array( 'label' => '{label_html}', 'field' => '{input_html}'
213
-     */
214
-    public static function get_form_fields_array($fields)
215
-    {
216
-
217
-        $form_fields = [];
218
-        $fields      = (array) $fields;
219
-
220
-        foreach ($fields as $field_name => $field_atts) {
221
-            // defaults:
222
-            $defaults = [
223
-                'class'         => '',
224
-                'classes'       => '',
225
-                'default'       => '',
226
-                'dimensions'    => ['10', '5'],
227
-                'extra_desc'    => '',
228
-                'id'            => $field_name,
229
-                'label'         => '',
230
-                'labels'        => '',
231
-                'required'      => false,
232
-                'tabindex'      => 0,
233
-                'type'          => 'text',
234
-                'unique_id'     => '',
235
-                'value'         => '',
236
-                'wpeditor_args' => [],
237
-            ];
238
-            // merge defaults with passed arguments
239
-            $_fields = wp_parse_args($field_atts, $defaults);
240
-
241
-            $class          = $_fields['class'];
242
-            $classes        = $_fields['classes'];
243
-            $default        = $_fields['default'];
244
-            $dims           = $_fields['dimensions'];
245
-            $extra_desc     = $_fields['extra_desc'];
246
-            $id             = $_fields['id'];
247
-            $label          = $_fields['label'];
248
-            $labels         = $_fields['labels'];
249
-            $required       = $_fields['required'];
250
-            $tab_index      = $_fields['tabindex'];
251
-            $type           = $_fields['type'];
252
-            $unique_id      = $_fields['unique_id'];
253
-            $value          = $_fields['value'];
254
-            $wp_editor_args = $_fields['wpeditor_args'];
255
-
256
-            // generate label
257
-            $label = ! empty($label) ? self::adminLabel($id, $label, $required) : '';
258
-            // generate field name
259
-            $name = ! empty($unique_id) ? $field_name . '[' . $unique_id . ']' : $field_name;
260
-
261
-            // we determine what we're building based on the type
262
-            switch ($type) {
263
-                case 'checkbox':
264
-                case 'radio':
265
-                    if (is_array($value)) {
266
-                        $c_input = '';
267
-                        foreach ($value as $key => $val) {
268
-                            $c_input .= self::adminMulti(
269
-                                $default,
270
-                                isset($classes[ $key ]) ? $classes[ $key ] : '',
271
-                                $field_name . '_' . $value,
272
-                                $name,
273
-                                $required,
274
-                                $tab_index,
275
-                                $type,
276
-                                $val,
277
-                                isset($labels[ $key ]) ? $labels[ $key ] : ''
278
-                            );
279
-                        }
280
-                        $field = $c_input;
281
-                    } else {
282
-                        $field = self::adminMulti(
283
-                            $default,
284
-                            $class,
285
-                            $id,
286
-                            $name,
287
-                            $required,
288
-                            $tab_index,
289
-                            $type,
290
-                            $value,
291
-                            $_fields['label']
292
-                        );
293
-                    }
294
-                    break;
295
-
296
-                case 'hidden':
297
-                    $field = self::adminHidden($class, $id, $name, $value);
298
-                    break;
299
-
300
-                case 'select':
301
-                    $options = [];
302
-                    foreach ($value as $key => $val) {
303
-                        $options[ $val ] = isset($labels[ $key ]) ? $labels[ $key ] : '';
304
-                    }
305
-                    $field = self::adminSelect($default, $class, $id, $name, $required, $tab_index, $options);
306
-                    break;
307
-
308
-                case 'textarea':
309
-                    $field =
310
-                        self::adminTextarea($class, $dims[0], $id, $name, $required, $dims[1], $tab_index, $value);
311
-                    break;
312
-
313
-                case 'wp_editor':
314
-                    $field = self::adminWpEditor(
315
-                        $class,
316
-                        $_fields['id'],
317
-                        $name,
318
-                        $dims[1],
319
-                        $tab_index,
320
-                        $value,
321
-                        $wp_editor_args
322
-                    );
323
-                    break;
324
-
325
-                default:
326
-                    $field = self::adminText($class, $id, $name, $required, $tab_index, $value);
327
-            }
328
-
329
-            $form_fields[ $field_name ] = ['label' => $label, 'field' => $field . $extra_desc];
330
-        }
331
-
332
-        return $form_fields;
333
-    }
334
-
335
-
336
-    /**
337
-     * @param string $class
338
-     * @param string $id
339
-     * @param string $name
340
-     * @param string $value
341
-     * @return string
342
-     * @since   4.10.14.p
343
-     */
344
-    public static function adminHidden($class, $id, $name, $value): string
345
-    {
346
-        $id    = esc_attr($id);
347
-        $name  = esc_attr($name);
348
-        $class = esc_attr($class);
349
-        return "
177
+		}
178
+		return $output . implode("\n", $hidden_inputs);
179
+	}
180
+
181
+
182
+	/**
183
+	 * form_fields_array
184
+	 * This utility function assembles form fields from a given structured array with field information.
185
+	 * //TODO: This is an alternate generator that we may want to use instead.
186
+	 *
187
+	 * @param array $fields structured array of fields to assemble in the following format:
188
+	 *                      [field_name] => array(
189
+	 *                      ['label'] => 'label for field',
190
+	 *                      ['labels'] => array('label_1', 'label_2'); //optional - if the field type is a multi select
191
+	 *                      type of field you can indicated the labels for each option via this index
192
+	 *                      ['extra_desc'] => 'extra description for the field', //optional
193
+	 *                      ['type'] => 'textarea'|'text'|'wp_editor'|'checkbox'|'radio'|'hidden'|'select', //defaults
194
+	 *                      to text
195
+	 *                      ['value'] => 'value that goes in the field', //(if multi then this is an array of values
196
+	 *                      and the 'default' paramater will be used for what is selected)
197
+	 *                      ['default'] => 'default if the field type is multi (i.e. select or radios or checkboxes)',
198
+	 *                      ['class'] => 'name-of-class(es)-for-input',
199
+	 *                      ['classes'] => array('class_1', 'class_2'); //optional - if the field type is a multi
200
+	 *                      select type of field you can indicate the css class for each option via this index.
201
+	 *                      ['id'] => 'css-id-for-input') //defaults to 'field_name'
202
+	 *                      ['unique_id'] => 1 //defaults to empty string.  This is useful for when the fields
203
+	 *                      generated are going to be used in a loop and you want to make sure that the field
204
+	 *                      identifiers are unique from each other.
205
+	 *                      ['dimensions'] => array(100,300), //defaults to empty array.  This is used by field types
206
+	 *                      such as textarea to indicate cols/rows.
207
+	 *                      ['tabindex'] => '' //this allows you to set the tabindex for the field.
208
+	 *                      ['wpeditor_args'] => array() //if the type of field is wpeditor then this can optionally
209
+	 *                      contain an array of arguments for the editor setup.
210
+	 *
211
+	 * @return array         an array of inputs for form indexed by field name, and in the following structure:
212
+	 *     [field_name] => array( 'label' => '{label_html}', 'field' => '{input_html}'
213
+	 */
214
+	public static function get_form_fields_array($fields)
215
+	{
216
+
217
+		$form_fields = [];
218
+		$fields      = (array) $fields;
219
+
220
+		foreach ($fields as $field_name => $field_atts) {
221
+			// defaults:
222
+			$defaults = [
223
+				'class'         => '',
224
+				'classes'       => '',
225
+				'default'       => '',
226
+				'dimensions'    => ['10', '5'],
227
+				'extra_desc'    => '',
228
+				'id'            => $field_name,
229
+				'label'         => '',
230
+				'labels'        => '',
231
+				'required'      => false,
232
+				'tabindex'      => 0,
233
+				'type'          => 'text',
234
+				'unique_id'     => '',
235
+				'value'         => '',
236
+				'wpeditor_args' => [],
237
+			];
238
+			// merge defaults with passed arguments
239
+			$_fields = wp_parse_args($field_atts, $defaults);
240
+
241
+			$class          = $_fields['class'];
242
+			$classes        = $_fields['classes'];
243
+			$default        = $_fields['default'];
244
+			$dims           = $_fields['dimensions'];
245
+			$extra_desc     = $_fields['extra_desc'];
246
+			$id             = $_fields['id'];
247
+			$label          = $_fields['label'];
248
+			$labels         = $_fields['labels'];
249
+			$required       = $_fields['required'];
250
+			$tab_index      = $_fields['tabindex'];
251
+			$type           = $_fields['type'];
252
+			$unique_id      = $_fields['unique_id'];
253
+			$value          = $_fields['value'];
254
+			$wp_editor_args = $_fields['wpeditor_args'];
255
+
256
+			// generate label
257
+			$label = ! empty($label) ? self::adminLabel($id, $label, $required) : '';
258
+			// generate field name
259
+			$name = ! empty($unique_id) ? $field_name . '[' . $unique_id . ']' : $field_name;
260
+
261
+			// we determine what we're building based on the type
262
+			switch ($type) {
263
+				case 'checkbox':
264
+				case 'radio':
265
+					if (is_array($value)) {
266
+						$c_input = '';
267
+						foreach ($value as $key => $val) {
268
+							$c_input .= self::adminMulti(
269
+								$default,
270
+								isset($classes[ $key ]) ? $classes[ $key ] : '',
271
+								$field_name . '_' . $value,
272
+								$name,
273
+								$required,
274
+								$tab_index,
275
+								$type,
276
+								$val,
277
+								isset($labels[ $key ]) ? $labels[ $key ] : ''
278
+							);
279
+						}
280
+						$field = $c_input;
281
+					} else {
282
+						$field = self::adminMulti(
283
+							$default,
284
+							$class,
285
+							$id,
286
+							$name,
287
+							$required,
288
+							$tab_index,
289
+							$type,
290
+							$value,
291
+							$_fields['label']
292
+						);
293
+					}
294
+					break;
295
+
296
+				case 'hidden':
297
+					$field = self::adminHidden($class, $id, $name, $value);
298
+					break;
299
+
300
+				case 'select':
301
+					$options = [];
302
+					foreach ($value as $key => $val) {
303
+						$options[ $val ] = isset($labels[ $key ]) ? $labels[ $key ] : '';
304
+					}
305
+					$field = self::adminSelect($default, $class, $id, $name, $required, $tab_index, $options);
306
+					break;
307
+
308
+				case 'textarea':
309
+					$field =
310
+						self::adminTextarea($class, $dims[0], $id, $name, $required, $dims[1], $tab_index, $value);
311
+					break;
312
+
313
+				case 'wp_editor':
314
+					$field = self::adminWpEditor(
315
+						$class,
316
+						$_fields['id'],
317
+						$name,
318
+						$dims[1],
319
+						$tab_index,
320
+						$value,
321
+						$wp_editor_args
322
+					);
323
+					break;
324
+
325
+				default:
326
+					$field = self::adminText($class, $id, $name, $required, $tab_index, $value);
327
+			}
328
+
329
+			$form_fields[ $field_name ] = ['label' => $label, 'field' => $field . $extra_desc];
330
+		}
331
+
332
+		return $form_fields;
333
+	}
334
+
335
+
336
+	/**
337
+	 * @param string $class
338
+	 * @param string $id
339
+	 * @param string $name
340
+	 * @param string $value
341
+	 * @return string
342
+	 * @since   4.10.14.p
343
+	 */
344
+	public static function adminHidden($class, $id, $name, $value): string
345
+	{
346
+		$id    = esc_attr($id);
347
+		$name  = esc_attr($name);
348
+		$class = esc_attr($class);
349
+		return "
350 350
         <input name='$name' type='hidden' id='$id' class='$class' value='$value' />";
351
-    }
352
-
353
-
354
-    /**
355
-     * @param string $id
356
-     * @param string $label
357
-     * @param string $required
358
-     * @return string
359
-     * @since   4.10.14.p
360
-     */
361
-    private static function adminLabel($id, $label, $required)
362
-    {
363
-        $id       = esc_attr($id);
364
-        $label    = esc_html($label);
365
-        $required = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? " <span>*</span>" : '';
366
-        return "<label for='{$id}'>{$label}{$required}</label>";
367
-    }
368
-
369
-
370
-    /**
371
-     * @param string $default
372
-     * @param string $class
373
-     * @param string $id
374
-     * @param string $name
375
-     * @param string $required
376
-     * @param int    $tab_index
377
-     * @param string $type
378
-     * @param string $value
379
-     * @param string $label
380
-     * @return string
381
-     * @since   4.10.14.p
382
-     */
383
-    private static function adminMulti($default, $class, $id, $name, $required, $tab_index, $type, $value, $label = '')
384
-    {
385
-        $id        = esc_attr($id);
386
-        $name      = esc_attr($name);
387
-        $class     = esc_attr($class);
388
-        $tab_index = absint($tab_index);
389
-        $checked   = ! empty($default) && $default == $value ? 'checked ' : '';
390
-        $required  = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
391
-        $input     = "
351
+	}
352
+
353
+
354
+	/**
355
+	 * @param string $id
356
+	 * @param string $label
357
+	 * @param string $required
358
+	 * @return string
359
+	 * @since   4.10.14.p
360
+	 */
361
+	private static function adminLabel($id, $label, $required)
362
+	{
363
+		$id       = esc_attr($id);
364
+		$label    = esc_html($label);
365
+		$required = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? " <span>*</span>" : '';
366
+		return "<label for='{$id}'>{$label}{$required}</label>";
367
+	}
368
+
369
+
370
+	/**
371
+	 * @param string $default
372
+	 * @param string $class
373
+	 * @param string $id
374
+	 * @param string $name
375
+	 * @param string $required
376
+	 * @param int    $tab_index
377
+	 * @param string $type
378
+	 * @param string $value
379
+	 * @param string $label
380
+	 * @return string
381
+	 * @since   4.10.14.p
382
+	 */
383
+	private static function adminMulti($default, $class, $id, $name, $required, $tab_index, $type, $value, $label = '')
384
+	{
385
+		$id        = esc_attr($id);
386
+		$name      = esc_attr($name);
387
+		$class     = esc_attr($class);
388
+		$tab_index = absint($tab_index);
389
+		$checked   = ! empty($default) && $default == $value ? 'checked ' : '';
390
+		$required  = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
391
+		$input     = "
392 392
         <input name='{$name}[]' type='{$type}' id='{$id}' class='{$class}' value='{$value}' {$checked} {$required} tabindex='{$tab_index}'/>";
393
-        if ($label === '') {
394
-            return $input;
395
-        }
396
-        $label = esc_html($label);
397
-        $label_class = self::appendInputSizeClass('', $label);
398
-        $label_class = $label_class ? ' class="' . $label_class . '"' : '';
399
-        return "
393
+		if ($label === '') {
394
+			return $input;
395
+		}
396
+		$label = esc_html($label);
397
+		$label_class = self::appendInputSizeClass('', $label);
398
+		$label_class = $label_class ? ' class="' . $label_class . '"' : '';
399
+		return "
400 400
         <label for='$id'{$label_class}>
401 401
             {$input}
402 402
             {$label}
403 403
         </label>";
404
-    }
405
-
406
-
407
-    /**
408
-     * @param string $default
409
-     * @param string $class
410
-     * @param string $id
411
-     * @param string $name
412
-     * @param string $required
413
-     * @param int    $tab_index
414
-     * @param array  $options
415
-     * @return string
416
-     * @since   4.10.14.p
417
-     */
418
-    private static function adminSelect($default, $class, $id, $name, $required, $tab_index, $options = [])
419
-    {
420
-        $options_array = [];
421
-        foreach ($options as $value => $label) {
422
-            $selected        = ! empty($default) && $default == $value ? 'selected' : '';
423
-            $value           = esc_attr($value);
424
-            $label           = wp_strip_all_tags($label);
425
-            $options_array[] = "<option value='{$value}' {$selected}>{$label}</option>";
426
-        }
427
-        $options_html = implode("\n", $options_array);
428
-        $id           = esc_attr($id);
429
-        $name         = esc_attr($name);
430
-        $class        = esc_attr($class);
431
-        $tab_index    = absint($tab_index);
432
-        $required     = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
433
-
434
-        $class = self::appendInputSizeClass($class, $options);
435
-
436
-        return "
404
+	}
405
+
406
+
407
+	/**
408
+	 * @param string $default
409
+	 * @param string $class
410
+	 * @param string $id
411
+	 * @param string $name
412
+	 * @param string $required
413
+	 * @param int    $tab_index
414
+	 * @param array  $options
415
+	 * @return string
416
+	 * @since   4.10.14.p
417
+	 */
418
+	private static function adminSelect($default, $class, $id, $name, $required, $tab_index, $options = [])
419
+	{
420
+		$options_array = [];
421
+		foreach ($options as $value => $label) {
422
+			$selected        = ! empty($default) && $default == $value ? 'selected' : '';
423
+			$value           = esc_attr($value);
424
+			$label           = wp_strip_all_tags($label);
425
+			$options_array[] = "<option value='{$value}' {$selected}>{$label}</option>";
426
+		}
427
+		$options_html = implode("\n", $options_array);
428
+		$id           = esc_attr($id);
429
+		$name         = esc_attr($name);
430
+		$class        = esc_attr($class);
431
+		$tab_index    = absint($tab_index);
432
+		$required     = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
433
+
434
+		$class = self::appendInputSizeClass($class, $options);
435
+
436
+		return "
437 437
         <select name='{$name}' id='{$id}' class='{$class}' {$required} tabindex='{$tab_index}'>
438 438
             {$options_html}
439 439
         </select>";
440
-    }
441
-
442
-
443
-    /**
444
-     * @param string $class
445
-     * @param string $id
446
-     * @param string $name
447
-     * @param string $required
448
-     * @param int    $tab_index
449
-     * @param string $value
450
-     * @return string
451
-     * @since   4.10.14.p
452
-     */
453
-    private static function adminText($class, $id, $name, $required, $tab_index, $value)
454
-    {
455
-        $id        = esc_attr($id);
456
-        $name      = esc_attr($name);
457
-        $class     = esc_attr($class);
458
-        $tab_index = absint($tab_index);
459
-        $required  = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
460
-        $class     = self::appendInputSizeClass($class, $value);
461
-        $value     = esc_attr($value);
462
-        return "
440
+	}
441
+
442
+
443
+	/**
444
+	 * @param string $class
445
+	 * @param string $id
446
+	 * @param string $name
447
+	 * @param string $required
448
+	 * @param int    $tab_index
449
+	 * @param string $value
450
+	 * @return string
451
+	 * @since   4.10.14.p
452
+	 */
453
+	private static function adminText($class, $id, $name, $required, $tab_index, $value)
454
+	{
455
+		$id        = esc_attr($id);
456
+		$name      = esc_attr($name);
457
+		$class     = esc_attr($class);
458
+		$tab_index = absint($tab_index);
459
+		$required  = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
460
+		$class     = self::appendInputSizeClass($class, $value);
461
+		$value     = esc_attr($value);
462
+		return "
463 463
         <input name='{$name}' type='text' id='{$id}' class='{$class}' value='{$value}' {$required} tabindex='{$tab_index}'/>";
464
-    }
465
-
466
-
467
-    /**
468
-     * @param string $class
469
-     * @param int    $cols
470
-     * @param string $id
471
-     * @param string $name
472
-     * @param string $required
473
-     * @param int    $rows
474
-     * @param int    $tab_index
475
-     * @param string $value
476
-     * @return string
477
-     * @since   4.10.14.p
478
-     */
479
-    private static function adminTextarea($class, $cols, $id, $name, $required, $rows, $tab_index, $value)
480
-    {
481
-        $id        = esc_attr($id);
482
-        $name      = esc_attr($name);
483
-        $class     = esc_attr($class);
484
-        $cols      = absint($cols);
485
-        $rows      = absint($rows);
486
-        $value     = esc_textarea($value);
487
-        $tab_index = absint($tab_index);
488
-        $required  = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
489
-        return "
464
+	}
465
+
466
+
467
+	/**
468
+	 * @param string $class
469
+	 * @param int    $cols
470
+	 * @param string $id
471
+	 * @param string $name
472
+	 * @param string $required
473
+	 * @param int    $rows
474
+	 * @param int    $tab_index
475
+	 * @param string $value
476
+	 * @return string
477
+	 * @since   4.10.14.p
478
+	 */
479
+	private static function adminTextarea($class, $cols, $id, $name, $required, $rows, $tab_index, $value)
480
+	{
481
+		$id        = esc_attr($id);
482
+		$name      = esc_attr($name);
483
+		$class     = esc_attr($class);
484
+		$cols      = absint($cols);
485
+		$rows      = absint($rows);
486
+		$value     = esc_textarea($value);
487
+		$tab_index = absint($tab_index);
488
+		$required  = filter_var($required, FILTER_VALIDATE_BOOLEAN) ? 'required' : '';
489
+		return "
490 490
         <textarea name='{$name}' id='{$id}' class='{$class}' rows='{$rows}' cols='{$cols}' {$required} tabindex='{$tab_index}'>{$value}</textarea>";
491
-    }
492
-
493
-
494
-    /**
495
-     * @param string $class
496
-     * @param string $id
497
-     * @param string $name
498
-     * @param int    $rows
499
-     * @param int    $tab_index
500
-     * @param string $value
501
-     * @param array  $wp_editor_args
502
-     * @return false|string
503
-     * @since   4.10.14.p
504
-     */
505
-    private static function adminWpEditor($class, $id, $name, $rows, $tab_index, $value, $wp_editor_args = [])
506
-    {
507
-        $editor_settings = $wp_editor_args + [
508
-                'textarea_name' => esc_attr($name),
509
-                'textarea_rows' => absint($rows),
510
-                'editor_class'  => esc_attr($class),
511
-                'tabindex'      => absint($tab_index),
512
-            ];
513
-        ob_start();
514
-        wp_editor($value, esc_attr($id), $editor_settings);
515
-        return ob_get_clean();
516
-    }
517
-
518
-
519
-    /**
520
-     * espresso admin page select_input
521
-     * Turns an array into a select fields
522
-     *
523
-     * @static
524
-     * @access public
525
-     * @param string  $name       field name
526
-     * @param array   $values     option values, numbered array starting at 0, where each value is an array with a key
527
-     *                            'text' (meaning text to display' and 'id' (meaning the internal value) eg:
528
-     *                            array(1=>array('text'=>'Monday','id'=>1),2=>array('text'=>'Tuesday','id'=>2)...). or
529
-     *                            as an array of key-value pairs, where the key is to be used for the select input's
530
-     *                            name, and the value will be the text shown to the user.  Optionally you can also
531
-     *                            include an additional key of "class" which will add a specific class to the option
532
-     *                            for that value.
533
-     * @param string  $default    default value
534
-     * @param string  $parameters extra parameters
535
-     * @param string  $class      css class
536
-     * @param boolean $autosize   whether to autosize the select or not
537
-     * @return string              html string for the select input
538
-     */
539
-    public static function select_input(
540
-        $name,
541
-        $values,
542
-        $default = '',
543
-        $parameters = '',
544
-        $class = '',
545
-        $autosize = true
546
-    ) {
547
-        // if $values was submitted in the wrong format, convert it over
548
-        if (! empty($values) && (! array_key_exists(0, $values) || ! is_array($values[0]))) {
549
-            $converted_values = [];
550
-            foreach ($values as $id => $text) {
551
-                $converted_values[] = ['id' => $id, 'text' => $text];
552
-            }
553
-            $values = $converted_values;
554
-        }
555
-
556
-        $field =
557
-            '<select id="' . EEH_Formatter::ee_tep_output_string($name)
558
-            . '" name="' . EEH_Formatter::ee_tep_output_string($name)
559
-            . '"';
560
-
561
-        if (EEH_Formatter::ee_tep_not_null($parameters)) {
562
-            $field .= ' ' . $parameters;
563
-        }
564
-        $class = $autosize ? self::appendInputSizeClass($class, $values) : '';
565
-
566
-        $field .= ' class="' . $class . '">';
567
-
568
-        if (empty($default) && isset($GLOBALS[ $name ])) {
569
-            $default = stripslashes($GLOBALS[ $name ]);
570
-        }
571
-
572
-        $field .= self::selectInputOption($values, $default);
573
-        $field .= '</select>';
574
-
575
-        return $field;
576
-    }
577
-
578
-
579
-    private static function selectInputOption(array $values, $default): string
580
-    {
581
-        if (isset($values['id'], $values['text'])) {
582
-            $id = is_scalar($values['id']) ? $values['id'] : '';
583
-            $text = is_scalar($values['text']) ? $values['text'] : '';
584
-            $selected = $default == $values['id'] ? ' selected = "selected"' : '';
585
-            $html_class = isset($values['class']) ? ' class="' . $values['class'] . '"' : '';
586
-            return "<option value='{$id}'{$selected}{$html_class}>{$text}</option>";
587
-        }
588
-        $options = '';
589
-        foreach ($values as $value) {
590
-            $options .= self::selectInputOption($value, $default);
591
-        }
592
-        return $options;
593
-    }
594
-
595
-
596
-    /**
597
-     * @param mixed $value
598
-     * @return int
599
-     * @since   5.0.0.p
600
-     */
601
-    private static function getInputValueLength($value): int
602
-    {
603
-        if ($value instanceof EE_Question_Option) {
604
-            return self::getInputValueLength($value->desc());
605
-        }
606
-        if (is_array($value)) {
607
-            $chars = 0;
608
-            foreach ($value as $val) {
609
-                $length = self::getInputValueLength($val);
610
-                $chars = max($length, $chars);
611
-            }
612
-            return $chars;
613
-        }
614
-        // not a primitive? return something big
615
-        if (! is_scalar($value)) {
616
-            return 500;
617
-        }
618
-        return strlen((string) $value);
619
-    }
620
-
621
-
622
-    /**
623
-     * @param string $class
624
-     * @param mixed $value
625
-     * @return string
626
-     * @since   5.0.0.p
627
-     */
628
-    private static function appendInputSizeClass(string $class, $value): string
629
-    {
630
-        if (strpos($class, 'ee-input-width--') !== false) {
631
-            return $class;
632
-        }
633
-        $chars = self::getInputValueLength($value);
634
-        if ($chars && $chars < 5) {
635
-            return "{$class} ee-input-width--tiny";
636
-        }
637
-        if ($chars && $chars < 25) {
638
-            return "{$class} ee-input-width--small";
639
-        }
640
-        if ($chars && $chars > 100) {
641
-            return "{$class} ee-input-width--big";
642
-        }
643
-        return "{$class} ee-input-width--reg";
644
-    }
645
-
646
-
647
-    /**
648
-     * generate_question_groups_html
649
-     *
650
-     * @param array  $question_groups
651
-     * @param string $group_wrapper
652
-     * @return string HTML
653
-     * @throws EE_Error
654
-     * @throws ReflectionException
655
-     */
656
-    public static function generate_question_groups_html($question_groups = [], $group_wrapper = 'fieldset')
657
-    {
658
-
659
-        $html                            = '';
660
-        $before_question_group_questions =
661
-            apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions', '');
662
-        $after_question_group_questions  =
663
-            apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions', '');
664
-
665
-        if (! empty($question_groups)) {
666
-            // loop thru question groups
667
-            foreach ($question_groups as $QSG) {
668
-                // check that questions exist
669
-                if (! empty($QSG['QSG_questions'])) {
670
-                    // use fieldsets
671
-                    $html .= "\n\t"
672
-                             . '<'
673
-                             . $group_wrapper
674
-                             . ' class="espresso-question-group-wrap" id="'
675
-                             . $QSG['QSG_identifier']
676
-                             . '">';
677
-                    // group_name
678
-                    $html .= $QSG['QSG_show_group_name']
679
-                        ? "\n\t\t"
680
-                          . '<h5 class="espresso-question-group-title-h5 section-title">'
681
-                          . self::prep_answer($QSG['QSG_name'])
682
-                          . '</h5>'
683
-                        : '';
684
-                    // group_desc
685
-                    $html .= $QSG['QSG_show_group_desc'] && ! empty($QSG['QSG_desc'])
686
-                        ? '<div class="espresso-question-group-desc-pg">'
687
-                          . self::prep_answer($QSG['QSG_desc'])
688
-                          . '</div>'
689
-                        : '';
690
-
691
-                    $html .= $before_question_group_questions;
692
-                    // loop thru questions
693
-                    foreach ($QSG['QSG_questions'] as $question) {
694
-                        $QFI  = new EE_Question_Form_Input(
695
-                            $question['qst_obj'],
696
-                            $question['ans_obj'],
697
-                            $question
698
-                        );
699
-                        $html .= self::generate_form_input($QFI);
700
-                    }
701
-                    $html .= $after_question_group_questions;
702
-                    $html .= "\n\t" . '</' . $group_wrapper . '>';
703
-                }
704
-            }
705
-        }
706
-
707
-        return $html;
708
-    }
709
-
710
-
711
-    /**
712
-     * generate_question_groups_html
713
-     *
714
-     * @param array  $question_groups
715
-     * @param array  $q_meta
716
-     * @param bool   $from_admin
717
-     * @param string $group_wrapper
718
-     * @return string HTML
719
-     * @throws EE_Error
720
-     * @throws ReflectionException
721
-     */
722
-    public static function generate_question_groups_html2(
723
-        $question_groups = [],
724
-        $q_meta = [],
725
-        $from_admin = false,
726
-        $group_wrapper = 'fieldset'
727
-    ) {
728
-
729
-        $html                            = '';
730
-        $before_question_group_questions =
731
-            apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions', '');
732
-        $after_question_group_questions  =
733
-            apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions', '');
734
-
735
-        $default_q_meta = [
736
-            'att_nmbr'    => 1,
737
-            'ticket_id'   => '',
738
-            'input_name'  => '',
739
-            'input_id'    => '',
740
-            'input_class' => '',
741
-        ];
742
-        $q_meta         = array_merge($default_q_meta, $q_meta);
743
-
744
-        if (! empty($question_groups)) {
745
-            // loop thru question groups
746
-            foreach ($question_groups as $QSG) {
747
-                if ($QSG instanceof EE_Question_Group) {
748
-                    // check that questions exist
749
-
750
-                    $where = ['QST_deleted' => 0];
751
-                    if (! $from_admin) {
752
-                        $where['QST_admin_only'] = 0;
753
-                    }
754
-                    $questions =
755
-                        $QSG->questions([$where, 'order_by' => ['Question_Group_Question.QGQ_order' => 'ASC']]);
756
-                    if (! empty($questions)) {
757
-                        // use fieldsets
758
-                        $html .= "\n\t"
759
-                                 . '<' . $group_wrapper . ' class="espresso-question-group-wrap" '
760
-                                 . 'id="' . $QSG->get('QSG_identifier') . '">';
761
-                        // group_name
762
-                        if ($QSG->show_group_name()) {
763
-                            $html .= "\n\t\t"
764
-                                     . '<h5 class="espresso-question-group-title-h5 section-title">'
765
-                                     . $QSG->get_pretty('QSG_name')
766
-                                     . '</h5>';
767
-                        }
768
-                        // group_desc
769
-                        if ($QSG->show_group_desc()) {
770
-                            $html .= '<div class="espresso-question-group-desc-pg">'
771
-                                     . $QSG->get_pretty('QSG_desc')
772
-                                     . '</div>';
773
-                        }
774
-
775
-                        $html .= $before_question_group_questions;
776
-                        // loop thru questions
777
-                        foreach ($questions as $QST) {
778
-                            $qstn_id = $QST->is_system_question() ? $QST->system_ID() : $QST->ID();
779
-
780
-                            $answer = null;
781
-
782
-                            /** @var RequestInterface $request */
783
-                            $request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
784
-                            $request_qstn = $request->getRequestParam('qstn', [], 'string', true);
785
-                            if (! empty($request_qstn) && isset($q_meta['input_id']) && isset($q_meta['att_nmbr'])) {
786
-                                // check for answer in $request_qstn in case we are reprocessing a form after an error
787
-                                if (isset($request_qstn[ $q_meta['input_id'] ][ $qstn_id ])) {
788
-                                    $answer = is_array($request_qstn[ $q_meta['input_id'] ][ $qstn_id ])
789
-                                        ? $request_qstn[ $q_meta['input_id'] ][ $qstn_id ]
790
-                                        : sanitize_text_field($request_qstn[ $q_meta['input_id'] ][ $qstn_id ]);
791
-                                }
792
-                            } elseif (isset($q_meta['attendee']) && $q_meta['attendee']) {
793
-                                // attendee data from the session
794
-                                $answer =
795
-                                    isset($q_meta['attendee'][ $qstn_id ]) ? $q_meta['attendee'][ $qstn_id ] : null;
796
-                            }
797
-
798
-
799
-                            $QFI  = new EE_Question_Form_Input(
800
-                                $QST,
801
-                                EE_Answer::new_instance(
802
-                                    [
803
-                                        'ANS_ID'    => 0,
804
-                                        'QST_ID'    => 0,
805
-                                        'REG_ID'    => 0,
806
-                                        'ANS_value' => $answer,
807
-                                    ]
808
-                                ),
809
-                                $q_meta
810
-                            );
811
-                            $html .= self::generate_form_input($QFI);
812
-                        }
813
-                        $html .= $after_question_group_questions;
814
-                        $html .= "\n\t" . '</' . $group_wrapper . '>';
815
-                    }
816
-                }
817
-            }
818
-        }
819
-        return $html;
820
-    }
821
-
822
-
823
-    /**
824
-     * generate_form_input
825
-     *
826
-     * @param EE_Question_Form_Input $QFI
827
-     * @return string HTML
828
-     * @throws EE_Error
829
-     * @throws ReflectionException
830
-     */
831
-    public static function generate_form_input(EE_Question_Form_Input $QFI)
832
-    {
833
-        if (isset($QFI->QST_admin_only) && $QFI->QST_admin_only && ! is_admin()) {
834
-            return '';
835
-        }
836
-        /** @var RequestInterface $request */
837
-        $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
838
-
839
-        $QFI = self::_load_system_dropdowns($QFI);
840
-        $QFI = self::_load_specialized_dropdowns($QFI);
841
-
842
-        // we also need to verify
843
-
844
-        $display_text = (string) $QFI->get('QST_display_text');
845
-        $input_name   = (string) $QFI->get('QST_input_name');
846
-        $answer       = $request->getRequestParam($input_name, $QFI->get('ANS_value'));
847
-        $input_id     = (string) $QFI->get('QST_input_id');
848
-        $input_class  = (string) $QFI->get('QST_input_class');
849
-        //      $disabled = $QFI->get('QST_disabled') ? ' disabled="disabled"' : '';
850
-        $disabled          = (bool) $QFI->get('QST_disabled');
851
-        $required_label    = apply_filters(' FHEE__EEH_Form_Fields__generate_form_input__required_label', '<em>*</em>');
852
-        $QST_required      = (bool) $QFI->get('QST_required');
853
-        $required          = $QST_required
854
-            ? ['label' => $required_label, 'class' => 'required needs-value', 'title' => $QST_required]
855
-            : [];
856
-        $use_html_entities = $QFI->get_meta('htmlentities');
857
-        $required_text     = (string) $QFI->get('QST_required_text') !== ''
858
-            ? (string) $QFI->get('QST_required_text')
859
-            : esc_html__('This field is required', 'event_espresso');
860
-        $required_text     = $QST_required
861
-            ? "\n\t\t\t"
862
-              . '<div class="required-text hidden">'
863
-              . self::prep_answer($required_text, $use_html_entities)
864
-              . '</div>'
865
-            : '';
866
-        $label_class       = (string) $QFI->get('label_class');
867
-        $label_class       = $label_class ? "{$label_class} espresso-form-input-lbl" : 'espresso-form-input-lbl';
868
-        $QST_options       = $QFI->options(true, $answer);
869
-        $options           = is_array($QST_options) ? self::prep_answer_options($QST_options) : [];
870
-        $system_ID         = (string) $QFI->get('QST_system');
871
-        $label_b4          = (bool) $QFI->get_meta('label_b4');
872
-        $use_desc_4_label  = (bool) $QFI->get_meta('use_desc_4_label');
873
-        $add_mobile_label  = (bool) $QFI->get_meta('add_mobile_label');
874
-
875
-
876
-        switch ($QFI->get('QST_type')) {
877
-            case 'TEXTAREA':
878
-                return EEH_Form_Fields::textarea(
879
-                    $display_text,
880
-                    $answer,
881
-                    $input_name,
882
-                    $input_id,
883
-                    $input_class,
884
-                    [],
885
-                    $required,
886
-                    $required_text,
887
-                    $label_class,
888
-                    $disabled,
889
-                    $system_ID,
890
-                    $use_html_entities,
891
-                    $add_mobile_label
892
-                );
893
-
894
-            case 'DROPDOWN':
895
-                return EEH_Form_Fields::select(
896
-                    $display_text,
897
-                    $answer,
898
-                    $options,
899
-                    $input_name,
900
-                    $input_id,
901
-                    $input_class,
902
-                    $required,
903
-                    $required_text,
904
-                    $label_class,
905
-                    $disabled,
906
-                    $system_ID,
907
-                    $use_html_entities,
908
-                    true,
909
-                    $add_mobile_label
910
-                );
911
-
912
-
913
-            case 'RADIO_BTN':
914
-                return EEH_Form_Fields::radio(
915
-                    $display_text,
916
-                    $answer,
917
-                    $options,
918
-                    $input_name,
919
-                    $input_id,
920
-                    $input_class,
921
-                    $required,
922
-                    $required_text,
923
-                    $label_class,
924
-                    $disabled,
925
-                    $system_ID,
926
-                    $use_html_entities,
927
-                    $label_b4,
928
-                    $use_desc_4_label,
929
-                    $add_mobile_label
930
-                );
931
-
932
-            case 'CHECKBOX':
933
-                return EEH_Form_Fields::checkbox(
934
-                    $display_text,
935
-                    $answer,
936
-                    $options,
937
-                    $input_name,
938
-                    $input_id,
939
-                    $input_class,
940
-                    $required,
941
-                    $required_text,
942
-                    $label_class,
943
-                    $disabled,
944
-                    $label_b4,
945
-                    $system_ID,
946
-                    $use_html_entities,
947
-                    $add_mobile_label
948
-                );
949
-
950
-            case 'DATE':
951
-                return EEH_Form_Fields::datepicker(
952
-                    $display_text,
953
-                    $answer,
954
-                    $input_name,
955
-                    $input_id,
956
-                    $input_class,
957
-                    $required,
958
-                    $required_text,
959
-                    $label_class,
960
-                    $disabled,
961
-                    $system_ID,
962
-                    $use_html_entities,
963
-                    $add_mobile_label
964
-                );
965
-
966
-            case 'TEXT':
967
-            default:
968
-                return EEH_Form_Fields::text(
969
-                    $display_text,
970
-                    $answer,
971
-                    $input_name,
972
-                    $input_id,
973
-                    $input_class,
974
-                    $required,
975
-                    $required_text,
976
-                    $label_class,
977
-                    $disabled,
978
-                    $system_ID,
979
-                    $use_html_entities,
980
-                    $add_mobile_label
981
-                );
982
-        }
983
-    }
984
-
985
-
986
-    public static function label(
987
-        string $question,
988
-        string $required_text = '',
989
-        string $required_label = '',
990
-        string $name = '',
991
-        string $label_class = '',
992
-        bool $filter = true
993
-    ): string {
994
-        $for   = ! empty($name) ? " for='{$name}'" : '';
995
-        $class = ! empty($label_class) ? " class='{$label_class}'" : '';
996
-        $label = self::prep_question($question) . $required_label;
997
-        $label_html = "
491
+	}
492
+
493
+
494
+	/**
495
+	 * @param string $class
496
+	 * @param string $id
497
+	 * @param string $name
498
+	 * @param int    $rows
499
+	 * @param int    $tab_index
500
+	 * @param string $value
501
+	 * @param array  $wp_editor_args
502
+	 * @return false|string
503
+	 * @since   4.10.14.p
504
+	 */
505
+	private static function adminWpEditor($class, $id, $name, $rows, $tab_index, $value, $wp_editor_args = [])
506
+	{
507
+		$editor_settings = $wp_editor_args + [
508
+				'textarea_name' => esc_attr($name),
509
+				'textarea_rows' => absint($rows),
510
+				'editor_class'  => esc_attr($class),
511
+				'tabindex'      => absint($tab_index),
512
+			];
513
+		ob_start();
514
+		wp_editor($value, esc_attr($id), $editor_settings);
515
+		return ob_get_clean();
516
+	}
517
+
518
+
519
+	/**
520
+	 * espresso admin page select_input
521
+	 * Turns an array into a select fields
522
+	 *
523
+	 * @static
524
+	 * @access public
525
+	 * @param string  $name       field name
526
+	 * @param array   $values     option values, numbered array starting at 0, where each value is an array with a key
527
+	 *                            'text' (meaning text to display' and 'id' (meaning the internal value) eg:
528
+	 *                            array(1=>array('text'=>'Monday','id'=>1),2=>array('text'=>'Tuesday','id'=>2)...). or
529
+	 *                            as an array of key-value pairs, where the key is to be used for the select input's
530
+	 *                            name, and the value will be the text shown to the user.  Optionally you can also
531
+	 *                            include an additional key of "class" which will add a specific class to the option
532
+	 *                            for that value.
533
+	 * @param string  $default    default value
534
+	 * @param string  $parameters extra parameters
535
+	 * @param string  $class      css class
536
+	 * @param boolean $autosize   whether to autosize the select or not
537
+	 * @return string              html string for the select input
538
+	 */
539
+	public static function select_input(
540
+		$name,
541
+		$values,
542
+		$default = '',
543
+		$parameters = '',
544
+		$class = '',
545
+		$autosize = true
546
+	) {
547
+		// if $values was submitted in the wrong format, convert it over
548
+		if (! empty($values) && (! array_key_exists(0, $values) || ! is_array($values[0]))) {
549
+			$converted_values = [];
550
+			foreach ($values as $id => $text) {
551
+				$converted_values[] = ['id' => $id, 'text' => $text];
552
+			}
553
+			$values = $converted_values;
554
+		}
555
+
556
+		$field =
557
+			'<select id="' . EEH_Formatter::ee_tep_output_string($name)
558
+			. '" name="' . EEH_Formatter::ee_tep_output_string($name)
559
+			. '"';
560
+
561
+		if (EEH_Formatter::ee_tep_not_null($parameters)) {
562
+			$field .= ' ' . $parameters;
563
+		}
564
+		$class = $autosize ? self::appendInputSizeClass($class, $values) : '';
565
+
566
+		$field .= ' class="' . $class . '">';
567
+
568
+		if (empty($default) && isset($GLOBALS[ $name ])) {
569
+			$default = stripslashes($GLOBALS[ $name ]);
570
+		}
571
+
572
+		$field .= self::selectInputOption($values, $default);
573
+		$field .= '</select>';
574
+
575
+		return $field;
576
+	}
577
+
578
+
579
+	private static function selectInputOption(array $values, $default): string
580
+	{
581
+		if (isset($values['id'], $values['text'])) {
582
+			$id = is_scalar($values['id']) ? $values['id'] : '';
583
+			$text = is_scalar($values['text']) ? $values['text'] : '';
584
+			$selected = $default == $values['id'] ? ' selected = "selected"' : '';
585
+			$html_class = isset($values['class']) ? ' class="' . $values['class'] . '"' : '';
586
+			return "<option value='{$id}'{$selected}{$html_class}>{$text}</option>";
587
+		}
588
+		$options = '';
589
+		foreach ($values as $value) {
590
+			$options .= self::selectInputOption($value, $default);
591
+		}
592
+		return $options;
593
+	}
594
+
595
+
596
+	/**
597
+	 * @param mixed $value
598
+	 * @return int
599
+	 * @since   5.0.0.p
600
+	 */
601
+	private static function getInputValueLength($value): int
602
+	{
603
+		if ($value instanceof EE_Question_Option) {
604
+			return self::getInputValueLength($value->desc());
605
+		}
606
+		if (is_array($value)) {
607
+			$chars = 0;
608
+			foreach ($value as $val) {
609
+				$length = self::getInputValueLength($val);
610
+				$chars = max($length, $chars);
611
+			}
612
+			return $chars;
613
+		}
614
+		// not a primitive? return something big
615
+		if (! is_scalar($value)) {
616
+			return 500;
617
+		}
618
+		return strlen((string) $value);
619
+	}
620
+
621
+
622
+	/**
623
+	 * @param string $class
624
+	 * @param mixed $value
625
+	 * @return string
626
+	 * @since   5.0.0.p
627
+	 */
628
+	private static function appendInputSizeClass(string $class, $value): string
629
+	{
630
+		if (strpos($class, 'ee-input-width--') !== false) {
631
+			return $class;
632
+		}
633
+		$chars = self::getInputValueLength($value);
634
+		if ($chars && $chars < 5) {
635
+			return "{$class} ee-input-width--tiny";
636
+		}
637
+		if ($chars && $chars < 25) {
638
+			return "{$class} ee-input-width--small";
639
+		}
640
+		if ($chars && $chars > 100) {
641
+			return "{$class} ee-input-width--big";
642
+		}
643
+		return "{$class} ee-input-width--reg";
644
+	}
645
+
646
+
647
+	/**
648
+	 * generate_question_groups_html
649
+	 *
650
+	 * @param array  $question_groups
651
+	 * @param string $group_wrapper
652
+	 * @return string HTML
653
+	 * @throws EE_Error
654
+	 * @throws ReflectionException
655
+	 */
656
+	public static function generate_question_groups_html($question_groups = [], $group_wrapper = 'fieldset')
657
+	{
658
+
659
+		$html                            = '';
660
+		$before_question_group_questions =
661
+			apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions', '');
662
+		$after_question_group_questions  =
663
+			apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions', '');
664
+
665
+		if (! empty($question_groups)) {
666
+			// loop thru question groups
667
+			foreach ($question_groups as $QSG) {
668
+				// check that questions exist
669
+				if (! empty($QSG['QSG_questions'])) {
670
+					// use fieldsets
671
+					$html .= "\n\t"
672
+							 . '<'
673
+							 . $group_wrapper
674
+							 . ' class="espresso-question-group-wrap" id="'
675
+							 . $QSG['QSG_identifier']
676
+							 . '">';
677
+					// group_name
678
+					$html .= $QSG['QSG_show_group_name']
679
+						? "\n\t\t"
680
+						  . '<h5 class="espresso-question-group-title-h5 section-title">'
681
+						  . self::prep_answer($QSG['QSG_name'])
682
+						  . '</h5>'
683
+						: '';
684
+					// group_desc
685
+					$html .= $QSG['QSG_show_group_desc'] && ! empty($QSG['QSG_desc'])
686
+						? '<div class="espresso-question-group-desc-pg">'
687
+						  . self::prep_answer($QSG['QSG_desc'])
688
+						  . '</div>'
689
+						: '';
690
+
691
+					$html .= $before_question_group_questions;
692
+					// loop thru questions
693
+					foreach ($QSG['QSG_questions'] as $question) {
694
+						$QFI  = new EE_Question_Form_Input(
695
+							$question['qst_obj'],
696
+							$question['ans_obj'],
697
+							$question
698
+						);
699
+						$html .= self::generate_form_input($QFI);
700
+					}
701
+					$html .= $after_question_group_questions;
702
+					$html .= "\n\t" . '</' . $group_wrapper . '>';
703
+				}
704
+			}
705
+		}
706
+
707
+		return $html;
708
+	}
709
+
710
+
711
+	/**
712
+	 * generate_question_groups_html
713
+	 *
714
+	 * @param array  $question_groups
715
+	 * @param array  $q_meta
716
+	 * @param bool   $from_admin
717
+	 * @param string $group_wrapper
718
+	 * @return string HTML
719
+	 * @throws EE_Error
720
+	 * @throws ReflectionException
721
+	 */
722
+	public static function generate_question_groups_html2(
723
+		$question_groups = [],
724
+		$q_meta = [],
725
+		$from_admin = false,
726
+		$group_wrapper = 'fieldset'
727
+	) {
728
+
729
+		$html                            = '';
730
+		$before_question_group_questions =
731
+			apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions', '');
732
+		$after_question_group_questions  =
733
+			apply_filters('FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions', '');
734
+
735
+		$default_q_meta = [
736
+			'att_nmbr'    => 1,
737
+			'ticket_id'   => '',
738
+			'input_name'  => '',
739
+			'input_id'    => '',
740
+			'input_class' => '',
741
+		];
742
+		$q_meta         = array_merge($default_q_meta, $q_meta);
743
+
744
+		if (! empty($question_groups)) {
745
+			// loop thru question groups
746
+			foreach ($question_groups as $QSG) {
747
+				if ($QSG instanceof EE_Question_Group) {
748
+					// check that questions exist
749
+
750
+					$where = ['QST_deleted' => 0];
751
+					if (! $from_admin) {
752
+						$where['QST_admin_only'] = 0;
753
+					}
754
+					$questions =
755
+						$QSG->questions([$where, 'order_by' => ['Question_Group_Question.QGQ_order' => 'ASC']]);
756
+					if (! empty($questions)) {
757
+						// use fieldsets
758
+						$html .= "\n\t"
759
+								 . '<' . $group_wrapper . ' class="espresso-question-group-wrap" '
760
+								 . 'id="' . $QSG->get('QSG_identifier') . '">';
761
+						// group_name
762
+						if ($QSG->show_group_name()) {
763
+							$html .= "\n\t\t"
764
+									 . '<h5 class="espresso-question-group-title-h5 section-title">'
765
+									 . $QSG->get_pretty('QSG_name')
766
+									 . '</h5>';
767
+						}
768
+						// group_desc
769
+						if ($QSG->show_group_desc()) {
770
+							$html .= '<div class="espresso-question-group-desc-pg">'
771
+									 . $QSG->get_pretty('QSG_desc')
772
+									 . '</div>';
773
+						}
774
+
775
+						$html .= $before_question_group_questions;
776
+						// loop thru questions
777
+						foreach ($questions as $QST) {
778
+							$qstn_id = $QST->is_system_question() ? $QST->system_ID() : $QST->ID();
779
+
780
+							$answer = null;
781
+
782
+							/** @var RequestInterface $request */
783
+							$request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
784
+							$request_qstn = $request->getRequestParam('qstn', [], 'string', true);
785
+							if (! empty($request_qstn) && isset($q_meta['input_id']) && isset($q_meta['att_nmbr'])) {
786
+								// check for answer in $request_qstn in case we are reprocessing a form after an error
787
+								if (isset($request_qstn[ $q_meta['input_id'] ][ $qstn_id ])) {
788
+									$answer = is_array($request_qstn[ $q_meta['input_id'] ][ $qstn_id ])
789
+										? $request_qstn[ $q_meta['input_id'] ][ $qstn_id ]
790
+										: sanitize_text_field($request_qstn[ $q_meta['input_id'] ][ $qstn_id ]);
791
+								}
792
+							} elseif (isset($q_meta['attendee']) && $q_meta['attendee']) {
793
+								// attendee data from the session
794
+								$answer =
795
+									isset($q_meta['attendee'][ $qstn_id ]) ? $q_meta['attendee'][ $qstn_id ] : null;
796
+							}
797
+
798
+
799
+							$QFI  = new EE_Question_Form_Input(
800
+								$QST,
801
+								EE_Answer::new_instance(
802
+									[
803
+										'ANS_ID'    => 0,
804
+										'QST_ID'    => 0,
805
+										'REG_ID'    => 0,
806
+										'ANS_value' => $answer,
807
+									]
808
+								),
809
+								$q_meta
810
+							);
811
+							$html .= self::generate_form_input($QFI);
812
+						}
813
+						$html .= $after_question_group_questions;
814
+						$html .= "\n\t" . '</' . $group_wrapper . '>';
815
+					}
816
+				}
817
+			}
818
+		}
819
+		return $html;
820
+	}
821
+
822
+
823
+	/**
824
+	 * generate_form_input
825
+	 *
826
+	 * @param EE_Question_Form_Input $QFI
827
+	 * @return string HTML
828
+	 * @throws EE_Error
829
+	 * @throws ReflectionException
830
+	 */
831
+	public static function generate_form_input(EE_Question_Form_Input $QFI)
832
+	{
833
+		if (isset($QFI->QST_admin_only) && $QFI->QST_admin_only && ! is_admin()) {
834
+			return '';
835
+		}
836
+		/** @var RequestInterface $request */
837
+		$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
838
+
839
+		$QFI = self::_load_system_dropdowns($QFI);
840
+		$QFI = self::_load_specialized_dropdowns($QFI);
841
+
842
+		// we also need to verify
843
+
844
+		$display_text = (string) $QFI->get('QST_display_text');
845
+		$input_name   = (string) $QFI->get('QST_input_name');
846
+		$answer       = $request->getRequestParam($input_name, $QFI->get('ANS_value'));
847
+		$input_id     = (string) $QFI->get('QST_input_id');
848
+		$input_class  = (string) $QFI->get('QST_input_class');
849
+		//      $disabled = $QFI->get('QST_disabled') ? ' disabled="disabled"' : '';
850
+		$disabled          = (bool) $QFI->get('QST_disabled');
851
+		$required_label    = apply_filters(' FHEE__EEH_Form_Fields__generate_form_input__required_label', '<em>*</em>');
852
+		$QST_required      = (bool) $QFI->get('QST_required');
853
+		$required          = $QST_required
854
+			? ['label' => $required_label, 'class' => 'required needs-value', 'title' => $QST_required]
855
+			: [];
856
+		$use_html_entities = $QFI->get_meta('htmlentities');
857
+		$required_text     = (string) $QFI->get('QST_required_text') !== ''
858
+			? (string) $QFI->get('QST_required_text')
859
+			: esc_html__('This field is required', 'event_espresso');
860
+		$required_text     = $QST_required
861
+			? "\n\t\t\t"
862
+			  . '<div class="required-text hidden">'
863
+			  . self::prep_answer($required_text, $use_html_entities)
864
+			  . '</div>'
865
+			: '';
866
+		$label_class       = (string) $QFI->get('label_class');
867
+		$label_class       = $label_class ? "{$label_class} espresso-form-input-lbl" : 'espresso-form-input-lbl';
868
+		$QST_options       = $QFI->options(true, $answer);
869
+		$options           = is_array($QST_options) ? self::prep_answer_options($QST_options) : [];
870
+		$system_ID         = (string) $QFI->get('QST_system');
871
+		$label_b4          = (bool) $QFI->get_meta('label_b4');
872
+		$use_desc_4_label  = (bool) $QFI->get_meta('use_desc_4_label');
873
+		$add_mobile_label  = (bool) $QFI->get_meta('add_mobile_label');
874
+
875
+
876
+		switch ($QFI->get('QST_type')) {
877
+			case 'TEXTAREA':
878
+				return EEH_Form_Fields::textarea(
879
+					$display_text,
880
+					$answer,
881
+					$input_name,
882
+					$input_id,
883
+					$input_class,
884
+					[],
885
+					$required,
886
+					$required_text,
887
+					$label_class,
888
+					$disabled,
889
+					$system_ID,
890
+					$use_html_entities,
891
+					$add_mobile_label
892
+				);
893
+
894
+			case 'DROPDOWN':
895
+				return EEH_Form_Fields::select(
896
+					$display_text,
897
+					$answer,
898
+					$options,
899
+					$input_name,
900
+					$input_id,
901
+					$input_class,
902
+					$required,
903
+					$required_text,
904
+					$label_class,
905
+					$disabled,
906
+					$system_ID,
907
+					$use_html_entities,
908
+					true,
909
+					$add_mobile_label
910
+				);
911
+
912
+
913
+			case 'RADIO_BTN':
914
+				return EEH_Form_Fields::radio(
915
+					$display_text,
916
+					$answer,
917
+					$options,
918
+					$input_name,
919
+					$input_id,
920
+					$input_class,
921
+					$required,
922
+					$required_text,
923
+					$label_class,
924
+					$disabled,
925
+					$system_ID,
926
+					$use_html_entities,
927
+					$label_b4,
928
+					$use_desc_4_label,
929
+					$add_mobile_label
930
+				);
931
+
932
+			case 'CHECKBOX':
933
+				return EEH_Form_Fields::checkbox(
934
+					$display_text,
935
+					$answer,
936
+					$options,
937
+					$input_name,
938
+					$input_id,
939
+					$input_class,
940
+					$required,
941
+					$required_text,
942
+					$label_class,
943
+					$disabled,
944
+					$label_b4,
945
+					$system_ID,
946
+					$use_html_entities,
947
+					$add_mobile_label
948
+				);
949
+
950
+			case 'DATE':
951
+				return EEH_Form_Fields::datepicker(
952
+					$display_text,
953
+					$answer,
954
+					$input_name,
955
+					$input_id,
956
+					$input_class,
957
+					$required,
958
+					$required_text,
959
+					$label_class,
960
+					$disabled,
961
+					$system_ID,
962
+					$use_html_entities,
963
+					$add_mobile_label
964
+				);
965
+
966
+			case 'TEXT':
967
+			default:
968
+				return EEH_Form_Fields::text(
969
+					$display_text,
970
+					$answer,
971
+					$input_name,
972
+					$input_id,
973
+					$input_class,
974
+					$required,
975
+					$required_text,
976
+					$label_class,
977
+					$disabled,
978
+					$system_ID,
979
+					$use_html_entities,
980
+					$add_mobile_label
981
+				);
982
+		}
983
+	}
984
+
985
+
986
+	public static function label(
987
+		string $question,
988
+		string $required_text = '',
989
+		string $required_label = '',
990
+		string $name = '',
991
+		string $label_class = '',
992
+		bool $filter = true
993
+	): string {
994
+		$for   = ! empty($name) ? " for='{$name}'" : '';
995
+		$class = ! empty($label_class) ? " class='{$label_class}'" : '';
996
+		$label = self::prep_question($question) . $required_label;
997
+		$label_html = "
998 998
             {$required_text}
999 999
             <label{$for}{$class}>{$label}</label>";
1000
-        // filter label but ensure required text comes before it
1001
-        return $filter
1002
-            ? apply_filters('FHEE__EEH_Form_Fields__label_html', $label_html, $required_text)
1003
-            : $label_html;
1004
-    }
1005
-
1006
-
1007
-
1008
-    public static function mobileLabel(
1009
-        bool $add_mobile_label,
1010
-        string $question,
1011
-        string $required_text = '',
1012
-        string $required_label = '',
1013
-        string $label_class = '',
1014
-        string $name = ''
1015
-    ): string {
1016
-        return $add_mobile_label
1017
-            ? self::label($question, $required_text, $required_label, $name, $label_class, false)
1018
-            : '';
1019
-    }
1020
-
1021
-
1022
-    /**
1023
-     * generates HTML for a form text input
1024
-     *
1025
-     * @param string $question    label content
1026
-     * @param string $answer      form input value attribute
1027
-     * @param string $name        form input name attribute
1028
-     * @param string $id          form input css id attribute
1029
-     * @param string $class       form input css class attribute
1030
-     * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1031
-     *                            required 'class', and required 'msg' attribute
1032
-     * @param string $label_class css class attribute for the label
1033
-     * @param string $disabled    disabled="disabled" or null
1034
-     * @return string HTML
1035
-     */
1036
-    public static function text(
1037
-        $question = false,
1038
-        $answer = null,
1039
-        $name = false,
1040
-        $id = '',
1041
-        $class = '',
1042
-        $required = false,
1043
-        $required_text = '',
1044
-        $label_class = '',
1045
-        $disabled = false,
1046
-        $system_ID = false,
1047
-        $use_html_entities = true,
1048
-        $add_mobile_label = false,
1049
-        $extra_attributes = ''
1050
-    ) {
1051
-        // need these
1052
-        if (! $question || ! $name) {
1053
-            return null;
1054
-        }
1055
-        // prep the answer
1056
-        $answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1057
-        // prep the required array
1058
-        $required = self::prep_required($required);
1059
-        // set disabled tag
1060
-        $disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1061
-        // ya gots ta have style man!!!
1062
-        $txt_class = is_admin() ? 'regular-text' : 'espresso-text-inp';
1063
-        $class     = empty($class) ? $txt_class : $class;
1064
-        $class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1065
-        $class = self::appendInputSizeClass($class, $answer);
1066
-        $class .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1067
-        $extra_attributes = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', $extra_attributes);
1068
-
1069
-        $label_html = self::label($question, $required_text, $required['label'], $name, $label_class);
1070
-        $mobile_label = self::mobileLabel(
1071
-            $add_mobile_label,
1072
-            $question,
1073
-            $required_text,
1074
-            $required['label'],
1075
-            $label_class,
1076
-            $name
1077
-        );
1078
-
1079
-        $input_html = $mobile_label . '
1000
+		// filter label but ensure required text comes before it
1001
+		return $filter
1002
+			? apply_filters('FHEE__EEH_Form_Fields__label_html', $label_html, $required_text)
1003
+			: $label_html;
1004
+	}
1005
+
1006
+
1007
+
1008
+	public static function mobileLabel(
1009
+		bool $add_mobile_label,
1010
+		string $question,
1011
+		string $required_text = '',
1012
+		string $required_label = '',
1013
+		string $label_class = '',
1014
+		string $name = ''
1015
+	): string {
1016
+		return $add_mobile_label
1017
+			? self::label($question, $required_text, $required_label, $name, $label_class, false)
1018
+			: '';
1019
+	}
1020
+
1021
+
1022
+	/**
1023
+	 * generates HTML for a form text input
1024
+	 *
1025
+	 * @param string $question    label content
1026
+	 * @param string $answer      form input value attribute
1027
+	 * @param string $name        form input name attribute
1028
+	 * @param string $id          form input css id attribute
1029
+	 * @param string $class       form input css class attribute
1030
+	 * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1031
+	 *                            required 'class', and required 'msg' attribute
1032
+	 * @param string $label_class css class attribute for the label
1033
+	 * @param string $disabled    disabled="disabled" or null
1034
+	 * @return string HTML
1035
+	 */
1036
+	public static function text(
1037
+		$question = false,
1038
+		$answer = null,
1039
+		$name = false,
1040
+		$id = '',
1041
+		$class = '',
1042
+		$required = false,
1043
+		$required_text = '',
1044
+		$label_class = '',
1045
+		$disabled = false,
1046
+		$system_ID = false,
1047
+		$use_html_entities = true,
1048
+		$add_mobile_label = false,
1049
+		$extra_attributes = ''
1050
+	) {
1051
+		// need these
1052
+		if (! $question || ! $name) {
1053
+			return null;
1054
+		}
1055
+		// prep the answer
1056
+		$answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1057
+		// prep the required array
1058
+		$required = self::prep_required($required);
1059
+		// set disabled tag
1060
+		$disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1061
+		// ya gots ta have style man!!!
1062
+		$txt_class = is_admin() ? 'regular-text' : 'espresso-text-inp';
1063
+		$class     = empty($class) ? $txt_class : $class;
1064
+		$class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1065
+		$class = self::appendInputSizeClass($class, $answer);
1066
+		$class .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1067
+		$extra_attributes = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', $extra_attributes);
1068
+
1069
+		$label_html = self::label($question, $required_text, $required['label'], $name, $label_class);
1070
+		$mobile_label = self::mobileLabel(
1071
+			$add_mobile_label,
1072
+			$question,
1073
+			$required_text,
1074
+			$required['label'],
1075
+			$label_class,
1076
+			$name
1077
+		);
1078
+
1079
+		$input_html = $mobile_label . '
1080 1080
             <input  type="text"
1081 1081
                     name="' . $name . '"
1082 1082
                     id="' . $id . '"
@@ -1086,1035 +1086,1035 @@  discard block
 block discarded – undo
1086 1086
                     ' . $disabled . ' ' . $extra_attributes . '
1087 1087
             />';
1088 1088
 
1089
-        $input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1090
-        return $label_html . $input_html;
1091
-    }
1092
-
1093
-
1094
-    /**
1095
-     * generates HTML for a form textarea
1096
-     *
1097
-     * @param string $question    label content
1098
-     * @param string $answer      form input value attribute
1099
-     * @param string $name        form input name attribute
1100
-     * @param string $id          form input css id attribute
1101
-     * @param string $class       form input css class attribute
1102
-     * @param array  $dimensions  array of form input rows and cols attributes : array( 'rows' => 3, 'cols' => 40 )
1103
-     * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1104
-     *                            required 'class', and required 'msg' attribute
1105
-     * @param string $label_class css class attribute for the label
1106
-     * @param string $disabled    disabled="disabled" or null
1107
-     * @return string HTML
1108
-     */
1109
-    public static function textarea(
1110
-        $question = false,
1111
-        $answer = null,
1112
-        $name = false,
1113
-        $id = '',
1114
-        $class = '',
1115
-        $dimensions = false,
1116
-        $required = false,
1117
-        $required_text = '',
1118
-        $label_class = '',
1119
-        $disabled = false,
1120
-        $system_ID = false,
1121
-        $use_html_entities = true,
1122
-        $add_mobile_label = false
1123
-    ) {
1124
-        // need these
1125
-        if (! $question || ! $name) {
1126
-            return null;
1127
-        }
1128
-        // prep the answer
1129
-        $answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1130
-        // prep the required array
1131
-        $required = self::prep_required($required);
1132
-        // make sure $dimensions is an array
1133
-        $dimensions = is_array($dimensions) ? $dimensions : [];
1134
-        // and set some defaults
1135
-        $dimensions = array_merge(['rows' => 3, 'cols' => 40], $dimensions);
1136
-        // set disabled tag
1137
-        $disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1138
-        // ya gots ta have style man!!!
1139
-        $class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1140
-        $class     .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1141
-        $extra     = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1142
-
1143
-        $label_html   = self::label($question, $required_text, $required['label'], $name, $label_class);
1144
-        $mobile_label = self::mobileLabel(
1145
-            $add_mobile_label,
1146
-            $question,
1147
-            $required_text,
1148
-            $required['label'],
1149
-            $label_class,
1150
-            $name
1151
-        );
1152
-
1153
-        $input_html = $mobile_label
1154
-            . '<textarea name="' . $name . '" id="' . $id . '" class="' . trim($class) . '" '
1155
-            . 'rows="' . $dimensions['rows'] . '" cols="' . $dimensions['cols'] . '"  '
1156
-            . 'aria-label="' . $required['msg'] . '" ' . $disabled . ' ' . $extra . '>'
1157
-             . esc_textarea($answer)
1158
-              . '</textarea>';
1159
-
1160
-        $input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1161
-        return $label_html . $input_html;
1162
-    }
1163
-
1164
-
1165
-    /**
1166
-     * generates HTML for a form select input
1167
-     *
1168
-     * @param string $question    label content
1169
-     * @param string $answer      form input value attribute
1170
-     * @param array  $options     array of answer options where array key = option value and array value = option
1171
-     *                            display text
1172
-     * @param string $name        form input name attribute
1173
-     * @param string $id          form input css id attribute
1174
-     * @param string $class       form input css class attribute
1175
-     * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1176
-     *                            required 'class', and required 'msg' attribute
1177
-     * @param string $label_class css class attribute for the label
1178
-     * @param string $disabled    disabled="disabled" or null
1179
-     * @return string HTML
1180
-     */
1181
-    public static function select(
1182
-        $question = false,
1183
-        $answer = null,
1184
-        $options = false,
1185
-        $name = false,
1186
-        $id = '',
1187
-        $class = '',
1188
-        $required = false,
1189
-        $required_text = '',
1190
-        $label_class = '',
1191
-        $disabled = false,
1192
-        $system_ID = false,
1193
-        $use_html_entities = true,
1194
-        $add_please_select_option = false,
1195
-        $add_mobile_label = false
1196
-    ) {
1197
-
1198
-        // need these
1199
-        if (! $question || ! $name || ! $options || empty($options) || ! is_array($options)) {
1200
-            return null;
1201
-        }
1202
-        // prep the answer
1203
-        $answer = is_array($answer)
1204
-            ? self::prep_answer(array_shift($answer), $use_html_entities)
1205
-            : self::prep_answer($answer, $use_html_entities);
1206
-        // prep the required array
1207
-        $required = self::prep_required($required);
1208
-        // set disabled tag
1209
-        $disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1210
-        // ya gots ta have style man!!!
1211
-        $class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1212
-        $class = self::appendInputSizeClass($class, $options);
1213
-        $extra     = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1214
-
1215
-        $label_html   = self::label($question, $required_text, $required['label'], $name, $label_class);
1216
-        $mobile_label = self::mobileLabel(
1217
-            $add_mobile_label,
1218
-            $question,
1219
-            $required_text,
1220
-            $required['label'],
1221
-            $label_class,
1222
-            $name
1223
-        );
1224
-
1225
-        $input_html = $mobile_label
1226
-            . '<select name="' . $name . '" id="' . $id . '" class="' . trim($class) . ' ' . $required['class'] . '" '
1227
-            . 'aria-label="' . esc_attr($required['msg']) . '"' . $disabled . ' ' . $extra . '>';
1228
-        // recursively count array elements, to determine total number of options
1229
-        $only_option = count($options, 1) == 1;
1230
-        if (! $only_option) {
1231
-            // if there is NO answer set and there are multiple options to choose from, then set the "please select" message as selected
1232
-            $selected   = $answer === null ? ' selected' : '';
1233
-            $input_html .= $add_please_select_option
1234
-                ? "\n\t\t\t\t"
1235
-                  . '<option value=""' . $selected . '>'
1236
-                  . esc_html__(' - please select - ', 'event_espresso')
1237
-                  . '</option>'
1238
-                : '';
1239
-        }
1240
-        foreach ($options as $key => $value) {
1241
-            // if value is an array, then create option groups, else create regular ol' options
1242
-            $input_html .= is_array($value)
1243
-                ? self::_generate_select_option_group(
1244
-                    $key,
1245
-                    $value,
1246
-                    $answer,
1247
-                    $use_html_entities
1248
-                )
1249
-                : self::_generate_select_option(
1250
-                    $value->value(),
1251
-                    $value->desc(),
1252
-                    $answer,
1253
-                    $only_option,
1254
-                    $use_html_entities
1255
-                );
1256
-        }
1257
-
1258
-        $input_html .= "\n\t\t\t" . '</select>';
1259
-
1260
-        $input_html =
1261
-            apply_filters(
1262
-                'FHEE__EEH_Form_Fields__select__before_end_wrapper',
1263
-                $input_html,
1264
-                $question,
1265
-                $answer,
1266
-                $name,
1267
-                $id,
1268
-                $class,
1269
-                $system_ID
1270
-            );
1271
-
1272
-        $input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1273
-        return $label_html . $input_html;
1274
-    }
1275
-
1276
-
1277
-    /**
1278
-     *  _generate_select_option_group
1279
-     *
1280
-     *  if  $value for a select box is an array, then the key will be used as the optgroup label
1281
-     *  and the value array will be looped thru and the elements sent to _generate_select_option
1282
-     *
1283
-     * @param mixed   $opt_group
1284
-     * @param mixed   $QSOs
1285
-     * @param mixed   $answer
1286
-     * @param boolean $use_html_entities
1287
-     * @return string
1288
-     */
1289
-    private static function _generate_select_option_group($opt_group, $QSOs, $answer, $use_html_entities = true)
1290
-    {
1291
-        $html = "\n\t\t\t\t" . '<optgroup label="' . self::prep_option_value($opt_group) . '">';
1292
-        foreach ($QSOs as $QSO) {
1293
-            $html .= self::_generate_select_option($QSO->value(), $QSO->desc(), $answer, false, $use_html_entities);
1294
-        }
1295
-        $html .= "\n\t\t\t\t" . '</optgroup>';
1296
-        return $html;
1297
-    }
1298
-
1299
-
1300
-    /**
1301
-     *  _generate_select_option
1302
-     *
1303
-     * @param mixed   $key
1304
-     * @param mixed   $value
1305
-     * @param mixed   $answer
1306
-     * @param int     $only_option
1307
-     * @param boolean $use_html_entities
1308
-     * @return string
1309
-     */
1310
-    private static function _generate_select_option(
1311
-        $key,
1312
-        $value,
1313
-        $answer,
1314
-        $only_option = false,
1315
-        $use_html_entities = true
1316
-    ) {
1317
-        $key      = self::prep_answer($key, $use_html_entities);
1318
-        $value    = self::prep_answer($value, $use_html_entities);
1319
-        $value    = ! empty($value) ? $value : $key;
1320
-        $selected = ($answer == $key || $only_option) ? 'selected' : '';
1321
-        return "\n\t\t\t\t"
1322
-               . '<option value="' . self::prep_option_value($key) . '" ' . $selected . '> '
1323
-               . $value
1324
-               . '&nbsp;&nbsp;&nbsp;</option>';
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     * generates HTML for form radio button inputs
1330
-     *
1331
-     * @param string      $question    label content
1332
-     * @param string|int  $answer      form input value attribute
1333
-     * @param array|bool  $options     array of answer options where array key = option value and array value = option
1334
-     *                                 display text
1335
-     * @param string $name        form input name attribute
1336
-     * @param string      $id          form input css id attribute
1337
-     * @param string      $class       form input css class attribute
1338
-     * @param array|bool  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1339
-     *                                 required 'class', and required 'msg' attribute
1340
-     * @param string      $required_text
1341
-     * @param string      $label_class css class attribute for the label
1342
-     * @param bool|string $disabled    disabled="disabled" or null
1343
-     * @param string      $system_ID
1344
-     * @param bool        $use_html_entities
1345
-     * @param bool        $label_b4
1346
-     * @param bool        $use_desc_4_label
1347
-     * @return string HTML
1348
-     */
1349
-    public static function radio(
1350
-        string $question = '',
1351
-        $answer = null,
1352
-        $options = false,
1353
-        string $name = '',
1354
-        string $id = '',
1355
-        string $class = '',
1356
-        $required = false,
1357
-        string $required_text = '',
1358
-        string $label_class = '',
1359
-        bool $disabled = false,
1360
-        string $system_ID = '',
1361
-        bool $use_html_entities = true,
1362
-        bool $label_b4 = false,
1363
-        bool $use_desc_4_label = false,
1364
-        bool $add_mobile_label = false
1365
-    ) {
1366
-        // need these
1367
-        if (! $question || ! $name || ! $options || empty($options) || ! is_array($options)) {
1368
-            return null;
1369
-        }
1370
-        // prep the answer
1371
-        $answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1372
-        // prep the required array
1373
-        $required = self::prep_required($required);
1374
-        // set disabled tag
1375
-        $disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1376
-        // ya gots ta have style man!!!
1377
-        $radio_class = is_admin() ? 'ee-admin-radio-lbl' : $label_class;
1378
-        $class       = ! empty($class) ? $class : 'espresso-radio-btn-inp';
1379
-        $extra       = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1380
-
1381
-        $label_html = self::label($question, $required_text, $required['label'], '', $label_class);
1382
-        $mobile_label = self::mobileLabel(
1383
-            $add_mobile_label,
1384
-            $question,
1385
-            $required_text,
1386
-            $required['label'],
1387
-            $label_class
1388
-        );
1389
-
1390
-        $input_html = $mobile_label
1391
-            . '<ul id="' . $id . '-ul" class="espresso-radio-btn-options-ul ' . $class . '-ul">';
1392
-
1393
-        $class .= ! empty($system_ID) ? ' ' . $system_ID : '';
1394
-        $class .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1395
-
1396
-        foreach ($options as $OPT) {
1397
-            if ($OPT instanceof EE_Question_Option) {
1398
-                $value   = self::prep_option_value($OPT->value());
1399
-                $label   = $use_desc_4_label ? $OPT->desc() : $OPT->value();
1400
-                $size    = $use_desc_4_label
1401
-                    ? self::get_label_size_class($OPT->value() . ' ' . $OPT->desc())
1402
-                    : self::get_label_size_class($OPT->value());
1403
-                $desc    = $OPT->desc();// no self::prep_answer
1404
-                $answer  = is_numeric($value) && empty($answer) ? 0 : $answer;
1405
-                $value  = is_numeric($answer) && empty($value) ? 0 : $value;
1406
-                $checked = (string) $value == (string) $answer ? ' checked' : '';
1407
-                $opt     = '-' . sanitize_key($value);
1408
-
1409
-                $input_html .= "\n\t\t\t\t" . '<li' . $size . '>';
1410
-                $input_html .= "\n\t\t\t\t\t" . '<label class="' . $radio_class . ' espresso-radio-btn-lbl">';
1411
-                $input_html .= $label_b4 ? "\n\t\t\t\t\t\t" . '<span>' . $label . '</span>&nbsp;' : '';
1412
-                $input_html .= "\n\t\t\t\t\t\t"
1413
-                               . '<input type="radio" name="' . $name . '" id="' . $id . $opt . '" '
1414
-                               . 'class="' . $class . '" value="' . $value . '" '
1415
-                               . 'aria-label="' . esc_attr($required['msg']) . '" ' . $disabled
1416
-                               . $checked . ' ' . $extra . '/>';
1417
-                $input_html .= ! $label_b4
1418
-                    ? "\n\t\t\t\t\t\t"
1419
-                      . '&nbsp;<span class="espresso-radio-btn-desc">'
1420
-                      . $label
1421
-                      . '</span>'
1422
-                    : '';
1423
-                $input_html .= "\n\t\t\t\t\t" . '</label>';
1424
-                $input_html .= $use_desc_4_label
1425
-                    ? ''
1426
-                    : '<span class="espresso-radio-btn-option-desc small-text grey-text">' . $desc . '</span>';
1427
-                $input_html .= "\n\t\t\t\t" . '</li>';
1428
-            }
1429
-        }
1430
-
1431
-        $input_html .= "\n\t\t\t" . '</ul>';
1432
-
1433
-        $input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1434
-        return $label_html . $input_html;
1435
-    }
1436
-
1437
-
1438
-    /**
1439
-     * generates HTML for form checkbox inputs
1440
-     *
1441
-     * @param string $question    label content
1442
-     * @param string $answer      form input value attribute
1443
-     * @param array  $options     array of options where array key = option value and array value = option display text
1444
-     * @param string $name        form input name attribute
1445
-     * @param string $id          form input css id attribute
1446
-     * @param string $class       form input css class attribute
1447
-     * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1448
-     *                            required 'class', and required 'msg' attribute
1449
-     * @param string $label_class css class attribute for the label
1450
-     * @param string $disabled    disabled="disabled" or null
1451
-     * @return string HTML
1452
-     */
1453
-    public static function checkbox(
1454
-        $question = false,
1455
-        $answer = null,
1456
-        $options = false,
1457
-        $name = false,
1458
-        $id = '',
1459
-        $class = '',
1460
-        $required = false,
1461
-        $required_text = '',
1462
-        $label_class = '',
1463
-        $disabled = false,
1464
-        $label_b4 = false,
1465
-        $system_ID = false,
1466
-        $use_html_entities = true,
1467
-        $add_mobile_label = false
1468
-    ) {
1469
-        // need these
1470
-        if (! $question || ! $name || ! $options || empty($options) || ! is_array($options)) {
1471
-            return null;
1472
-        }
1473
-        $answer = maybe_unserialize($answer);
1474
-
1475
-        // prep the answer(s)
1476
-        $answer = is_array($answer) ? $answer : [sanitize_key($answer) => $answer];
1477
-
1478
-        foreach ($answer as $key => $value) {
1479
-            $key            = self::prep_option_value($key);
1480
-            $answer[ $key ] = self::prep_answer($value, $use_html_entities);
1481
-        }
1482
-
1483
-        // prep the required array
1484
-        $required = self::prep_required($required);
1485
-        // set disabled tag
1486
-        $disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1487
-        // ya gots ta have style man!!!
1488
-        $radio_class = is_admin() ? 'ee-admin-radio-lbl' : $label_class;
1489
-        $class       = empty($class) ? 'espresso-radio-btn-inp' : $class;
1490
-        $extra       = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1491
-
1492
-        $label_html   = self::label($question, $required_text, $required['label'], '', $label_class);
1493
-        $mobile_label = self::mobileLabel(
1494
-            $add_mobile_label,
1495
-            $question,
1496
-            $required_text,
1497
-            $required['label'],
1498
-            $label_class
1499
-        );
1500
-
1501
-        $input_html = $mobile_label
1502
-            . '<ul id="' . $id . '-ul" class="espresso-checkbox-options-ul ' . $class . '-ul">';
1503
-
1504
-        $class .= ! empty($system_ID) ? ' ' . $system_ID : '';
1505
-        $class .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1506
-
1507
-        foreach ($options as $OPT) {
1508
-            $value = $OPT->value();// self::prep_option_value( $OPT->value() );
1509
-            $size  = self::get_label_size_class($OPT->value() . ' ' . $OPT->desc());
1510
-            $text  = self::prep_answer($OPT->value());
1511
-            $desc  = $OPT->desc();
1512
-            $opt   = '-' . sanitize_key($value);
1513
-
1514
-            $checked = is_array($answer) && in_array($text, $answer) ? ' checked' : '';
1515
-
1516
-            $input_html .= "\n\t\t\t\t" . '<li' . $size . '>';
1517
-            $input_html .= "\n\t\t\t\t\t" . '<label class="' . $radio_class . ' espresso-checkbox-lbl">';
1518
-            $input_html .= $label_b4 ? "\n\t\t\t\t\t\t" . '<span>' . $text . '</span>&nbsp;' : '';
1519
-            $input_html .= "\n\t\t\t\t\t\t"
1520
-                           . '<input type="checkbox" name="' . $name . '[' . $OPT->ID() . ']" '
1521
-                           . 'id="' . $id . $opt . '" class="' . $class . '" value="' . $value . '" '
1522
-                           . 'aria-label="' . esc_attr($required['msg']) . '" ' . $disabled . $checked . ' ' . $extra . '/>';
1523
-            $input_html .= ! $label_b4 ? "\n\t\t\t\t\t\t" . '&nbsp;<span>' . $text . '</span>' : '';
1524
-            $input_html .= "\n\t\t\t\t\t" . '</label>';
1525
-            if (! empty($desc) && $desc != $text) {
1526
-                $input_html .= "\n\t\t\t\t\t"
1527
-                               . ' &nbsp; <br/><div class="espresso-checkbox-option-desc small-text grey-text">'
1528
-                               . $desc
1529
-                               . '</div>';
1530
-            }
1531
-            $input_html .= "\n\t\t\t\t" . '</li>';
1532
-        }
1533
-
1534
-        $input_html .= "\n\t\t\t" . '</ul>';
1535
-
1536
-        $input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1537
-        return $label_html . $input_html;
1538
-    }
1539
-
1540
-
1541
-    /**
1542
-     * generates HTML for a form datepicker input
1543
-     *
1544
-     * @param string $question    label content
1545
-     * @param string $answer      form input value attribute
1546
-     * @param string $name        form input name attribute
1547
-     * @param string $id          form input css id attribute
1548
-     * @param string $class       form input css class attribute
1549
-     * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1550
-     *                            required 'class', and required 'msg' attribute
1551
-     * @param string $label_class css class attribute for the label
1552
-     * @param string $disabled    disabled="disabled" or null
1553
-     * @return string HTML
1554
-     */
1555
-    public static function datepicker(
1556
-        $question = false,
1557
-        $answer = null,
1558
-        $name = false,
1559
-        $id = '',
1560
-        $class = '',
1561
-        $required = false,
1562
-        $required_text = '',
1563
-        $label_class = '',
1564
-        $disabled = false,
1565
-        $system_ID = false,
1566
-        $use_html_entities = true,
1567
-        $add_mobile_label = false
1568
-    ) {
1569
-        // need these
1570
-        if (! $question || ! $name) {
1571
-            return null;
1572
-        }
1573
-        // prep the answer
1574
-        $answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1575
-        // prep the required array
1576
-        $required = self::prep_required($required);
1577
-        // set disabled tag
1578
-        $disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1579
-        // ya gots ta have style man!!!
1580
-        $txt_class = is_admin() ? 'regular-text' : 'espresso-datepicker-inp';
1581
-        $class     = empty($class) ? $txt_class : $class;
1582
-        $class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1583
-        $class = self::appendInputSizeClass($class, $answer);
1584
-        $extra     = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1585
-
1586
-        $label_html   = self::label($question, $required_text, $required['label'], '', $label_class);
1587
-        $mobile_label = self::mobileLabel(
1588
-            $add_mobile_label,
1589
-            $question,
1590
-            $required_text,
1591
-            $required['label'],
1592
-            $label_class,
1593
-            $name
1594
-        );
1595
-
1596
-        $input_html = $mobile_label
1597
-            . '<input type="text" name="' . $name . '" id="' . $id . '" '
1598
-            . 'class="' . $class . ' ' . $required['class'] . ' datepicker" value="' . $answer . '"  '
1599
-            . 'aria-label="' . esc_attr($required['msg']) . '" ' . $disabled . ' ' . $extra . '/>';
1600
-
1601
-        // enqueue scripts
1602
-        wp_register_style(
1603
-            'espresso-ui-theme',
1604
-            EE_GLOBAL_ASSETS_URL . 'css/espresso-ui-theme/jquery-ui-1.10.3.custom.min.css',
1605
-            [],
1606
-            EVENT_ESPRESSO_VERSION
1607
-        );
1608
-        wp_enqueue_style('espresso-ui-theme');
1609
-        wp_enqueue_script('jquery-ui-datepicker');
1610
-
1611
-        $input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1612
-        return $label_html . $input_html;
1613
-    }
1614
-
1615
-
1616
-    /**
1617
-     *  remove_label_keep_required_msg
1618
-     *  this will strip out a form input's label HTML while keeping the required text HTML that MUST be before the label
1619
-     *
1620
-     * @access public
1621
-     * @return     string
1622
-     */
1623
-    public static function remove_label_keep_required_msg($label_html, $required_text)
1624
-    {
1625
-        return $required_text;
1626
-    }
1627
-
1628
-
1629
-    /**
1630
-     * Simply returns the HTML for a hidden input of the given name and value.
1631
-     *
1632
-     * @param string $name
1633
-     * @param string $value
1634
-     * @return string HTML
1635
-     */
1636
-    public static function hidden_input($name, $value, $id = '')
1637
-    {
1638
-        $id = ! empty($id) ? $id : $name;
1639
-        return '<input id="' . $id . '" type="hidden" name="' . $name . '" value="' . $value . '"/>';
1640
-    }
1641
-
1642
-
1643
-    /**
1644
-     * prep_question
1645
-     *
1646
-     * @param string $question
1647
-     * @return string
1648
-     */
1649
-    public static function prep_question($question)
1650
-    {
1651
-        return $question;
1652
-    }
1653
-
1654
-
1655
-    /**
1656
-     *  prep_answer
1657
-     *
1658
-     * @param mixed $answer
1659
-     * @return string
1660
-     */
1661
-    public static function prep_answer($answer, $use_html_entities = true)
1662
-    {
1663
-        // make sure we convert bools first.  Otherwise (bool) false becomes an empty string which is NOT desired,
1664
-        // we want "0".
1665
-        if (is_bool($answer)) {
1666
-            $answer = $answer ? 1 : 0;
1667
-        }
1668
-        $answer = trim(stripslashes(str_replace('&#039;', "'", $answer)));
1669
-        return $use_html_entities ? htmlentities($answer, ENT_QUOTES, 'UTF-8') : $answer;
1670
-    }
1671
-
1672
-
1673
-    /**
1674
-     *  prep_answer_options
1675
-     *
1676
-     * @param array $QSOs array of EE_Question_Option objects
1677
-     * @return array
1678
-     */
1679
-    public static function prep_answer_options($QSOs = [])
1680
-    {
1681
-        $prepped_answer_options = [];
1682
-        if (is_array($QSOs) && ! empty($QSOs)) {
1683
-            foreach ($QSOs as $key => $QSO) {
1684
-                if (! $QSO instanceof EE_Question_Option) {
1685
-                    $QSO = EE_Question_Option::new_instance(
1686
-                        [
1687
-                            'QSO_value' => is_array($QSO) && isset($QSO['id'])
1688
-                                ? (string) $QSO['id']
1689
-                                : (string) $key,
1690
-                            'QSO_desc'  => is_array($QSO) && isset($QSO['text'])
1691
-                                ? (string) $QSO['text']
1692
-                                : (string) $QSO,
1693
-                        ]
1694
-                    );
1695
-                }
1696
-                if ($QSO->opt_group()) {
1697
-                    $prepped_answer_options[ $QSO->opt_group() ][] = $QSO;
1698
-                } else {
1699
-                    $prepped_answer_options[] = $QSO;
1700
-                }
1701
-            }
1702
-        }
1703
-        //      d( $prepped_answer_options );
1704
-        return $prepped_answer_options;
1705
-    }
1706
-
1707
-
1708
-    /**
1709
-     *  prep_option_value
1710
-     *
1711
-     * @param string|int $option_value
1712
-     * @return string
1713
-     */
1714
-    public static function prep_option_value($option_value)
1715
-    {
1716
-        return esc_attr(trim(stripslashes($option_value)));
1717
-    }
1718
-
1719
-
1720
-    /**
1721
-     *  prep_required
1722
-     *
1723
-     * @param string|array $required
1724
-     * @return array
1725
-     */
1726
-    public static function prep_required($required = [])
1727
-    {
1728
-        // make sure required is an array
1729
-        $required = is_array($required) ? $required : [];
1730
-        // and set some defaults
1731
-        return array_merge(['label' => '', 'class' => '', 'msg' => ''], $required);
1732
-    }
1733
-
1734
-
1735
-    /**
1736
-     *  get_label_size_class
1737
-     *
1738
-     * @param string $value
1739
-     * @return string
1740
-     */
1741
-    public static function get_label_size_class($value = false)
1742
-    {
1743
-        if ($value === false || $value === '') {
1744
-            return ' class="medium-lbl"';
1745
-        }
1746
-        // determine length of option value
1747
-        $val_size = strlen($value);
1748
-        switch ($val_size) {
1749
-            case $val_size < 3:
1750
-                $size = ' class="nano-lbl"';
1751
-                break;
1752
-            case $val_size < 6:
1753
-                $size = ' class="micro-lbl"';
1754
-                break;
1755
-            case $val_size < 12:
1756
-                $size = ' class="tiny-lbl"';
1757
-                break;
1758
-            case $val_size < 25:
1759
-                $size = ' class="small-lbl"';
1760
-                break;
1761
-            case $val_size > 100:
1762
-                $size = ' class="big-lbl"';
1763
-                break;
1764
-            default:
1765
-                $size = ' class="medium-lbl"';
1766
-                break;
1767
-        }
1768
-        return $size;
1769
-    }
1770
-
1771
-
1772
-    /**
1773
-     *  _load_system_dropdowns
1774
-     *
1775
-     * @param EE_Question_Form_Input $QFI
1776
-     * @return array
1777
-     * @throws EE_Error
1778
-     * @throws ReflectionException
1779
-     */
1780
-    private static function _load_system_dropdowns($QFI)
1781
-    {
1782
-        $QST_system = $QFI->get('QST_system');
1783
-        switch ($QST_system) {
1784
-            case 'state':
1785
-                $QFI = self::generate_state_dropdown($QFI);
1786
-                break;
1787
-            case 'country':
1788
-                $QFI = self::generate_country_dropdown($QFI);
1789
-                break;
1790
-            case 'admin-state':
1791
-                $QFI = self::generate_state_dropdown($QFI, true);
1792
-                break;
1793
-            case 'admin-country':
1794
-                $QFI = self::generate_country_dropdown($QFI, true);
1795
-                break;
1796
-        }
1797
-        return $QFI;
1798
-    }
1799
-
1800
-
1801
-    /**
1802
-     * This preps dropdowns that are specialized.
1803
-     *
1804
-     * @param EE_Question_Form_Input $QFI
1805
-     *
1806
-     * @return EE_Question_Form_Input
1807
-     * @throws EE_Error
1808
-     * @throws ReflectionException
1809
-     * @since  4.6.0
1810
-     */
1811
-    protected static function _load_specialized_dropdowns($QFI)
1812
-    {
1813
-        switch ($QFI->get('QST_type')) {
1814
-            case 'STATE':
1815
-                $QFI = self::generate_state_dropdown($QFI);
1816
-                break;
1817
-            case 'COUNTRY':
1818
-                $QFI = self::generate_country_dropdown($QFI);
1819
-                break;
1820
-        }
1821
-        return $QFI;
1822
-    }
1823
-
1824
-
1825
-    /**
1826
-     *    generate_state_dropdown
1827
-     *
1828
-     * @param EE_Question_Form_Input $QST
1829
-     * @param bool                   $get_all
1830
-     * @return EE_Question_Form_Input
1831
-     * @throws EE_Error
1832
-     * @throws ReflectionException
1833
-     */
1834
-    public static function generate_state_dropdown($QST, $get_all = false)
1835
-    {
1836
-        $states = $get_all
1837
-            ? EEM_State::instance()->get_all_states()
1838
-            : EEM_State::instance()->get_all_states_of_active_countries();
1839
-        if ($states && count($states) != count($QST->options())) {
1840
-            $QST->set('QST_type', 'DROPDOWN');
1841
-            // if multiple countries, we'll create option groups within the dropdown
1842
-            foreach ($states as $state) {
1843
-                if ($state instanceof EE_State) {
1844
-                    $QSO = EE_Question_Option::new_instance(
1845
-                        [
1846
-                            'QSO_value'   => $state->ID(),
1847
-                            'QSO_desc'    => $state->name(),
1848
-                            'QST_ID'      => $QST->get('QST_ID'),
1849
-                            'QSO_deleted' => false,
1850
-                        ]
1851
-                    );
1852
-                    // set option group
1853
-                    $QSO->set_opt_group($state->country()->name());
1854
-                    // add option to question
1855
-                    $QST->add_temp_option($QSO);
1856
-                }
1857
-            }
1858
-        }
1859
-        return $QST;
1860
-    }
1861
-
1862
-
1863
-    /**
1864
-     *    generate_country_dropdown
1865
-     *
1866
-     * @param      $QST
1867
-     * @param bool $get_all
1868
-     * @return array
1869
-     * @throws EE_Error
1870
-     * @throws ReflectionException
1871
-     * @internal param array $question
1872
-     */
1873
-    public static function generate_country_dropdown($QST, $get_all = false)
1874
-    {
1875
-        $countries = $get_all
1876
-            ? EEM_Country::instance()->get_all_countries()
1877
-            : EEM_Country::instance()->get_all_active_countries();
1878
-        if ($countries && count($countries) != count($QST->options())) {
1879
-            $QST->set('QST_type', 'DROPDOWN');
1880
-            // now add countries
1881
-            foreach ($countries as $country) {
1882
-                if ($country instanceof EE_Country) {
1883
-                    $QSO = EE_Question_Option::new_instance(
1884
-                        [
1885
-                            'QSO_value'   => $country->ID(),
1886
-                            'QSO_desc'    => $country->name(),
1887
-                            'QST_ID'      => $QST->get('QST_ID'),
1888
-                            'QSO_deleted' => false,
1889
-                        ]
1890
-                    );
1891
-                    $QST->add_temp_option($QSO);
1892
-                }
1893
-            }
1894
-        }
1895
-        return $QST;
1896
-    }
1897
-
1898
-
1899
-    /**
1900
-     *  generates options for a month dropdown selector with numbers from 01 to 12
1901
-     *
1902
-     * @return array()
1903
-     */
1904
-    public static function two_digit_months_dropdown_options()
1905
-    {
1906
-        $options = [];
1907
-        for ($x = 1; $x <= 12; $x++) {
1908
-            $mm             = str_pad($x, 2, '0', STR_PAD_LEFT);
1909
-            $options[ $mm ] = $mm;
1910
-        }
1911
-        return EEH_Form_Fields::prep_answer_options($options);
1912
-    }
1913
-
1914
-
1915
-    /**
1916
-     *  generates a year dropdown selector with numbers for the next ten years
1917
-     *
1918
-     * @return array
1919
-     */
1920
-    public static function next_decade_two_digit_year_dropdown_options()
1921
-    {
1922
-        $options      = [];
1923
-        $current_year = date('y');
1924
-        $next_decade  = $current_year + 10;
1925
-        for ($x = $current_year; $x <= $next_decade; $x++) {
1926
-            $yy             = str_pad($x, 2, '0', STR_PAD_LEFT);
1927
-            $options[ $yy ] = $yy;
1928
-        }
1929
-        return EEH_Form_Fields::prep_answer_options($options);
1930
-    }
1931
-
1932
-
1933
-    /**
1934
-     * generates a month/year dropdown selector for all registrations matching the given criteria.  Typically used for
1935
-     * list table filter.
1936
-     *
1937
-     * @param string  $cur_date     any currently selected date can be entered here.
1938
-     * @param string  $status       Registration status
1939
-     * @param integer $evt_category Event Category ID if the Event Category filter is selected
1940
-     * @return string                html
1941
-     * @throws EE_Error
1942
-     */
1943
-    public static function generate_registration_months_dropdown($cur_date = '', $status = '', $evt_category = 0)
1944
-    {
1945
-        $_where = [];
1946
-        if (! empty($status)) {
1947
-            $_where['STS_ID'] = $status;
1948
-        }
1949
-
1950
-        if ($evt_category > 0) {
1951
-            $_where['Event.Term_Taxonomy.term_id'] = $evt_category;
1952
-        }
1953
-
1954
-        $reg_dates = EEM_Registration::instance()->get_reg_months_and_years($_where);
1955
-
1956
-        // setup vals for select input helper
1957
-        $options = [
1958
-            0 => [
1959
-                'text' => esc_html__('Select a Month/Year', 'event_espresso'),
1960
-                'id'   => '',
1961
-            ],
1962
-        ];
1963
-
1964
-        foreach ($reg_dates as $reg_date) {
1965
-            $date      = $reg_date->reg_month . ' ' . $reg_date->reg_year;
1966
-            $options[] = [
1967
-                'text' => $date,
1968
-                'id'   => $date,
1969
-            ];
1970
-        }
1971
-
1972
-        return self::select_input('month_range', $options, $cur_date, '', 'ee-list-table-filter');
1973
-    }
1974
-
1975
-
1976
-    /**
1977
-     * generates a month/year dropdown selector for all events matching the given criteria
1978
-     * Typically used for list table filter
1979
-     *
1980
-     * @param string $cur_date          any currently selected date can be entered here.
1981
-     * @param string $status            "view" (i.e. all, today, month, draft)
1982
-     * @param int    $evt_category      category event belongs to
1983
-     * @param string $evt_active_status "upcoming", "expired", "active", or "inactive"
1984
-     * @return string
1985
-     * @throws EE_Error
1986
-     * @throws ReflectionException
1987
-     */
1988
-    public static function generate_event_months_dropdown(
1989
-        string $cur_date = '',
1990
-        string $status = 'all',
1991
-        int $evt_category = 0,
1992
-        string $evt_active_status = ''
1993
-    ): string {
1994
-        $status = empty($status) ? 'all' : $status;
1995
-        // determine what post_status our condition will have for the query.
1996
-        switch ($status) {
1997
-            case 'month':
1998
-            case 'today':
1999
-            case 'all':
2000
-                $where['Event.status'] = ['NOT IN', ['trash']];
2001
-                break;
2002
-            case 'draft':
2003
-                $where['Event.status'] = ['IN', ['draft', 'auto-draft']];
2004
-                break;
2005
-            default:
2006
-                $where['Event.status'] = $status;
2007
-        }
2008
-
2009
-        // categories?
2010
-        if (! empty($evt_category) and $evt_category > 0) {
2011
-            $where['Event.Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
2012
-            $where['Event.Term_Taxonomy.term_id']  = $evt_category;
2013
-        }
2014
-
2015
-        //      $where['DTT_is_primary'] = 1;
2016
-
2017
-        $DTTS = EEM_Datetime::instance()->get_dtt_months_and_years($where, $evt_active_status);
2018
-
2019
-        // let's setup vals for select input helper
2020
-        $options = [
2021
-            0 => [
2022
-                'text' => esc_html__('Select a Month/Year', 'event_espresso'),
2023
-                'id'   => "",
2024
-            ],
2025
-        ];
2026
-
2027
-
2028
-        // translate month and date
2029
-        global $wp_locale;
2030
-
2031
-        foreach ($DTTS as $DTT) {
2032
-            $localized_date = $wp_locale->get_month($DTT->dtt_month_num) . ' ' . $DTT->dtt_year;
2033
-            $id             = $DTT->dtt_month . ' ' . $DTT->dtt_year;
2034
-            $options[]      = [
2035
-                'text' => $localized_date,
2036
-                'id'   => $id,
2037
-            ];
2038
-        }
2039
-
2040
-
2041
-        return self::select_input('month_range', $options, $cur_date, '', 'ee-list-table-filter');
2042
-    }
2043
-
2044
-
2045
-    /**
2046
-     * generates the dropdown selector for event categories
2047
-     * typically used as a filter on list tables.
2048
-     *
2049
-     * @param integer $current_cat currently selected category
2050
-     * @return string               html for dropdown
2051
-     * @throws EE_Error
2052
-     * @throws ReflectionException
2053
-     */
2054
-    public static function generate_event_category_dropdown($current_cat = -1)
2055
-    {
2056
-        $categories = EEM_Term::instance()->get_all_ee_categories(true);
2057
-        $options    = [
2058
-            '0' => [
2059
-                'text' => esc_html__('All Categories', 'event_espresso'),
2060
-                'id'   => -1,
2061
-            ],
2062
-        ];
2063
-
2064
-        // setup categories for dropdown
2065
-        foreach ($categories as $category) {
2066
-            $options[] = [
2067
-                'text' => $category->get('name'),
2068
-                'id'   => $category->ID(),
2069
-            ];
2070
-        }
2071
-
2072
-        return self::select_input('EVT_CAT', $options, $current_cat, '', 'ee-list-table-filter');
2073
-    }
2074
-
2075
-
2076
-    /**
2077
-     *    generate a submit button with or without its own microform
2078
-     *    this is the only way to create buttons that are compatible across all themes
2079
-     *
2080
-     * @access    public
2081
-     * @param string      $url              - the form action
2082
-     * @param string      $ID               - some kind of unique ID, appended with "-sbmt" for the input and "-frm"
2083
-     *                                      for the form
2084
-     * @param string      $class            - css classes (separated by spaces if more than one)
2085
-     * @param string      $text             - what appears on the button
2086
-     * @param string      $nonce_action     - if using nonces
2087
-     * @param bool|string $input_only       - whether to print form header and footer. TRUE returns the input without
2088
-     *                                      the form
2089
-     * @param string      $extra_attributes - any extra attributes that need to be attached to the form input
2090
-     * @return    string
2091
-     */
2092
-    public static function submit_button(
2093
-        $url = '',
2094
-        $ID = '',
2095
-        $class = '',
2096
-        $text = '',
2097
-        $nonce_action = '',
2098
-        $input_only = false,
2099
-        $extra_attributes = ''
2100
-    ) {
2101
-        $btn = '';
2102
-        if (empty($url) || empty($ID)) {
2103
-            return $btn;
2104
-        }
2105
-        $text = ! empty($text) ? $text : esc_html__('Submit', 'event_espresso');
2106
-        $btn  .= '<input id="' . $ID . '-btn" class="' . $class . '" '
2107
-                 . 'type="submit" value="' . $text . '" ' . $extra_attributes . '/>';
2108
-        if (! $input_only) {
2109
-            $btn_frm = '<form id="' . $ID . '-frm" method="POST" action="' . $url . '">';
2110
-            $btn_frm .= ! empty($nonce_action)
2111
-                ? wp_nonce_field($nonce_action, $nonce_action . '_nonce', true, false)
2112
-                : '';
2113
-            $btn_frm .= $btn;
2114
-            $btn_frm .= '</form>';
2115
-            $btn     = $btn_frm;
2116
-            unset($btn_frm);
2117
-        }
2118
-        return $btn;
2119
-    }
1089
+		$input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1090
+		return $label_html . $input_html;
1091
+	}
1092
+
1093
+
1094
+	/**
1095
+	 * generates HTML for a form textarea
1096
+	 *
1097
+	 * @param string $question    label content
1098
+	 * @param string $answer      form input value attribute
1099
+	 * @param string $name        form input name attribute
1100
+	 * @param string $id          form input css id attribute
1101
+	 * @param string $class       form input css class attribute
1102
+	 * @param array  $dimensions  array of form input rows and cols attributes : array( 'rows' => 3, 'cols' => 40 )
1103
+	 * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1104
+	 *                            required 'class', and required 'msg' attribute
1105
+	 * @param string $label_class css class attribute for the label
1106
+	 * @param string $disabled    disabled="disabled" or null
1107
+	 * @return string HTML
1108
+	 */
1109
+	public static function textarea(
1110
+		$question = false,
1111
+		$answer = null,
1112
+		$name = false,
1113
+		$id = '',
1114
+		$class = '',
1115
+		$dimensions = false,
1116
+		$required = false,
1117
+		$required_text = '',
1118
+		$label_class = '',
1119
+		$disabled = false,
1120
+		$system_ID = false,
1121
+		$use_html_entities = true,
1122
+		$add_mobile_label = false
1123
+	) {
1124
+		// need these
1125
+		if (! $question || ! $name) {
1126
+			return null;
1127
+		}
1128
+		// prep the answer
1129
+		$answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1130
+		// prep the required array
1131
+		$required = self::prep_required($required);
1132
+		// make sure $dimensions is an array
1133
+		$dimensions = is_array($dimensions) ? $dimensions : [];
1134
+		// and set some defaults
1135
+		$dimensions = array_merge(['rows' => 3, 'cols' => 40], $dimensions);
1136
+		// set disabled tag
1137
+		$disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1138
+		// ya gots ta have style man!!!
1139
+		$class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1140
+		$class     .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1141
+		$extra     = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1142
+
1143
+		$label_html   = self::label($question, $required_text, $required['label'], $name, $label_class);
1144
+		$mobile_label = self::mobileLabel(
1145
+			$add_mobile_label,
1146
+			$question,
1147
+			$required_text,
1148
+			$required['label'],
1149
+			$label_class,
1150
+			$name
1151
+		);
1152
+
1153
+		$input_html = $mobile_label
1154
+			. '<textarea name="' . $name . '" id="' . $id . '" class="' . trim($class) . '" '
1155
+			. 'rows="' . $dimensions['rows'] . '" cols="' . $dimensions['cols'] . '"  '
1156
+			. 'aria-label="' . $required['msg'] . '" ' . $disabled . ' ' . $extra . '>'
1157
+			 . esc_textarea($answer)
1158
+			  . '</textarea>';
1159
+
1160
+		$input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1161
+		return $label_html . $input_html;
1162
+	}
1163
+
1164
+
1165
+	/**
1166
+	 * generates HTML for a form select input
1167
+	 *
1168
+	 * @param string $question    label content
1169
+	 * @param string $answer      form input value attribute
1170
+	 * @param array  $options     array of answer options where array key = option value and array value = option
1171
+	 *                            display text
1172
+	 * @param string $name        form input name attribute
1173
+	 * @param string $id          form input css id attribute
1174
+	 * @param string $class       form input css class attribute
1175
+	 * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1176
+	 *                            required 'class', and required 'msg' attribute
1177
+	 * @param string $label_class css class attribute for the label
1178
+	 * @param string $disabled    disabled="disabled" or null
1179
+	 * @return string HTML
1180
+	 */
1181
+	public static function select(
1182
+		$question = false,
1183
+		$answer = null,
1184
+		$options = false,
1185
+		$name = false,
1186
+		$id = '',
1187
+		$class = '',
1188
+		$required = false,
1189
+		$required_text = '',
1190
+		$label_class = '',
1191
+		$disabled = false,
1192
+		$system_ID = false,
1193
+		$use_html_entities = true,
1194
+		$add_please_select_option = false,
1195
+		$add_mobile_label = false
1196
+	) {
1197
+
1198
+		// need these
1199
+		if (! $question || ! $name || ! $options || empty($options) || ! is_array($options)) {
1200
+			return null;
1201
+		}
1202
+		// prep the answer
1203
+		$answer = is_array($answer)
1204
+			? self::prep_answer(array_shift($answer), $use_html_entities)
1205
+			: self::prep_answer($answer, $use_html_entities);
1206
+		// prep the required array
1207
+		$required = self::prep_required($required);
1208
+		// set disabled tag
1209
+		$disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1210
+		// ya gots ta have style man!!!
1211
+		$class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1212
+		$class = self::appendInputSizeClass($class, $options);
1213
+		$extra     = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1214
+
1215
+		$label_html   = self::label($question, $required_text, $required['label'], $name, $label_class);
1216
+		$mobile_label = self::mobileLabel(
1217
+			$add_mobile_label,
1218
+			$question,
1219
+			$required_text,
1220
+			$required['label'],
1221
+			$label_class,
1222
+			$name
1223
+		);
1224
+
1225
+		$input_html = $mobile_label
1226
+			. '<select name="' . $name . '" id="' . $id . '" class="' . trim($class) . ' ' . $required['class'] . '" '
1227
+			. 'aria-label="' . esc_attr($required['msg']) . '"' . $disabled . ' ' . $extra . '>';
1228
+		// recursively count array elements, to determine total number of options
1229
+		$only_option = count($options, 1) == 1;
1230
+		if (! $only_option) {
1231
+			// if there is NO answer set and there are multiple options to choose from, then set the "please select" message as selected
1232
+			$selected   = $answer === null ? ' selected' : '';
1233
+			$input_html .= $add_please_select_option
1234
+				? "\n\t\t\t\t"
1235
+				  . '<option value=""' . $selected . '>'
1236
+				  . esc_html__(' - please select - ', 'event_espresso')
1237
+				  . '</option>'
1238
+				: '';
1239
+		}
1240
+		foreach ($options as $key => $value) {
1241
+			// if value is an array, then create option groups, else create regular ol' options
1242
+			$input_html .= is_array($value)
1243
+				? self::_generate_select_option_group(
1244
+					$key,
1245
+					$value,
1246
+					$answer,
1247
+					$use_html_entities
1248
+				)
1249
+				: self::_generate_select_option(
1250
+					$value->value(),
1251
+					$value->desc(),
1252
+					$answer,
1253
+					$only_option,
1254
+					$use_html_entities
1255
+				);
1256
+		}
1257
+
1258
+		$input_html .= "\n\t\t\t" . '</select>';
1259
+
1260
+		$input_html =
1261
+			apply_filters(
1262
+				'FHEE__EEH_Form_Fields__select__before_end_wrapper',
1263
+				$input_html,
1264
+				$question,
1265
+				$answer,
1266
+				$name,
1267
+				$id,
1268
+				$class,
1269
+				$system_ID
1270
+			);
1271
+
1272
+		$input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1273
+		return $label_html . $input_html;
1274
+	}
1275
+
1276
+
1277
+	/**
1278
+	 *  _generate_select_option_group
1279
+	 *
1280
+	 *  if  $value for a select box is an array, then the key will be used as the optgroup label
1281
+	 *  and the value array will be looped thru and the elements sent to _generate_select_option
1282
+	 *
1283
+	 * @param mixed   $opt_group
1284
+	 * @param mixed   $QSOs
1285
+	 * @param mixed   $answer
1286
+	 * @param boolean $use_html_entities
1287
+	 * @return string
1288
+	 */
1289
+	private static function _generate_select_option_group($opt_group, $QSOs, $answer, $use_html_entities = true)
1290
+	{
1291
+		$html = "\n\t\t\t\t" . '<optgroup label="' . self::prep_option_value($opt_group) . '">';
1292
+		foreach ($QSOs as $QSO) {
1293
+			$html .= self::_generate_select_option($QSO->value(), $QSO->desc(), $answer, false, $use_html_entities);
1294
+		}
1295
+		$html .= "\n\t\t\t\t" . '</optgroup>';
1296
+		return $html;
1297
+	}
1298
+
1299
+
1300
+	/**
1301
+	 *  _generate_select_option
1302
+	 *
1303
+	 * @param mixed   $key
1304
+	 * @param mixed   $value
1305
+	 * @param mixed   $answer
1306
+	 * @param int     $only_option
1307
+	 * @param boolean $use_html_entities
1308
+	 * @return string
1309
+	 */
1310
+	private static function _generate_select_option(
1311
+		$key,
1312
+		$value,
1313
+		$answer,
1314
+		$only_option = false,
1315
+		$use_html_entities = true
1316
+	) {
1317
+		$key      = self::prep_answer($key, $use_html_entities);
1318
+		$value    = self::prep_answer($value, $use_html_entities);
1319
+		$value    = ! empty($value) ? $value : $key;
1320
+		$selected = ($answer == $key || $only_option) ? 'selected' : '';
1321
+		return "\n\t\t\t\t"
1322
+			   . '<option value="' . self::prep_option_value($key) . '" ' . $selected . '> '
1323
+			   . $value
1324
+			   . '&nbsp;&nbsp;&nbsp;</option>';
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 * generates HTML for form radio button inputs
1330
+	 *
1331
+	 * @param string      $question    label content
1332
+	 * @param string|int  $answer      form input value attribute
1333
+	 * @param array|bool  $options     array of answer options where array key = option value and array value = option
1334
+	 *                                 display text
1335
+	 * @param string $name        form input name attribute
1336
+	 * @param string      $id          form input css id attribute
1337
+	 * @param string      $class       form input css class attribute
1338
+	 * @param array|bool  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1339
+	 *                                 required 'class', and required 'msg' attribute
1340
+	 * @param string      $required_text
1341
+	 * @param string      $label_class css class attribute for the label
1342
+	 * @param bool|string $disabled    disabled="disabled" or null
1343
+	 * @param string      $system_ID
1344
+	 * @param bool        $use_html_entities
1345
+	 * @param bool        $label_b4
1346
+	 * @param bool        $use_desc_4_label
1347
+	 * @return string HTML
1348
+	 */
1349
+	public static function radio(
1350
+		string $question = '',
1351
+		$answer = null,
1352
+		$options = false,
1353
+		string $name = '',
1354
+		string $id = '',
1355
+		string $class = '',
1356
+		$required = false,
1357
+		string $required_text = '',
1358
+		string $label_class = '',
1359
+		bool $disabled = false,
1360
+		string $system_ID = '',
1361
+		bool $use_html_entities = true,
1362
+		bool $label_b4 = false,
1363
+		bool $use_desc_4_label = false,
1364
+		bool $add_mobile_label = false
1365
+	) {
1366
+		// need these
1367
+		if (! $question || ! $name || ! $options || empty($options) || ! is_array($options)) {
1368
+			return null;
1369
+		}
1370
+		// prep the answer
1371
+		$answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1372
+		// prep the required array
1373
+		$required = self::prep_required($required);
1374
+		// set disabled tag
1375
+		$disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1376
+		// ya gots ta have style man!!!
1377
+		$radio_class = is_admin() ? 'ee-admin-radio-lbl' : $label_class;
1378
+		$class       = ! empty($class) ? $class : 'espresso-radio-btn-inp';
1379
+		$extra       = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1380
+
1381
+		$label_html = self::label($question, $required_text, $required['label'], '', $label_class);
1382
+		$mobile_label = self::mobileLabel(
1383
+			$add_mobile_label,
1384
+			$question,
1385
+			$required_text,
1386
+			$required['label'],
1387
+			$label_class
1388
+		);
1389
+
1390
+		$input_html = $mobile_label
1391
+			. '<ul id="' . $id . '-ul" class="espresso-radio-btn-options-ul ' . $class . '-ul">';
1392
+
1393
+		$class .= ! empty($system_ID) ? ' ' . $system_ID : '';
1394
+		$class .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1395
+
1396
+		foreach ($options as $OPT) {
1397
+			if ($OPT instanceof EE_Question_Option) {
1398
+				$value   = self::prep_option_value($OPT->value());
1399
+				$label   = $use_desc_4_label ? $OPT->desc() : $OPT->value();
1400
+				$size    = $use_desc_4_label
1401
+					? self::get_label_size_class($OPT->value() . ' ' . $OPT->desc())
1402
+					: self::get_label_size_class($OPT->value());
1403
+				$desc    = $OPT->desc();// no self::prep_answer
1404
+				$answer  = is_numeric($value) && empty($answer) ? 0 : $answer;
1405
+				$value  = is_numeric($answer) && empty($value) ? 0 : $value;
1406
+				$checked = (string) $value == (string) $answer ? ' checked' : '';
1407
+				$opt     = '-' . sanitize_key($value);
1408
+
1409
+				$input_html .= "\n\t\t\t\t" . '<li' . $size . '>';
1410
+				$input_html .= "\n\t\t\t\t\t" . '<label class="' . $radio_class . ' espresso-radio-btn-lbl">';
1411
+				$input_html .= $label_b4 ? "\n\t\t\t\t\t\t" . '<span>' . $label . '</span>&nbsp;' : '';
1412
+				$input_html .= "\n\t\t\t\t\t\t"
1413
+							   . '<input type="radio" name="' . $name . '" id="' . $id . $opt . '" '
1414
+							   . 'class="' . $class . '" value="' . $value . '" '
1415
+							   . 'aria-label="' . esc_attr($required['msg']) . '" ' . $disabled
1416
+							   . $checked . ' ' . $extra . '/>';
1417
+				$input_html .= ! $label_b4
1418
+					? "\n\t\t\t\t\t\t"
1419
+					  . '&nbsp;<span class="espresso-radio-btn-desc">'
1420
+					  . $label
1421
+					  . '</span>'
1422
+					: '';
1423
+				$input_html .= "\n\t\t\t\t\t" . '</label>';
1424
+				$input_html .= $use_desc_4_label
1425
+					? ''
1426
+					: '<span class="espresso-radio-btn-option-desc small-text grey-text">' . $desc . '</span>';
1427
+				$input_html .= "\n\t\t\t\t" . '</li>';
1428
+			}
1429
+		}
1430
+
1431
+		$input_html .= "\n\t\t\t" . '</ul>';
1432
+
1433
+		$input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1434
+		return $label_html . $input_html;
1435
+	}
1436
+
1437
+
1438
+	/**
1439
+	 * generates HTML for form checkbox inputs
1440
+	 *
1441
+	 * @param string $question    label content
1442
+	 * @param string $answer      form input value attribute
1443
+	 * @param array  $options     array of options where array key = option value and array value = option display text
1444
+	 * @param string $name        form input name attribute
1445
+	 * @param string $id          form input css id attribute
1446
+	 * @param string $class       form input css class attribute
1447
+	 * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1448
+	 *                            required 'class', and required 'msg' attribute
1449
+	 * @param string $label_class css class attribute for the label
1450
+	 * @param string $disabled    disabled="disabled" or null
1451
+	 * @return string HTML
1452
+	 */
1453
+	public static function checkbox(
1454
+		$question = false,
1455
+		$answer = null,
1456
+		$options = false,
1457
+		$name = false,
1458
+		$id = '',
1459
+		$class = '',
1460
+		$required = false,
1461
+		$required_text = '',
1462
+		$label_class = '',
1463
+		$disabled = false,
1464
+		$label_b4 = false,
1465
+		$system_ID = false,
1466
+		$use_html_entities = true,
1467
+		$add_mobile_label = false
1468
+	) {
1469
+		// need these
1470
+		if (! $question || ! $name || ! $options || empty($options) || ! is_array($options)) {
1471
+			return null;
1472
+		}
1473
+		$answer = maybe_unserialize($answer);
1474
+
1475
+		// prep the answer(s)
1476
+		$answer = is_array($answer) ? $answer : [sanitize_key($answer) => $answer];
1477
+
1478
+		foreach ($answer as $key => $value) {
1479
+			$key            = self::prep_option_value($key);
1480
+			$answer[ $key ] = self::prep_answer($value, $use_html_entities);
1481
+		}
1482
+
1483
+		// prep the required array
1484
+		$required = self::prep_required($required);
1485
+		// set disabled tag
1486
+		$disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1487
+		// ya gots ta have style man!!!
1488
+		$radio_class = is_admin() ? 'ee-admin-radio-lbl' : $label_class;
1489
+		$class       = empty($class) ? 'espresso-radio-btn-inp' : $class;
1490
+		$extra       = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1491
+
1492
+		$label_html   = self::label($question, $required_text, $required['label'], '', $label_class);
1493
+		$mobile_label = self::mobileLabel(
1494
+			$add_mobile_label,
1495
+			$question,
1496
+			$required_text,
1497
+			$required['label'],
1498
+			$label_class
1499
+		);
1500
+
1501
+		$input_html = $mobile_label
1502
+			. '<ul id="' . $id . '-ul" class="espresso-checkbox-options-ul ' . $class . '-ul">';
1503
+
1504
+		$class .= ! empty($system_ID) ? ' ' . $system_ID : '';
1505
+		$class .= ! empty($required['class']) ? ' ' . $required['class'] : '';
1506
+
1507
+		foreach ($options as $OPT) {
1508
+			$value = $OPT->value();// self::prep_option_value( $OPT->value() );
1509
+			$size  = self::get_label_size_class($OPT->value() . ' ' . $OPT->desc());
1510
+			$text  = self::prep_answer($OPT->value());
1511
+			$desc  = $OPT->desc();
1512
+			$opt   = '-' . sanitize_key($value);
1513
+
1514
+			$checked = is_array($answer) && in_array($text, $answer) ? ' checked' : '';
1515
+
1516
+			$input_html .= "\n\t\t\t\t" . '<li' . $size . '>';
1517
+			$input_html .= "\n\t\t\t\t\t" . '<label class="' . $radio_class . ' espresso-checkbox-lbl">';
1518
+			$input_html .= $label_b4 ? "\n\t\t\t\t\t\t" . '<span>' . $text . '</span>&nbsp;' : '';
1519
+			$input_html .= "\n\t\t\t\t\t\t"
1520
+						   . '<input type="checkbox" name="' . $name . '[' . $OPT->ID() . ']" '
1521
+						   . 'id="' . $id . $opt . '" class="' . $class . '" value="' . $value . '" '
1522
+						   . 'aria-label="' . esc_attr($required['msg']) . '" ' . $disabled . $checked . ' ' . $extra . '/>';
1523
+			$input_html .= ! $label_b4 ? "\n\t\t\t\t\t\t" . '&nbsp;<span>' . $text . '</span>' : '';
1524
+			$input_html .= "\n\t\t\t\t\t" . '</label>';
1525
+			if (! empty($desc) && $desc != $text) {
1526
+				$input_html .= "\n\t\t\t\t\t"
1527
+							   . ' &nbsp; <br/><div class="espresso-checkbox-option-desc small-text grey-text">'
1528
+							   . $desc
1529
+							   . '</div>';
1530
+			}
1531
+			$input_html .= "\n\t\t\t\t" . '</li>';
1532
+		}
1533
+
1534
+		$input_html .= "\n\t\t\t" . '</ul>';
1535
+
1536
+		$input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1537
+		return $label_html . $input_html;
1538
+	}
1539
+
1540
+
1541
+	/**
1542
+	 * generates HTML for a form datepicker input
1543
+	 *
1544
+	 * @param string $question    label content
1545
+	 * @param string $answer      form input value attribute
1546
+	 * @param string $name        form input name attribute
1547
+	 * @param string $id          form input css id attribute
1548
+	 * @param string $class       form input css class attribute
1549
+	 * @param array  $required    'label', 'class', and 'msg' - array of values for required "label" content, css
1550
+	 *                            required 'class', and required 'msg' attribute
1551
+	 * @param string $label_class css class attribute for the label
1552
+	 * @param string $disabled    disabled="disabled" or null
1553
+	 * @return string HTML
1554
+	 */
1555
+	public static function datepicker(
1556
+		$question = false,
1557
+		$answer = null,
1558
+		$name = false,
1559
+		$id = '',
1560
+		$class = '',
1561
+		$required = false,
1562
+		$required_text = '',
1563
+		$label_class = '',
1564
+		$disabled = false,
1565
+		$system_ID = false,
1566
+		$use_html_entities = true,
1567
+		$add_mobile_label = false
1568
+	) {
1569
+		// need these
1570
+		if (! $question || ! $name) {
1571
+			return null;
1572
+		}
1573
+		// prep the answer
1574
+		$answer = is_array($answer) ? '' : self::prep_answer($answer, $use_html_entities);
1575
+		// prep the required array
1576
+		$required = self::prep_required($required);
1577
+		// set disabled tag
1578
+		$disabled = $answer === null || ! $disabled ? '' : ' disabled="disabled"';
1579
+		// ya gots ta have style man!!!
1580
+		$txt_class = is_admin() ? 'regular-text' : 'espresso-datepicker-inp';
1581
+		$class     = empty($class) ? $txt_class : $class;
1582
+		$class     .= ! empty($system_ID) ? ' ' . $system_ID : '';
1583
+		$class = self::appendInputSizeClass($class, $answer);
1584
+		$extra     = apply_filters('FHEE__EEH_Form_Fields__additional_form_field_attributes', '');
1585
+
1586
+		$label_html   = self::label($question, $required_text, $required['label'], '', $label_class);
1587
+		$mobile_label = self::mobileLabel(
1588
+			$add_mobile_label,
1589
+			$question,
1590
+			$required_text,
1591
+			$required['label'],
1592
+			$label_class,
1593
+			$name
1594
+		);
1595
+
1596
+		$input_html = $mobile_label
1597
+			. '<input type="text" name="' . $name . '" id="' . $id . '" '
1598
+			. 'class="' . $class . ' ' . $required['class'] . ' datepicker" value="' . $answer . '"  '
1599
+			. 'aria-label="' . esc_attr($required['msg']) . '" ' . $disabled . ' ' . $extra . '/>';
1600
+
1601
+		// enqueue scripts
1602
+		wp_register_style(
1603
+			'espresso-ui-theme',
1604
+			EE_GLOBAL_ASSETS_URL . 'css/espresso-ui-theme/jquery-ui-1.10.3.custom.min.css',
1605
+			[],
1606
+			EVENT_ESPRESSO_VERSION
1607
+		);
1608
+		wp_enqueue_style('espresso-ui-theme');
1609
+		wp_enqueue_script('jquery-ui-datepicker');
1610
+
1611
+		$input_html = apply_filters('FHEE__EEH_Form_Fields__input_html', $input_html, $label_html, $id);
1612
+		return $label_html . $input_html;
1613
+	}
1614
+
1615
+
1616
+	/**
1617
+	 *  remove_label_keep_required_msg
1618
+	 *  this will strip out a form input's label HTML while keeping the required text HTML that MUST be before the label
1619
+	 *
1620
+	 * @access public
1621
+	 * @return     string
1622
+	 */
1623
+	public static function remove_label_keep_required_msg($label_html, $required_text)
1624
+	{
1625
+		return $required_text;
1626
+	}
1627
+
1628
+
1629
+	/**
1630
+	 * Simply returns the HTML for a hidden input of the given name and value.
1631
+	 *
1632
+	 * @param string $name
1633
+	 * @param string $value
1634
+	 * @return string HTML
1635
+	 */
1636
+	public static function hidden_input($name, $value, $id = '')
1637
+	{
1638
+		$id = ! empty($id) ? $id : $name;
1639
+		return '<input id="' . $id . '" type="hidden" name="' . $name . '" value="' . $value . '"/>';
1640
+	}
1641
+
1642
+
1643
+	/**
1644
+	 * prep_question
1645
+	 *
1646
+	 * @param string $question
1647
+	 * @return string
1648
+	 */
1649
+	public static function prep_question($question)
1650
+	{
1651
+		return $question;
1652
+	}
1653
+
1654
+
1655
+	/**
1656
+	 *  prep_answer
1657
+	 *
1658
+	 * @param mixed $answer
1659
+	 * @return string
1660
+	 */
1661
+	public static function prep_answer($answer, $use_html_entities = true)
1662
+	{
1663
+		// make sure we convert bools first.  Otherwise (bool) false becomes an empty string which is NOT desired,
1664
+		// we want "0".
1665
+		if (is_bool($answer)) {
1666
+			$answer = $answer ? 1 : 0;
1667
+		}
1668
+		$answer = trim(stripslashes(str_replace('&#039;', "'", $answer)));
1669
+		return $use_html_entities ? htmlentities($answer, ENT_QUOTES, 'UTF-8') : $answer;
1670
+	}
1671
+
1672
+
1673
+	/**
1674
+	 *  prep_answer_options
1675
+	 *
1676
+	 * @param array $QSOs array of EE_Question_Option objects
1677
+	 * @return array
1678
+	 */
1679
+	public static function prep_answer_options($QSOs = [])
1680
+	{
1681
+		$prepped_answer_options = [];
1682
+		if (is_array($QSOs) && ! empty($QSOs)) {
1683
+			foreach ($QSOs as $key => $QSO) {
1684
+				if (! $QSO instanceof EE_Question_Option) {
1685
+					$QSO = EE_Question_Option::new_instance(
1686
+						[
1687
+							'QSO_value' => is_array($QSO) && isset($QSO['id'])
1688
+								? (string) $QSO['id']
1689
+								: (string) $key,
1690
+							'QSO_desc'  => is_array($QSO) && isset($QSO['text'])
1691
+								? (string) $QSO['text']
1692
+								: (string) $QSO,
1693
+						]
1694
+					);
1695
+				}
1696
+				if ($QSO->opt_group()) {
1697
+					$prepped_answer_options[ $QSO->opt_group() ][] = $QSO;
1698
+				} else {
1699
+					$prepped_answer_options[] = $QSO;
1700
+				}
1701
+			}
1702
+		}
1703
+		//      d( $prepped_answer_options );
1704
+		return $prepped_answer_options;
1705
+	}
1706
+
1707
+
1708
+	/**
1709
+	 *  prep_option_value
1710
+	 *
1711
+	 * @param string|int $option_value
1712
+	 * @return string
1713
+	 */
1714
+	public static function prep_option_value($option_value)
1715
+	{
1716
+		return esc_attr(trim(stripslashes($option_value)));
1717
+	}
1718
+
1719
+
1720
+	/**
1721
+	 *  prep_required
1722
+	 *
1723
+	 * @param string|array $required
1724
+	 * @return array
1725
+	 */
1726
+	public static function prep_required($required = [])
1727
+	{
1728
+		// make sure required is an array
1729
+		$required = is_array($required) ? $required : [];
1730
+		// and set some defaults
1731
+		return array_merge(['label' => '', 'class' => '', 'msg' => ''], $required);
1732
+	}
1733
+
1734
+
1735
+	/**
1736
+	 *  get_label_size_class
1737
+	 *
1738
+	 * @param string $value
1739
+	 * @return string
1740
+	 */
1741
+	public static function get_label_size_class($value = false)
1742
+	{
1743
+		if ($value === false || $value === '') {
1744
+			return ' class="medium-lbl"';
1745
+		}
1746
+		// determine length of option value
1747
+		$val_size = strlen($value);
1748
+		switch ($val_size) {
1749
+			case $val_size < 3:
1750
+				$size = ' class="nano-lbl"';
1751
+				break;
1752
+			case $val_size < 6:
1753
+				$size = ' class="micro-lbl"';
1754
+				break;
1755
+			case $val_size < 12:
1756
+				$size = ' class="tiny-lbl"';
1757
+				break;
1758
+			case $val_size < 25:
1759
+				$size = ' class="small-lbl"';
1760
+				break;
1761
+			case $val_size > 100:
1762
+				$size = ' class="big-lbl"';
1763
+				break;
1764
+			default:
1765
+				$size = ' class="medium-lbl"';
1766
+				break;
1767
+		}
1768
+		return $size;
1769
+	}
1770
+
1771
+
1772
+	/**
1773
+	 *  _load_system_dropdowns
1774
+	 *
1775
+	 * @param EE_Question_Form_Input $QFI
1776
+	 * @return array
1777
+	 * @throws EE_Error
1778
+	 * @throws ReflectionException
1779
+	 */
1780
+	private static function _load_system_dropdowns($QFI)
1781
+	{
1782
+		$QST_system = $QFI->get('QST_system');
1783
+		switch ($QST_system) {
1784
+			case 'state':
1785
+				$QFI = self::generate_state_dropdown($QFI);
1786
+				break;
1787
+			case 'country':
1788
+				$QFI = self::generate_country_dropdown($QFI);
1789
+				break;
1790
+			case 'admin-state':
1791
+				$QFI = self::generate_state_dropdown($QFI, true);
1792
+				break;
1793
+			case 'admin-country':
1794
+				$QFI = self::generate_country_dropdown($QFI, true);
1795
+				break;
1796
+		}
1797
+		return $QFI;
1798
+	}
1799
+
1800
+
1801
+	/**
1802
+	 * This preps dropdowns that are specialized.
1803
+	 *
1804
+	 * @param EE_Question_Form_Input $QFI
1805
+	 *
1806
+	 * @return EE_Question_Form_Input
1807
+	 * @throws EE_Error
1808
+	 * @throws ReflectionException
1809
+	 * @since  4.6.0
1810
+	 */
1811
+	protected static function _load_specialized_dropdowns($QFI)
1812
+	{
1813
+		switch ($QFI->get('QST_type')) {
1814
+			case 'STATE':
1815
+				$QFI = self::generate_state_dropdown($QFI);
1816
+				break;
1817
+			case 'COUNTRY':
1818
+				$QFI = self::generate_country_dropdown($QFI);
1819
+				break;
1820
+		}
1821
+		return $QFI;
1822
+	}
1823
+
1824
+
1825
+	/**
1826
+	 *    generate_state_dropdown
1827
+	 *
1828
+	 * @param EE_Question_Form_Input $QST
1829
+	 * @param bool                   $get_all
1830
+	 * @return EE_Question_Form_Input
1831
+	 * @throws EE_Error
1832
+	 * @throws ReflectionException
1833
+	 */
1834
+	public static function generate_state_dropdown($QST, $get_all = false)
1835
+	{
1836
+		$states = $get_all
1837
+			? EEM_State::instance()->get_all_states()
1838
+			: EEM_State::instance()->get_all_states_of_active_countries();
1839
+		if ($states && count($states) != count($QST->options())) {
1840
+			$QST->set('QST_type', 'DROPDOWN');
1841
+			// if multiple countries, we'll create option groups within the dropdown
1842
+			foreach ($states as $state) {
1843
+				if ($state instanceof EE_State) {
1844
+					$QSO = EE_Question_Option::new_instance(
1845
+						[
1846
+							'QSO_value'   => $state->ID(),
1847
+							'QSO_desc'    => $state->name(),
1848
+							'QST_ID'      => $QST->get('QST_ID'),
1849
+							'QSO_deleted' => false,
1850
+						]
1851
+					);
1852
+					// set option group
1853
+					$QSO->set_opt_group($state->country()->name());
1854
+					// add option to question
1855
+					$QST->add_temp_option($QSO);
1856
+				}
1857
+			}
1858
+		}
1859
+		return $QST;
1860
+	}
1861
+
1862
+
1863
+	/**
1864
+	 *    generate_country_dropdown
1865
+	 *
1866
+	 * @param      $QST
1867
+	 * @param bool $get_all
1868
+	 * @return array
1869
+	 * @throws EE_Error
1870
+	 * @throws ReflectionException
1871
+	 * @internal param array $question
1872
+	 */
1873
+	public static function generate_country_dropdown($QST, $get_all = false)
1874
+	{
1875
+		$countries = $get_all
1876
+			? EEM_Country::instance()->get_all_countries()
1877
+			: EEM_Country::instance()->get_all_active_countries();
1878
+		if ($countries && count($countries) != count($QST->options())) {
1879
+			$QST->set('QST_type', 'DROPDOWN');
1880
+			// now add countries
1881
+			foreach ($countries as $country) {
1882
+				if ($country instanceof EE_Country) {
1883
+					$QSO = EE_Question_Option::new_instance(
1884
+						[
1885
+							'QSO_value'   => $country->ID(),
1886
+							'QSO_desc'    => $country->name(),
1887
+							'QST_ID'      => $QST->get('QST_ID'),
1888
+							'QSO_deleted' => false,
1889
+						]
1890
+					);
1891
+					$QST->add_temp_option($QSO);
1892
+				}
1893
+			}
1894
+		}
1895
+		return $QST;
1896
+	}
1897
+
1898
+
1899
+	/**
1900
+	 *  generates options for a month dropdown selector with numbers from 01 to 12
1901
+	 *
1902
+	 * @return array()
1903
+	 */
1904
+	public static function two_digit_months_dropdown_options()
1905
+	{
1906
+		$options = [];
1907
+		for ($x = 1; $x <= 12; $x++) {
1908
+			$mm             = str_pad($x, 2, '0', STR_PAD_LEFT);
1909
+			$options[ $mm ] = $mm;
1910
+		}
1911
+		return EEH_Form_Fields::prep_answer_options($options);
1912
+	}
1913
+
1914
+
1915
+	/**
1916
+	 *  generates a year dropdown selector with numbers for the next ten years
1917
+	 *
1918
+	 * @return array
1919
+	 */
1920
+	public static function next_decade_two_digit_year_dropdown_options()
1921
+	{
1922
+		$options      = [];
1923
+		$current_year = date('y');
1924
+		$next_decade  = $current_year + 10;
1925
+		for ($x = $current_year; $x <= $next_decade; $x++) {
1926
+			$yy             = str_pad($x, 2, '0', STR_PAD_LEFT);
1927
+			$options[ $yy ] = $yy;
1928
+		}
1929
+		return EEH_Form_Fields::prep_answer_options($options);
1930
+	}
1931
+
1932
+
1933
+	/**
1934
+	 * generates a month/year dropdown selector for all registrations matching the given criteria.  Typically used for
1935
+	 * list table filter.
1936
+	 *
1937
+	 * @param string  $cur_date     any currently selected date can be entered here.
1938
+	 * @param string  $status       Registration status
1939
+	 * @param integer $evt_category Event Category ID if the Event Category filter is selected
1940
+	 * @return string                html
1941
+	 * @throws EE_Error
1942
+	 */
1943
+	public static function generate_registration_months_dropdown($cur_date = '', $status = '', $evt_category = 0)
1944
+	{
1945
+		$_where = [];
1946
+		if (! empty($status)) {
1947
+			$_where['STS_ID'] = $status;
1948
+		}
1949
+
1950
+		if ($evt_category > 0) {
1951
+			$_where['Event.Term_Taxonomy.term_id'] = $evt_category;
1952
+		}
1953
+
1954
+		$reg_dates = EEM_Registration::instance()->get_reg_months_and_years($_where);
1955
+
1956
+		// setup vals for select input helper
1957
+		$options = [
1958
+			0 => [
1959
+				'text' => esc_html__('Select a Month/Year', 'event_espresso'),
1960
+				'id'   => '',
1961
+			],
1962
+		];
1963
+
1964
+		foreach ($reg_dates as $reg_date) {
1965
+			$date      = $reg_date->reg_month . ' ' . $reg_date->reg_year;
1966
+			$options[] = [
1967
+				'text' => $date,
1968
+				'id'   => $date,
1969
+			];
1970
+		}
1971
+
1972
+		return self::select_input('month_range', $options, $cur_date, '', 'ee-list-table-filter');
1973
+	}
1974
+
1975
+
1976
+	/**
1977
+	 * generates a month/year dropdown selector for all events matching the given criteria
1978
+	 * Typically used for list table filter
1979
+	 *
1980
+	 * @param string $cur_date          any currently selected date can be entered here.
1981
+	 * @param string $status            "view" (i.e. all, today, month, draft)
1982
+	 * @param int    $evt_category      category event belongs to
1983
+	 * @param string $evt_active_status "upcoming", "expired", "active", or "inactive"
1984
+	 * @return string
1985
+	 * @throws EE_Error
1986
+	 * @throws ReflectionException
1987
+	 */
1988
+	public static function generate_event_months_dropdown(
1989
+		string $cur_date = '',
1990
+		string $status = 'all',
1991
+		int $evt_category = 0,
1992
+		string $evt_active_status = ''
1993
+	): string {
1994
+		$status = empty($status) ? 'all' : $status;
1995
+		// determine what post_status our condition will have for the query.
1996
+		switch ($status) {
1997
+			case 'month':
1998
+			case 'today':
1999
+			case 'all':
2000
+				$where['Event.status'] = ['NOT IN', ['trash']];
2001
+				break;
2002
+			case 'draft':
2003
+				$where['Event.status'] = ['IN', ['draft', 'auto-draft']];
2004
+				break;
2005
+			default:
2006
+				$where['Event.status'] = $status;
2007
+		}
2008
+
2009
+		// categories?
2010
+		if (! empty($evt_category) and $evt_category > 0) {
2011
+			$where['Event.Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
2012
+			$where['Event.Term_Taxonomy.term_id']  = $evt_category;
2013
+		}
2014
+
2015
+		//      $where['DTT_is_primary'] = 1;
2016
+
2017
+		$DTTS = EEM_Datetime::instance()->get_dtt_months_and_years($where, $evt_active_status);
2018
+
2019
+		// let's setup vals for select input helper
2020
+		$options = [
2021
+			0 => [
2022
+				'text' => esc_html__('Select a Month/Year', 'event_espresso'),
2023
+				'id'   => "",
2024
+			],
2025
+		];
2026
+
2027
+
2028
+		// translate month and date
2029
+		global $wp_locale;
2030
+
2031
+		foreach ($DTTS as $DTT) {
2032
+			$localized_date = $wp_locale->get_month($DTT->dtt_month_num) . ' ' . $DTT->dtt_year;
2033
+			$id             = $DTT->dtt_month . ' ' . $DTT->dtt_year;
2034
+			$options[]      = [
2035
+				'text' => $localized_date,
2036
+				'id'   => $id,
2037
+			];
2038
+		}
2039
+
2040
+
2041
+		return self::select_input('month_range', $options, $cur_date, '', 'ee-list-table-filter');
2042
+	}
2043
+
2044
+
2045
+	/**
2046
+	 * generates the dropdown selector for event categories
2047
+	 * typically used as a filter on list tables.
2048
+	 *
2049
+	 * @param integer $current_cat currently selected category
2050
+	 * @return string               html for dropdown
2051
+	 * @throws EE_Error
2052
+	 * @throws ReflectionException
2053
+	 */
2054
+	public static function generate_event_category_dropdown($current_cat = -1)
2055
+	{
2056
+		$categories = EEM_Term::instance()->get_all_ee_categories(true);
2057
+		$options    = [
2058
+			'0' => [
2059
+				'text' => esc_html__('All Categories', 'event_espresso'),
2060
+				'id'   => -1,
2061
+			],
2062
+		];
2063
+
2064
+		// setup categories for dropdown
2065
+		foreach ($categories as $category) {
2066
+			$options[] = [
2067
+				'text' => $category->get('name'),
2068
+				'id'   => $category->ID(),
2069
+			];
2070
+		}
2071
+
2072
+		return self::select_input('EVT_CAT', $options, $current_cat, '', 'ee-list-table-filter');
2073
+	}
2074
+
2075
+
2076
+	/**
2077
+	 *    generate a submit button with or without its own microform
2078
+	 *    this is the only way to create buttons that are compatible across all themes
2079
+	 *
2080
+	 * @access    public
2081
+	 * @param string      $url              - the form action
2082
+	 * @param string      $ID               - some kind of unique ID, appended with "-sbmt" for the input and "-frm"
2083
+	 *                                      for the form
2084
+	 * @param string      $class            - css classes (separated by spaces if more than one)
2085
+	 * @param string      $text             - what appears on the button
2086
+	 * @param string      $nonce_action     - if using nonces
2087
+	 * @param bool|string $input_only       - whether to print form header and footer. TRUE returns the input without
2088
+	 *                                      the form
2089
+	 * @param string      $extra_attributes - any extra attributes that need to be attached to the form input
2090
+	 * @return    string
2091
+	 */
2092
+	public static function submit_button(
2093
+		$url = '',
2094
+		$ID = '',
2095
+		$class = '',
2096
+		$text = '',
2097
+		$nonce_action = '',
2098
+		$input_only = false,
2099
+		$extra_attributes = ''
2100
+	) {
2101
+		$btn = '';
2102
+		if (empty($url) || empty($ID)) {
2103
+			return $btn;
2104
+		}
2105
+		$text = ! empty($text) ? $text : esc_html__('Submit', 'event_espresso');
2106
+		$btn  .= '<input id="' . $ID . '-btn" class="' . $class . '" '
2107
+				 . 'type="submit" value="' . $text . '" ' . $extra_attributes . '/>';
2108
+		if (! $input_only) {
2109
+			$btn_frm = '<form id="' . $ID . '-frm" method="POST" action="' . $url . '">';
2110
+			$btn_frm .= ! empty($nonce_action)
2111
+				? wp_nonce_field($nonce_action, $nonce_action . '_nonce', true, false)
2112
+				: '';
2113
+			$btn_frm .= $btn;
2114
+			$btn_frm .= '</form>';
2115
+			$btn     = $btn_frm;
2116
+			unset($btn_frm);
2117
+		}
2118
+		return $btn;
2119
+	}
2120 2120
 }
Please login to merge, or discard this patch.
core/libraries/iframe_display/Iframe.php 2 patches
Indentation   +407 added lines, -407 removed lines patch added patch discarded remove patch
@@ -17,411 +17,411 @@
 block discarded – undo
17 17
  */
18 18
 class Iframe
19 19
 {
20
-    /**
21
-     * HTML for notices and ajax gif
22
-     *
23
-     * @var string $title
24
-     */
25
-    protected string $title = '';
26
-
27
-    /**
28
-     * HTML for the content being displayed
29
-     *
30
-     * @var string $content
31
-     */
32
-    protected string $content = '';
33
-
34
-    /**
35
-     * whether to call wp_head() and wp_footer()
36
-     *
37
-     * @var bool $enqueue_wp_assets
38
-     */
39
-    protected bool $enqueue_wp_assets = false;
40
-
41
-    /**
42
-     * an array of CSS URLs
43
-     *
44
-     * @var array $css
45
-     */
46
-    protected array $css = [];
47
-
48
-    /**
49
-     * an array of JS URLs to be set in the HTML header.
50
-     *
51
-     * @var array $header_js
52
-     */
53
-    protected array $header_js = [];
54
-
55
-    /**
56
-     * an array of additional attributes to be added to <script> tags for header JS
57
-     *
58
-     * @var array $footer_js
59
-     */
60
-    protected array $header_js_attributes = [];
61
-
62
-    /**
63
-     * an array of JS URLs to be displayed before the HTML </body> tag
64
-     *
65
-     * @var array $footer_js
66
-     */
67
-    protected array $footer_js = [];
68
-
69
-    /**
70
-     * an array of additional attributes to be added to <script> tags for footer JS
71
-     *
72
-     * @var array $footer_js_attributes
73
-     */
74
-    protected array $footer_js_attributes = [];
75
-
76
-    /**
77
-     * an array of JSON vars to be set in the HTML header.
78
-     *
79
-     * @var array $localized_vars
80
-     */
81
-    protected array $localized_vars = [];
82
-
83
-    /**
84
-     * array of CSS styles to be printed inline before/after another CSS file
85
-     *
86
-     * @var array $inline_styles
87
-     */
88
-    protected array $inline_styles = [];
89
-
90
-
91
-    /**
92
-     * Iframe constructor
93
-     *
94
-     * @param string $title
95
-     * @param string $content
96
-     * @throws DomainException
97
-     */
98
-    public function __construct(string $title, string $content)
99
-    {
100
-        global $wp_version;
101
-        if (! defined('EE_IFRAME_DIR_URL')) {
102
-            define('EE_IFRAME_DIR_URL', plugin_dir_url(__FILE__));
103
-        }
104
-        $this->setContent($content);
105
-        $this->setTitle($title);
106
-        $this->addStylesheets(
107
-            apply_filters(
108
-                'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_css',
109
-                [
110
-                    'site_theme'       => get_stylesheet_directory_uri()
111
-                        . '/style.css?ver=' . EVENT_ESPRESSO_VERSION,
112
-                    'dashicons'        => includes_url('css/dashicons.min.css?ver=' . $wp_version),
113
-                    'espresso_default' => EE_GLOBAL_ASSETS_URL
114
-                        . 'css/espresso_default.css?ver=' . EVENT_ESPRESSO_VERSION,
115
-                ],
116
-                $this
117
-            )
118
-        );
119
-        $this->addScripts(
120
-            apply_filters(
121
-                'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_js',
122
-                [
123
-                    'jquery'        => includes_url('js/jquery/jquery.js?ver=' . $wp_version),
124
-                    'espresso_core' => EE_GLOBAL_ASSETS_URL
125
-                        . 'scripts/espresso_core.js?ver=' . EVENT_ESPRESSO_VERSION,
126
-                ],
127
-                $this
128
-            )
129
-        );
130
-        if (
131
-            apply_filters(
132
-                'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__load_default_theme_stylesheet',
133
-                false
134
-            )
135
-        ) {
136
-            $this->addStylesheets(
137
-                apply_filters(
138
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_theme_stylesheet',
139
-                    ['default_theme_stylesheet' => get_stylesheet_uri()],
140
-                    $this
141
-                )
142
-            );
143
-        }
144
-    }
145
-
146
-
147
-    /**
148
-     * @param string $title
149
-     * @throws DomainException
150
-     */
151
-    public function setTitle(string $title)
152
-    {
153
-        if (empty($title)) {
154
-            throw new DomainException(
155
-                esc_html__('You must provide a page title in order to create an iframe.', 'event_espresso')
156
-            );
157
-        }
158
-        $this->title = $title;
159
-    }
160
-
161
-
162
-    /**
163
-     * @param string $content
164
-     * @throws DomainException
165
-     */
166
-    public function setContent(string $content)
167
-    {
168
-        if (empty($content)) {
169
-            throw new DomainException(
170
-                esc_html__('You must provide content in order to create an iframe.', 'event_espresso')
171
-            );
172
-        }
173
-        $this->content = $content;
174
-    }
175
-
176
-
177
-    /**
178
-     * @param bool|int|string|null $enqueue_wp_assets
179
-     */
180
-    public function setEnqueueWpAssets($enqueue_wp_assets)
181
-    {
182
-        $this->enqueue_wp_assets = filter_var($enqueue_wp_assets, FILTER_VALIDATE_BOOLEAN);
183
-    }
184
-
185
-
186
-    /**
187
-     * @param array $stylesheets
188
-     * @throws DomainException
189
-     */
190
-    public function addStylesheets(array $stylesheets)
191
-    {
192
-        if (empty($stylesheets)) {
193
-            throw new DomainException(
194
-                esc_html__(
195
-                    'A non-empty array of URLs, is required to add a CSS stylesheet to an iframe.',
196
-                    'event_espresso'
197
-                )
198
-            );
199
-        }
200
-        foreach ($stylesheets as $handle => $stylesheet) {
201
-            $this->css[ $handle ] = $stylesheet;
202
-        }
203
-    }
204
-
205
-
206
-    /**
207
-     * Adds inline CSS styles to be printed before or after another CSS file.
208
-     *
209
-     * @param string $handle The unique identifier for the stylesheet.
210
-     * @param string $styles The CSS styles to be added inline.
211
-     * @param bool   $before Optional. Whether to print the styles before the stylesheet. Default true.
212
-     * @return void
213
-     * @since 5.0.28.p
214
-     */
215
-    public function addInlineStyles(string $handle, string $styles, bool $before = true): void
216
-    {
217
-        $handle = sanitize_key($handle);
218
-        $handle = $before ? "{$handle}_before" : "{$handle}_after";
219
-        $this->inline_styles[$handle] = str_replace(['<style>', '</style>'], '', $styles);
220
-    }
221
-
222
-
223
-    /**
224
-     * @param array $scripts
225
-     * @param bool  $add_to_header
226
-     * @throws DomainException
227
-     */
228
-    public function addScripts(array $scripts, bool $add_to_header = false)
229
-    {
230
-        if (empty($scripts)) {
231
-            throw new DomainException(
232
-                esc_html__(
233
-                    'A non-empty array of URLs, is required to add Javascript to an iframe.',
234
-                    'event_espresso'
235
-                )
236
-            );
237
-        }
238
-        foreach ($scripts as $handle => $script) {
239
-            if ($add_to_header) {
240
-                $this->header_js[ $handle ] = $script;
241
-            } else {
242
-                $this->footer_js[ $handle ] = $script;
243
-            }
244
-        }
245
-    }
246
-
247
-
248
-    /**
249
-     * @param array $script_attributes
250
-     * @param bool  $add_to_header
251
-     * @throws DomainException
252
-     */
253
-    public function addScriptAttributes(array $script_attributes, bool $add_to_header = false)
254
-    {
255
-        if (empty($script_attributes)) {
256
-            throw new DomainException(
257
-                esc_html__(
258
-                    'A non-empty array of strings, is required to add attributes to iframe Javascript.',
259
-                    'event_espresso'
260
-                )
261
-            );
262
-        }
263
-        foreach ($script_attributes as $handle => $script_attribute) {
264
-            if ($add_to_header) {
265
-                $this->header_js_attributes[ $handle ] = $script_attribute;
266
-            } else {
267
-                $this->footer_js_attributes[ $handle ] = $script_attribute;
268
-            }
269
-        }
270
-    }
271
-
272
-
273
-    /**
274
-     * @param array  $vars
275
-     * @param string $var_name
276
-     * @throws DomainException
277
-     */
278
-    public function addLocalizedVars(array $vars, string $var_name = 'eei18n')
279
-    {
280
-        if (empty($vars)) {
281
-            throw new DomainException(
282
-                esc_html__(
283
-                    'A non-empty array of vars, is required to add localized Javascript vars to an iframe.',
284
-                    'event_espresso'
285
-                )
286
-            );
287
-        }
288
-        foreach ($vars as $handle => $var) {
289
-            if ($var_name === 'eei18n') {
290
-                EE_Registry::$i18n_js_strings[ $handle ] = $var;
291
-            } elseif ($var_name === 'eeCAL' && $handle === 'espresso_calendar') {
292
-                $this->localized_vars[ $var_name ] = $var;
293
-            } else {
294
-                if (! isset($this->localized_vars[ $var_name ])) {
295
-                    $this->localized_vars[ $var_name ] = [];
296
-                }
297
-                $this->localized_vars[ $var_name ][ $handle ] = $var;
298
-            }
299
-        }
300
-    }
301
-
302
-
303
-    /**
304
-     * @param string $utm_content
305
-     * @throws DomainException
306
-     */
307
-    public function display(string $utm_content = '')
308
-    {
309
-        $this->content .= EEH_Template::powered_by_event_espresso(
310
-            '',
311
-            '',
312
-            ! empty($utm_content) ? ['utm_content' => $utm_content] : []
313
-        );
314
-        EE_System::do_not_cache();
315
-        echo $this->getTemplate();
316
-        exit;
317
-    }
318
-
319
-
320
-    /**
321
-     * @return string
322
-     * @throws DomainException
323
-     */
324
-    public function getTemplate(): string
325
-    {
326
-        return EEH_Template::display_template(
327
-            __DIR__ . DIRECTORY_SEPARATOR . 'iframe_wrapper.template.php',
328
-            [
329
-                'title'                => apply_filters(
330
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__title',
331
-                    $this->title,
332
-                    $this
333
-                ),
334
-                'content'              => apply_filters(
335
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__content',
336
-                    $this->content,
337
-                    $this
338
-                ),
339
-                'enqueue_wp_assets'    => apply_filters(
340
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__enqueue_wp_assets',
341
-                    $this->enqueue_wp_assets,
342
-                    $this
343
-                ),
344
-                'css'                  => (array) apply_filters(
345
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__css_urls',
346
-                    $this->css,
347
-                    $this
348
-                ),
349
-                'inline_styles'        => apply_filters(
350
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__styles',
351
-                    $this->inline_styles,
352
-                    $this
353
-                ),
354
-                'header_js'            => (array) apply_filters(
355
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_urls',
356
-                    $this->header_js,
357
-                    $this
358
-                ),
359
-                'header_js_attributes' => (array) apply_filters(
360
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_attributes',
361
-                    $this->header_js_attributes,
362
-                    $this
363
-                ),
364
-                'footer_js'            => (array) apply_filters(
365
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_urls',
366
-                    $this->footer_js,
367
-                    $this
368
-                ),
369
-                'footer_js_attributes' => (array) apply_filters(
370
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_attributes',
371
-                    $this->footer_js_attributes,
372
-                    $this
373
-                ),
374
-                'eei18n'               => apply_filters(
375
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__eei18n_js_strings',
376
-                    EE_Registry::localize_i18n_js_strings() . $this->localizeJsonVars(),
377
-                    $this
378
-                ),
379
-                'notices'              => EEH_Template::display_template(
380
-                    EE_TEMPLATES . 'espresso-ajax-notices.template.php',
381
-                    [],
382
-                    true
383
-                ),
384
-            ],
385
-            true,
386
-            true
387
-        );
388
-    }
389
-
390
-
391
-    /**
392
-     * localizeJsonVars
393
-     *
394
-     * @return string
395
-     */
396
-    public function localizeJsonVars(): string
397
-    {
398
-        $JSON = '';
399
-        foreach ($this->localized_vars as $var_name => $vars) {
400
-            $this->localized_vars[ $var_name ] = $this->encodeJsonVars($vars);
401
-            $JSON                              .= "/* <![CDATA[ */ var $var_name = ";
402
-            $JSON                              .= wp_json_encode($this->localized_vars[ $var_name ]);
403
-            $JSON                              .= '; /* ]]> */';
404
-        }
405
-        return $JSON;
406
-    }
407
-
408
-
409
-    /**
410
-     * @param bool|int|float|string|array $var
411
-     * @return array|string|null
412
-     */
413
-    public function encodeJsonVars($var)
414
-    {
415
-        if (is_array($var)) {
416
-            $localized_vars = [];
417
-            foreach ((array) $var as $key => $value) {
418
-                $localized_vars[ $key ] = $this->encodeJsonVars($value);
419
-            }
420
-            return $localized_vars;
421
-        }
422
-        if (is_scalar($var)) {
423
-            return html_entity_decode((string) $var, ENT_QUOTES, 'UTF-8');
424
-        }
425
-        return null;
426
-    }
20
+	/**
21
+	 * HTML for notices and ajax gif
22
+	 *
23
+	 * @var string $title
24
+	 */
25
+	protected string $title = '';
26
+
27
+	/**
28
+	 * HTML for the content being displayed
29
+	 *
30
+	 * @var string $content
31
+	 */
32
+	protected string $content = '';
33
+
34
+	/**
35
+	 * whether to call wp_head() and wp_footer()
36
+	 *
37
+	 * @var bool $enqueue_wp_assets
38
+	 */
39
+	protected bool $enqueue_wp_assets = false;
40
+
41
+	/**
42
+	 * an array of CSS URLs
43
+	 *
44
+	 * @var array $css
45
+	 */
46
+	protected array $css = [];
47
+
48
+	/**
49
+	 * an array of JS URLs to be set in the HTML header.
50
+	 *
51
+	 * @var array $header_js
52
+	 */
53
+	protected array $header_js = [];
54
+
55
+	/**
56
+	 * an array of additional attributes to be added to <script> tags for header JS
57
+	 *
58
+	 * @var array $footer_js
59
+	 */
60
+	protected array $header_js_attributes = [];
61
+
62
+	/**
63
+	 * an array of JS URLs to be displayed before the HTML </body> tag
64
+	 *
65
+	 * @var array $footer_js
66
+	 */
67
+	protected array $footer_js = [];
68
+
69
+	/**
70
+	 * an array of additional attributes to be added to <script> tags for footer JS
71
+	 *
72
+	 * @var array $footer_js_attributes
73
+	 */
74
+	protected array $footer_js_attributes = [];
75
+
76
+	/**
77
+	 * an array of JSON vars to be set in the HTML header.
78
+	 *
79
+	 * @var array $localized_vars
80
+	 */
81
+	protected array $localized_vars = [];
82
+
83
+	/**
84
+	 * array of CSS styles to be printed inline before/after another CSS file
85
+	 *
86
+	 * @var array $inline_styles
87
+	 */
88
+	protected array $inline_styles = [];
89
+
90
+
91
+	/**
92
+	 * Iframe constructor
93
+	 *
94
+	 * @param string $title
95
+	 * @param string $content
96
+	 * @throws DomainException
97
+	 */
98
+	public function __construct(string $title, string $content)
99
+	{
100
+		global $wp_version;
101
+		if (! defined('EE_IFRAME_DIR_URL')) {
102
+			define('EE_IFRAME_DIR_URL', plugin_dir_url(__FILE__));
103
+		}
104
+		$this->setContent($content);
105
+		$this->setTitle($title);
106
+		$this->addStylesheets(
107
+			apply_filters(
108
+				'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_css',
109
+				[
110
+					'site_theme'       => get_stylesheet_directory_uri()
111
+						. '/style.css?ver=' . EVENT_ESPRESSO_VERSION,
112
+					'dashicons'        => includes_url('css/dashicons.min.css?ver=' . $wp_version),
113
+					'espresso_default' => EE_GLOBAL_ASSETS_URL
114
+						. 'css/espresso_default.css?ver=' . EVENT_ESPRESSO_VERSION,
115
+				],
116
+				$this
117
+			)
118
+		);
119
+		$this->addScripts(
120
+			apply_filters(
121
+				'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_js',
122
+				[
123
+					'jquery'        => includes_url('js/jquery/jquery.js?ver=' . $wp_version),
124
+					'espresso_core' => EE_GLOBAL_ASSETS_URL
125
+						. 'scripts/espresso_core.js?ver=' . EVENT_ESPRESSO_VERSION,
126
+				],
127
+				$this
128
+			)
129
+		);
130
+		if (
131
+			apply_filters(
132
+				'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__load_default_theme_stylesheet',
133
+				false
134
+			)
135
+		) {
136
+			$this->addStylesheets(
137
+				apply_filters(
138
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_theme_stylesheet',
139
+					['default_theme_stylesheet' => get_stylesheet_uri()],
140
+					$this
141
+				)
142
+			);
143
+		}
144
+	}
145
+
146
+
147
+	/**
148
+	 * @param string $title
149
+	 * @throws DomainException
150
+	 */
151
+	public function setTitle(string $title)
152
+	{
153
+		if (empty($title)) {
154
+			throw new DomainException(
155
+				esc_html__('You must provide a page title in order to create an iframe.', 'event_espresso')
156
+			);
157
+		}
158
+		$this->title = $title;
159
+	}
160
+
161
+
162
+	/**
163
+	 * @param string $content
164
+	 * @throws DomainException
165
+	 */
166
+	public function setContent(string $content)
167
+	{
168
+		if (empty($content)) {
169
+			throw new DomainException(
170
+				esc_html__('You must provide content in order to create an iframe.', 'event_espresso')
171
+			);
172
+		}
173
+		$this->content = $content;
174
+	}
175
+
176
+
177
+	/**
178
+	 * @param bool|int|string|null $enqueue_wp_assets
179
+	 */
180
+	public function setEnqueueWpAssets($enqueue_wp_assets)
181
+	{
182
+		$this->enqueue_wp_assets = filter_var($enqueue_wp_assets, FILTER_VALIDATE_BOOLEAN);
183
+	}
184
+
185
+
186
+	/**
187
+	 * @param array $stylesheets
188
+	 * @throws DomainException
189
+	 */
190
+	public function addStylesheets(array $stylesheets)
191
+	{
192
+		if (empty($stylesheets)) {
193
+			throw new DomainException(
194
+				esc_html__(
195
+					'A non-empty array of URLs, is required to add a CSS stylesheet to an iframe.',
196
+					'event_espresso'
197
+				)
198
+			);
199
+		}
200
+		foreach ($stylesheets as $handle => $stylesheet) {
201
+			$this->css[ $handle ] = $stylesheet;
202
+		}
203
+	}
204
+
205
+
206
+	/**
207
+	 * Adds inline CSS styles to be printed before or after another CSS file.
208
+	 *
209
+	 * @param string $handle The unique identifier for the stylesheet.
210
+	 * @param string $styles The CSS styles to be added inline.
211
+	 * @param bool   $before Optional. Whether to print the styles before the stylesheet. Default true.
212
+	 * @return void
213
+	 * @since 5.0.28.p
214
+	 */
215
+	public function addInlineStyles(string $handle, string $styles, bool $before = true): void
216
+	{
217
+		$handle = sanitize_key($handle);
218
+		$handle = $before ? "{$handle}_before" : "{$handle}_after";
219
+		$this->inline_styles[$handle] = str_replace(['<style>', '</style>'], '', $styles);
220
+	}
221
+
222
+
223
+	/**
224
+	 * @param array $scripts
225
+	 * @param bool  $add_to_header
226
+	 * @throws DomainException
227
+	 */
228
+	public function addScripts(array $scripts, bool $add_to_header = false)
229
+	{
230
+		if (empty($scripts)) {
231
+			throw new DomainException(
232
+				esc_html__(
233
+					'A non-empty array of URLs, is required to add Javascript to an iframe.',
234
+					'event_espresso'
235
+				)
236
+			);
237
+		}
238
+		foreach ($scripts as $handle => $script) {
239
+			if ($add_to_header) {
240
+				$this->header_js[ $handle ] = $script;
241
+			} else {
242
+				$this->footer_js[ $handle ] = $script;
243
+			}
244
+		}
245
+	}
246
+
247
+
248
+	/**
249
+	 * @param array $script_attributes
250
+	 * @param bool  $add_to_header
251
+	 * @throws DomainException
252
+	 */
253
+	public function addScriptAttributes(array $script_attributes, bool $add_to_header = false)
254
+	{
255
+		if (empty($script_attributes)) {
256
+			throw new DomainException(
257
+				esc_html__(
258
+					'A non-empty array of strings, is required to add attributes to iframe Javascript.',
259
+					'event_espresso'
260
+				)
261
+			);
262
+		}
263
+		foreach ($script_attributes as $handle => $script_attribute) {
264
+			if ($add_to_header) {
265
+				$this->header_js_attributes[ $handle ] = $script_attribute;
266
+			} else {
267
+				$this->footer_js_attributes[ $handle ] = $script_attribute;
268
+			}
269
+		}
270
+	}
271
+
272
+
273
+	/**
274
+	 * @param array  $vars
275
+	 * @param string $var_name
276
+	 * @throws DomainException
277
+	 */
278
+	public function addLocalizedVars(array $vars, string $var_name = 'eei18n')
279
+	{
280
+		if (empty($vars)) {
281
+			throw new DomainException(
282
+				esc_html__(
283
+					'A non-empty array of vars, is required to add localized Javascript vars to an iframe.',
284
+					'event_espresso'
285
+				)
286
+			);
287
+		}
288
+		foreach ($vars as $handle => $var) {
289
+			if ($var_name === 'eei18n') {
290
+				EE_Registry::$i18n_js_strings[ $handle ] = $var;
291
+			} elseif ($var_name === 'eeCAL' && $handle === 'espresso_calendar') {
292
+				$this->localized_vars[ $var_name ] = $var;
293
+			} else {
294
+				if (! isset($this->localized_vars[ $var_name ])) {
295
+					$this->localized_vars[ $var_name ] = [];
296
+				}
297
+				$this->localized_vars[ $var_name ][ $handle ] = $var;
298
+			}
299
+		}
300
+	}
301
+
302
+
303
+	/**
304
+	 * @param string $utm_content
305
+	 * @throws DomainException
306
+	 */
307
+	public function display(string $utm_content = '')
308
+	{
309
+		$this->content .= EEH_Template::powered_by_event_espresso(
310
+			'',
311
+			'',
312
+			! empty($utm_content) ? ['utm_content' => $utm_content] : []
313
+		);
314
+		EE_System::do_not_cache();
315
+		echo $this->getTemplate();
316
+		exit;
317
+	}
318
+
319
+
320
+	/**
321
+	 * @return string
322
+	 * @throws DomainException
323
+	 */
324
+	public function getTemplate(): string
325
+	{
326
+		return EEH_Template::display_template(
327
+			__DIR__ . DIRECTORY_SEPARATOR . 'iframe_wrapper.template.php',
328
+			[
329
+				'title'                => apply_filters(
330
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__title',
331
+					$this->title,
332
+					$this
333
+				),
334
+				'content'              => apply_filters(
335
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__content',
336
+					$this->content,
337
+					$this
338
+				),
339
+				'enqueue_wp_assets'    => apply_filters(
340
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__enqueue_wp_assets',
341
+					$this->enqueue_wp_assets,
342
+					$this
343
+				),
344
+				'css'                  => (array) apply_filters(
345
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__css_urls',
346
+					$this->css,
347
+					$this
348
+				),
349
+				'inline_styles'        => apply_filters(
350
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__styles',
351
+					$this->inline_styles,
352
+					$this
353
+				),
354
+				'header_js'            => (array) apply_filters(
355
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_urls',
356
+					$this->header_js,
357
+					$this
358
+				),
359
+				'header_js_attributes' => (array) apply_filters(
360
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_attributes',
361
+					$this->header_js_attributes,
362
+					$this
363
+				),
364
+				'footer_js'            => (array) apply_filters(
365
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_urls',
366
+					$this->footer_js,
367
+					$this
368
+				),
369
+				'footer_js_attributes' => (array) apply_filters(
370
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_attributes',
371
+					$this->footer_js_attributes,
372
+					$this
373
+				),
374
+				'eei18n'               => apply_filters(
375
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__eei18n_js_strings',
376
+					EE_Registry::localize_i18n_js_strings() . $this->localizeJsonVars(),
377
+					$this
378
+				),
379
+				'notices'              => EEH_Template::display_template(
380
+					EE_TEMPLATES . 'espresso-ajax-notices.template.php',
381
+					[],
382
+					true
383
+				),
384
+			],
385
+			true,
386
+			true
387
+		);
388
+	}
389
+
390
+
391
+	/**
392
+	 * localizeJsonVars
393
+	 *
394
+	 * @return string
395
+	 */
396
+	public function localizeJsonVars(): string
397
+	{
398
+		$JSON = '';
399
+		foreach ($this->localized_vars as $var_name => $vars) {
400
+			$this->localized_vars[ $var_name ] = $this->encodeJsonVars($vars);
401
+			$JSON                              .= "/* <![CDATA[ */ var $var_name = ";
402
+			$JSON                              .= wp_json_encode($this->localized_vars[ $var_name ]);
403
+			$JSON                              .= '; /* ]]> */';
404
+		}
405
+		return $JSON;
406
+	}
407
+
408
+
409
+	/**
410
+	 * @param bool|int|float|string|array $var
411
+	 * @return array|string|null
412
+	 */
413
+	public function encodeJsonVars($var)
414
+	{
415
+		if (is_array($var)) {
416
+			$localized_vars = [];
417
+			foreach ((array) $var as $key => $value) {
418
+				$localized_vars[ $key ] = $this->encodeJsonVars($value);
419
+			}
420
+			return $localized_vars;
421
+		}
422
+		if (is_scalar($var)) {
423
+			return html_entity_decode((string) $var, ENT_QUOTES, 'UTF-8');
424
+		}
425
+		return null;
426
+	}
427 427
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
     public function __construct(string $title, string $content)
99 99
     {
100 100
         global $wp_version;
101
-        if (! defined('EE_IFRAME_DIR_URL')) {
101
+        if ( ! defined('EE_IFRAME_DIR_URL')) {
102 102
             define('EE_IFRAME_DIR_URL', plugin_dir_url(__FILE__));
103 103
         }
104 104
         $this->setContent($content);
@@ -108,10 +108,10 @@  discard block
 block discarded – undo
108 108
                 'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_css',
109 109
                 [
110 110
                     'site_theme'       => get_stylesheet_directory_uri()
111
-                        . '/style.css?ver=' . EVENT_ESPRESSO_VERSION,
112
-                    'dashicons'        => includes_url('css/dashicons.min.css?ver=' . $wp_version),
111
+                        . '/style.css?ver='.EVENT_ESPRESSO_VERSION,
112
+                    'dashicons'        => includes_url('css/dashicons.min.css?ver='.$wp_version),
113 113
                     'espresso_default' => EE_GLOBAL_ASSETS_URL
114
-                        . 'css/espresso_default.css?ver=' . EVENT_ESPRESSO_VERSION,
114
+                        . 'css/espresso_default.css?ver='.EVENT_ESPRESSO_VERSION,
115 115
                 ],
116 116
                 $this
117 117
             )
@@ -120,9 +120,9 @@  discard block
 block discarded – undo
120 120
             apply_filters(
121 121
                 'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_js',
122 122
                 [
123
-                    'jquery'        => includes_url('js/jquery/jquery.js?ver=' . $wp_version),
123
+                    'jquery'        => includes_url('js/jquery/jquery.js?ver='.$wp_version),
124 124
                     'espresso_core' => EE_GLOBAL_ASSETS_URL
125
-                        . 'scripts/espresso_core.js?ver=' . EVENT_ESPRESSO_VERSION,
125
+                        . 'scripts/espresso_core.js?ver='.EVENT_ESPRESSO_VERSION,
126 126
                 ],
127 127
                 $this
128 128
             )
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
             );
199 199
         }
200 200
         foreach ($stylesheets as $handle => $stylesheet) {
201
-            $this->css[ $handle ] = $stylesheet;
201
+            $this->css[$handle] = $stylesheet;
202 202
         }
203 203
     }
204 204
 
@@ -237,9 +237,9 @@  discard block
 block discarded – undo
237 237
         }
238 238
         foreach ($scripts as $handle => $script) {
239 239
             if ($add_to_header) {
240
-                $this->header_js[ $handle ] = $script;
240
+                $this->header_js[$handle] = $script;
241 241
             } else {
242
-                $this->footer_js[ $handle ] = $script;
242
+                $this->footer_js[$handle] = $script;
243 243
             }
244 244
         }
245 245
     }
@@ -262,9 +262,9 @@  discard block
 block discarded – undo
262 262
         }
263 263
         foreach ($script_attributes as $handle => $script_attribute) {
264 264
             if ($add_to_header) {
265
-                $this->header_js_attributes[ $handle ] = $script_attribute;
265
+                $this->header_js_attributes[$handle] = $script_attribute;
266 266
             } else {
267
-                $this->footer_js_attributes[ $handle ] = $script_attribute;
267
+                $this->footer_js_attributes[$handle] = $script_attribute;
268 268
             }
269 269
         }
270 270
     }
@@ -287,14 +287,14 @@  discard block
 block discarded – undo
287 287
         }
288 288
         foreach ($vars as $handle => $var) {
289 289
             if ($var_name === 'eei18n') {
290
-                EE_Registry::$i18n_js_strings[ $handle ] = $var;
290
+                EE_Registry::$i18n_js_strings[$handle] = $var;
291 291
             } elseif ($var_name === 'eeCAL' && $handle === 'espresso_calendar') {
292
-                $this->localized_vars[ $var_name ] = $var;
292
+                $this->localized_vars[$var_name] = $var;
293 293
             } else {
294
-                if (! isset($this->localized_vars[ $var_name ])) {
295
-                    $this->localized_vars[ $var_name ] = [];
294
+                if ( ! isset($this->localized_vars[$var_name])) {
295
+                    $this->localized_vars[$var_name] = [];
296 296
                 }
297
-                $this->localized_vars[ $var_name ][ $handle ] = $var;
297
+                $this->localized_vars[$var_name][$handle] = $var;
298 298
             }
299 299
         }
300 300
     }
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
     public function getTemplate(): string
325 325
     {
326 326
         return EEH_Template::display_template(
327
-            __DIR__ . DIRECTORY_SEPARATOR . 'iframe_wrapper.template.php',
327
+            __DIR__.DIRECTORY_SEPARATOR.'iframe_wrapper.template.php',
328 328
             [
329 329
                 'title'                => apply_filters(
330 330
                     'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__title',
@@ -373,11 +373,11 @@  discard block
 block discarded – undo
373 373
                 ),
374 374
                 'eei18n'               => apply_filters(
375 375
                     'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__eei18n_js_strings',
376
-                    EE_Registry::localize_i18n_js_strings() . $this->localizeJsonVars(),
376
+                    EE_Registry::localize_i18n_js_strings().$this->localizeJsonVars(),
377 377
                     $this
378 378
                 ),
379 379
                 'notices'              => EEH_Template::display_template(
380
-                    EE_TEMPLATES . 'espresso-ajax-notices.template.php',
380
+                    EE_TEMPLATES.'espresso-ajax-notices.template.php',
381 381
                     [],
382 382
                     true
383 383
                 ),
@@ -397,9 +397,9 @@  discard block
 block discarded – undo
397 397
     {
398 398
         $JSON = '';
399 399
         foreach ($this->localized_vars as $var_name => $vars) {
400
-            $this->localized_vars[ $var_name ] = $this->encodeJsonVars($vars);
400
+            $this->localized_vars[$var_name] = $this->encodeJsonVars($vars);
401 401
             $JSON                              .= "/* <![CDATA[ */ var $var_name = ";
402
-            $JSON                              .= wp_json_encode($this->localized_vars[ $var_name ]);
402
+            $JSON                              .= wp_json_encode($this->localized_vars[$var_name]);
403 403
             $JSON                              .= '; /* ]]> */';
404 404
         }
405 405
         return $JSON;
@@ -415,7 +415,7 @@  discard block
 block discarded – undo
415 415
         if (is_array($var)) {
416 416
             $localized_vars = [];
417 417
             foreach ((array) $var as $key => $value) {
418
-                $localized_vars[ $key ] = $this->encodeJsonVars($value);
418
+                $localized_vars[$key] = $this->encodeJsonVars($value);
419 419
             }
420 420
             return $localized_vars;
421 421
         }
Please login to merge, or discard this patch.