Completed
Branch dev (59c1f4)
by
unknown
07:23 queued 05:02
created
core/domain/services/admin/events/data/PreviewDeletion.php 1 patch
Indentation   +116 added lines, -116 removed lines patch added patch discarded remove patch
@@ -31,127 +31,127 @@
 block discarded – undo
31 31
  */
32 32
 class PreviewDeletion
33 33
 {
34
-    /**
35
-     * @var NodeGroupDao
36
-     */
37
-    protected $dao;
34
+	/**
35
+	 * @var NodeGroupDao
36
+	 */
37
+	protected $dao;
38 38
 
39
-    /**
40
-     * @var EEM_Event
41
-     */
42
-    protected $event_model;
39
+	/**
40
+	 * @var EEM_Event
41
+	 */
42
+	protected $event_model;
43 43
 
44
-    /**
45
-     * @var EEM_Datetime
46
-     */
47
-    protected $datetime_model;
44
+	/**
45
+	 * @var EEM_Datetime
46
+	 */
47
+	protected $datetime_model;
48 48
 
49
-    /**
50
-     * @var EEM_Registration
51
-     */
52
-    protected $registration_model;
49
+	/**
50
+	 * @var EEM_Registration
51
+	 */
52
+	protected $registration_model;
53 53
 
54
-    /**
55
-     * PreviewDeletion constructor.
56
-     * @param NodeGroupDao $dao
57
-     * @param EEM_Event $event_model
58
-     * @param EEM_Datetime $datetime_model
59
-     * @param EEM_Registration $registration_model
60
-     */
61
-    public function __construct(
62
-        NodeGroupDao $dao,
63
-        EEM_Event $event_model,
64
-        EEM_Datetime $datetime_model,
65
-        EEM_Registration $registration_model
66
-    ) {
67
-        $this->dao = $dao;
68
-        $this->event_model = $event_model;
69
-        $this->datetime_model = $datetime_model;
70
-        $this->registration_model = $registration_model;
71
-    }
54
+	/**
55
+	 * PreviewDeletion constructor.
56
+	 * @param NodeGroupDao $dao
57
+	 * @param EEM_Event $event_model
58
+	 * @param EEM_Datetime $datetime_model
59
+	 * @param EEM_Registration $registration_model
60
+	 */
61
+	public function __construct(
62
+		NodeGroupDao $dao,
63
+		EEM_Event $event_model,
64
+		EEM_Datetime $datetime_model,
65
+		EEM_Registration $registration_model
66
+	) {
67
+		$this->dao = $dao;
68
+		$this->event_model = $event_model;
69
+		$this->datetime_model = $datetime_model;
70
+		$this->registration_model = $registration_model;
71
+	}
72 72
 
73
-    /**
74
-     * Renders the preview deletion page.
75
-     * @since 4.10.12.p
76
-     * @param $request_data
77
-     * @param $admin_base_url
78
-     * @return array
79
-     * @throws UnexpectedEntityException
80
-     * @throws DomainException
81
-     * @throws EE_Error
82
-     * @throws InvalidDataTypeException
83
-     * @throws InvalidInterfaceException
84
-     * @throws InvalidArgumentException
85
-     * @throws ReflectionException
86
-     */
87
-    public function handle($request_data, $admin_base_url)
88
-    {
89
-        $deletion_job_code = isset($request_data['deletion_job_code']) ? sanitize_key($request_data['deletion_job_code']) : '';
90
-        $models_and_ids_to_delete = $this->dao->getModelsAndIdsFromGroup($deletion_job_code);
91
-        $event_ids = isset($models_and_ids_to_delete['Event']) ? $models_and_ids_to_delete['Event'] : array();
92
-        if (empty($event_ids) || !is_array($event_ids)) {
93
-            throw new EE_Error(
94
-                esc_html__('No Events were found to delete.', 'event_espresso')
95
-            );
96
-        }
97
-        $datetime_ids = isset($models_and_ids_to_delete['Datetime']) ? $models_and_ids_to_delete['Datetime'] : array();
98
-        if (!is_array($datetime_ids)) {
99
-            throw new UnexpectedEntityException($datetime_ids, 'array');
100
-        }
101
-        $registration_ids = isset($models_and_ids_to_delete['Registration']) ? $models_and_ids_to_delete['Registration'] : array();
102
-        if (!is_array($registration_ids)) {
103
-            throw new UnexpectedEntityException($registration_ids, 'array');
104
-        }
105
-        $num_registrations_to_show = 10;
106
-        $reg_count = count($registration_ids);
107
-        if ($reg_count > $num_registrations_to_show) {
108
-            $registration_ids = array_slice($registration_ids, 0, $num_registrations_to_show);
109
-        }
110
-        $form = new ConfirmEventDeletionForm($event_ids);
111
-        $events = $this->event_model->get_all_deleted_and_undeleted(
112
-            [
113
-                [
114
-                    'EVT_ID' => ['IN', $event_ids]
115
-                ]
116
-            ]
117
-        );
118
-        $datetimes = $this->datetime_model->get_all_deleted_and_undeleted(
119
-            [
120
-                [
121
-                    'DTT_ID' => ['IN', $datetime_ids]
122
-                ]
123
-            ]
124
-        );
125
-        $registrations = $this->registration_model->get_all_deleted_and_undeleted(
126
-            [
127
-                [
128
-                    'REG_ID' => ['IN', $registration_ids]
129
-                ]
130
-            ]
131
-        );
132
-        $confirm_deletion_args = [
133
-            'action' => 'confirm_deletion',
134
-            'deletion_job_code' => $deletion_job_code
135
-        ];
136
-        return [
137
-            'admin_page_content' => EEH_Template::display_template(
138
-                EVENTS_TEMPLATE_PATH . 'event_preview_deletion.template.php',
139
-                [
140
-                    'form_url' => EE_Admin_Page::add_query_args_and_nonce(
141
-                        $confirm_deletion_args,
142
-                        $admin_base_url
143
-                    ),
144
-                    'form' => $form,
145
-                    'events' => $events,
146
-                    'datetimes' => $datetimes,
147
-                    'registrations' => $registrations,
148
-                    'reg_count' => $reg_count,
149
-                    'num_registrations_to_show' => $num_registrations_to_show
150
-                ],
151
-                true
152
-            )
153
-        ];
154
-    }
73
+	/**
74
+	 * Renders the preview deletion page.
75
+	 * @since 4.10.12.p
76
+	 * @param $request_data
77
+	 * @param $admin_base_url
78
+	 * @return array
79
+	 * @throws UnexpectedEntityException
80
+	 * @throws DomainException
81
+	 * @throws EE_Error
82
+	 * @throws InvalidDataTypeException
83
+	 * @throws InvalidInterfaceException
84
+	 * @throws InvalidArgumentException
85
+	 * @throws ReflectionException
86
+	 */
87
+	public function handle($request_data, $admin_base_url)
88
+	{
89
+		$deletion_job_code = isset($request_data['deletion_job_code']) ? sanitize_key($request_data['deletion_job_code']) : '';
90
+		$models_and_ids_to_delete = $this->dao->getModelsAndIdsFromGroup($deletion_job_code);
91
+		$event_ids = isset($models_and_ids_to_delete['Event']) ? $models_and_ids_to_delete['Event'] : array();
92
+		if (empty($event_ids) || !is_array($event_ids)) {
93
+			throw new EE_Error(
94
+				esc_html__('No Events were found to delete.', 'event_espresso')
95
+			);
96
+		}
97
+		$datetime_ids = isset($models_and_ids_to_delete['Datetime']) ? $models_and_ids_to_delete['Datetime'] : array();
98
+		if (!is_array($datetime_ids)) {
99
+			throw new UnexpectedEntityException($datetime_ids, 'array');
100
+		}
101
+		$registration_ids = isset($models_and_ids_to_delete['Registration']) ? $models_and_ids_to_delete['Registration'] : array();
102
+		if (!is_array($registration_ids)) {
103
+			throw new UnexpectedEntityException($registration_ids, 'array');
104
+		}
105
+		$num_registrations_to_show = 10;
106
+		$reg_count = count($registration_ids);
107
+		if ($reg_count > $num_registrations_to_show) {
108
+			$registration_ids = array_slice($registration_ids, 0, $num_registrations_to_show);
109
+		}
110
+		$form = new ConfirmEventDeletionForm($event_ids);
111
+		$events = $this->event_model->get_all_deleted_and_undeleted(
112
+			[
113
+				[
114
+					'EVT_ID' => ['IN', $event_ids]
115
+				]
116
+			]
117
+		);
118
+		$datetimes = $this->datetime_model->get_all_deleted_and_undeleted(
119
+			[
120
+				[
121
+					'DTT_ID' => ['IN', $datetime_ids]
122
+				]
123
+			]
124
+		);
125
+		$registrations = $this->registration_model->get_all_deleted_and_undeleted(
126
+			[
127
+				[
128
+					'REG_ID' => ['IN', $registration_ids]
129
+				]
130
+			]
131
+		);
132
+		$confirm_deletion_args = [
133
+			'action' => 'confirm_deletion',
134
+			'deletion_job_code' => $deletion_job_code
135
+		];
136
+		return [
137
+			'admin_page_content' => EEH_Template::display_template(
138
+				EVENTS_TEMPLATE_PATH . 'event_preview_deletion.template.php',
139
+				[
140
+					'form_url' => EE_Admin_Page::add_query_args_and_nonce(
141
+						$confirm_deletion_args,
142
+						$admin_base_url
143
+					),
144
+					'form' => $form,
145
+					'events' => $events,
146
+					'datetimes' => $datetimes,
147
+					'registrations' => $registrations,
148
+					'reg_count' => $reg_count,
149
+					'num_registrations_to_show' => $num_registrations_to_show
150
+				],
151
+				true
152
+			)
153
+		];
154
+	}
155 155
 }
156 156
 // End of file PreviewDeletion.php
157 157
 // Location: EventEspresso\core\domain\services\admin\events\data/PreviewDeletion.php
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/PreviewEventDeletion.php 1 patch
Indentation   +214 added lines, -214 removed lines patch added patch discarded remove patch
@@ -37,149 +37,149 @@  discard block
 block discarded – undo
37 37
 class PreviewEventDeletion extends JobHandler
38 38
 {
39 39
 
40
-    /**
41
-     * @var NodeGroupDao
42
-     */
43
-    protected $model_obj_node_group_persister;
40
+	/**
41
+	 * @var NodeGroupDao
42
+	 */
43
+	protected $model_obj_node_group_persister;
44 44
 
45
-    public function __construct(NodeGroupDao $model_obj_node_group_persister)
46
-    {
47
-        $this->model_obj_node_group_persister = $model_obj_node_group_persister;
48
-    }
45
+	public function __construct(NodeGroupDao $model_obj_node_group_persister)
46
+	{
47
+		$this->model_obj_node_group_persister = $model_obj_node_group_persister;
48
+	}
49 49
 
50
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
50
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
51 51
 
52
-    /**
53
-     *
54
-     * @param JobParameters $job_parameters
55
-     * @return JobStepResponse
56
-     * @throws EE_Error
57
-     * @throws InvalidDataTypeException
58
-     * @throws InvalidInterfaceException
59
-     * @throws InvalidArgumentException
60
-     * @throws ReflectionException
61
-     */
62
-    public function create_job(JobParameters $job_parameters)
63
-    {
64
-        // Set the "root" model objects we will want to delete (record their ID and model)
65
-        $event_ids = $job_parameters->request_datum('EVT_IDs', array());
66
-        // Find all the root nodes to delete (this isn't just events, because there's other data, like related tickets,
67
-        // prices, message templates, etc, whose model definition doesn't make them dependent on events. But,
68
-        // we have no UI to access them independent of events, so they may as well get deleted too.)
69
-        $roots = [];
70
-        foreach ($event_ids as $event_id) {
71
-            $roots[] = new ModelObjNode(
72
-                $event_id,
73
-                EEM_Event::instance()
74
-            );
75
-            // Also, we want to delete their related, non-global, tickets, prices and message templates
76
-            $related_non_global_tickets = EEM_Ticket::instance()->get_all_deleted_and_undeleted(
77
-                [
78
-                    [
79
-                        'TKT_is_default' => false,
80
-                        'Datetime.EVT_ID' => $event_id
81
-                    ]
82
-                ]
83
-            );
84
-            foreach ($related_non_global_tickets as $ticket) {
85
-                $roots[] = new ModelObjNode(
86
-                    $ticket->ID(),
87
-                    $ticket->get_model(),
88
-                    ['Registration']
89
-                );
90
-            }
91
-            $related_non_global_prices = EEM_Price::instance()->get_all_deleted_and_undeleted(
92
-                [
93
-                    [
94
-                        'PRC_is_default' => false,
95
-                        'Ticket.Datetime.EVT_ID' => $event_id
96
-                    ]
97
-                ]
98
-            );
99
-            foreach ($related_non_global_prices as $price) {
100
-                $roots[] = new ModelObjNode(
101
-                    $price->ID(),
102
-                    $price->get_model()
103
-                );
104
-            }
105
-        }
106
-        $transactions_ids = $this->getTransactionsToDelete($event_ids);
107
-        foreach ($transactions_ids as $transaction_id) {
108
-            $roots[] = new ModelObjNode(
109
-                $transaction_id,
110
-                EEM_Transaction::instance(),
111
-                ['Registration']
112
-            );
113
-        }
114
-        $job_parameters->add_extra_data('roots', $roots);
115
-        // Set an estimate of how long this will take (we're discovering as we go, so it seems impossible to give
116
-        // an accurate count.)
117
-        $estimated_work_per_model_obj = 10;
118
-        $count_regs = EEM_Registration::instance()->count(
119
-            [
120
-                [
121
-                    'EVT_ID' => ['IN', $event_ids]
122
-                ]
123
-            ]
124
-        );
125
-        $job_parameters->set_job_size((count($roots) + $count_regs) * $estimated_work_per_model_obj);
126
-        return new JobStepResponse(
127
-            $job_parameters,
128
-            esc_html__('Generating preview of data to be deleted...', 'event_espresso')
129
-        );
130
-    }
52
+	/**
53
+	 *
54
+	 * @param JobParameters $job_parameters
55
+	 * @return JobStepResponse
56
+	 * @throws EE_Error
57
+	 * @throws InvalidDataTypeException
58
+	 * @throws InvalidInterfaceException
59
+	 * @throws InvalidArgumentException
60
+	 * @throws ReflectionException
61
+	 */
62
+	public function create_job(JobParameters $job_parameters)
63
+	{
64
+		// Set the "root" model objects we will want to delete (record their ID and model)
65
+		$event_ids = $job_parameters->request_datum('EVT_IDs', array());
66
+		// Find all the root nodes to delete (this isn't just events, because there's other data, like related tickets,
67
+		// prices, message templates, etc, whose model definition doesn't make them dependent on events. But,
68
+		// we have no UI to access them independent of events, so they may as well get deleted too.)
69
+		$roots = [];
70
+		foreach ($event_ids as $event_id) {
71
+			$roots[] = new ModelObjNode(
72
+				$event_id,
73
+				EEM_Event::instance()
74
+			);
75
+			// Also, we want to delete their related, non-global, tickets, prices and message templates
76
+			$related_non_global_tickets = EEM_Ticket::instance()->get_all_deleted_and_undeleted(
77
+				[
78
+					[
79
+						'TKT_is_default' => false,
80
+						'Datetime.EVT_ID' => $event_id
81
+					]
82
+				]
83
+			);
84
+			foreach ($related_non_global_tickets as $ticket) {
85
+				$roots[] = new ModelObjNode(
86
+					$ticket->ID(),
87
+					$ticket->get_model(),
88
+					['Registration']
89
+				);
90
+			}
91
+			$related_non_global_prices = EEM_Price::instance()->get_all_deleted_and_undeleted(
92
+				[
93
+					[
94
+						'PRC_is_default' => false,
95
+						'Ticket.Datetime.EVT_ID' => $event_id
96
+					]
97
+				]
98
+			);
99
+			foreach ($related_non_global_prices as $price) {
100
+				$roots[] = new ModelObjNode(
101
+					$price->ID(),
102
+					$price->get_model()
103
+				);
104
+			}
105
+		}
106
+		$transactions_ids = $this->getTransactionsToDelete($event_ids);
107
+		foreach ($transactions_ids as $transaction_id) {
108
+			$roots[] = new ModelObjNode(
109
+				$transaction_id,
110
+				EEM_Transaction::instance(),
111
+				['Registration']
112
+			);
113
+		}
114
+		$job_parameters->add_extra_data('roots', $roots);
115
+		// Set an estimate of how long this will take (we're discovering as we go, so it seems impossible to give
116
+		// an accurate count.)
117
+		$estimated_work_per_model_obj = 10;
118
+		$count_regs = EEM_Registration::instance()->count(
119
+			[
120
+				[
121
+					'EVT_ID' => ['IN', $event_ids]
122
+				]
123
+			]
124
+		);
125
+		$job_parameters->set_job_size((count($roots) + $count_regs) * $estimated_work_per_model_obj);
126
+		return new JobStepResponse(
127
+			$job_parameters,
128
+			esc_html__('Generating preview of data to be deleted...', 'event_espresso')
129
+		);
130
+	}
131 131
 
132
-    /**
133
-     * @since 4.10.12.p
134
-     * @param EE_Base_Class[] $model_objs
135
-     * @param array $dont_traverse_models
136
-     * @return array
137
-     * @throws EE_Error
138
-     * @throws InvalidArgumentException
139
-     * @throws InvalidDataTypeException
140
-     * @throws InvalidInterfaceException
141
-     * @throws ReflectionException
142
-     */
143
-    protected function createModelObjNodes($model_objs, array $dont_traverse_models = [])
144
-    {
145
-        $nodes = [];
146
-        foreach ($model_objs as $model_obj) {
147
-            $nodes[] = new ModelObjNode(
148
-                $model_obj->ID(),
149
-                $model_obj->get_model(),
150
-                $dont_traverse_models
151
-            );
152
-        }
153
-        return $nodes;
154
-    }
132
+	/**
133
+	 * @since 4.10.12.p
134
+	 * @param EE_Base_Class[] $model_objs
135
+	 * @param array $dont_traverse_models
136
+	 * @return array
137
+	 * @throws EE_Error
138
+	 * @throws InvalidArgumentException
139
+	 * @throws InvalidDataTypeException
140
+	 * @throws InvalidInterfaceException
141
+	 * @throws ReflectionException
142
+	 */
143
+	protected function createModelObjNodes($model_objs, array $dont_traverse_models = [])
144
+	{
145
+		$nodes = [];
146
+		foreach ($model_objs as $model_obj) {
147
+			$nodes[] = new ModelObjNode(
148
+				$model_obj->ID(),
149
+				$model_obj->get_model(),
150
+				$dont_traverse_models
151
+			);
152
+		}
153
+		return $nodes;
154
+	}
155 155
 
156
-    /**
157
-     * Gets all the transactions related to these events that aren't related to other events. They'll be deleted too.
158
-     * (Ones that are related to other events can stay around until those other events are deleted too.)
159
-     * @since 4.10.12.p
160
-     * @param $event_ids
161
-     * @return array of transaction IDs
162
-     */
163
-    protected function getTransactionsToDelete($event_ids)
164
-    {
165
-        if (empty($event_ids)) {
166
-            return [];
167
-        }
168
-        global $wpdb;
169
-        $event_ids = array_map(
170
-            'intval',
171
-            $event_ids
172
-        );
173
-        $imploded_sanitized_event_ids = implode(',', $event_ids);
174
-        // Select transactions with registrations for the events $event_ids which also don't have registrations
175
-        // for any events NOT in $event_ids.
176
-        // Notice the outer query searched for transactions whose registrations ARE in $event_ids,
177
-        // whereas the inner query checks if the outer query's transaction has any registrations that are
178
-        // NOT IN $event_ids (ie, don't have registrations for events we're not just about to delete.)
179
-        return array_map(
180
-            'intval',
181
-            $wpdb->get_col(
182
-                "SELECT
156
+	/**
157
+	 * Gets all the transactions related to these events that aren't related to other events. They'll be deleted too.
158
+	 * (Ones that are related to other events can stay around until those other events are deleted too.)
159
+	 * @since 4.10.12.p
160
+	 * @param $event_ids
161
+	 * @return array of transaction IDs
162
+	 */
163
+	protected function getTransactionsToDelete($event_ids)
164
+	{
165
+		if (empty($event_ids)) {
166
+			return [];
167
+		}
168
+		global $wpdb;
169
+		$event_ids = array_map(
170
+			'intval',
171
+			$event_ids
172
+		);
173
+		$imploded_sanitized_event_ids = implode(',', $event_ids);
174
+		// Select transactions with registrations for the events $event_ids which also don't have registrations
175
+		// for any events NOT in $event_ids.
176
+		// Notice the outer query searched for transactions whose registrations ARE in $event_ids,
177
+		// whereas the inner query checks if the outer query's transaction has any registrations that are
178
+		// NOT IN $event_ids (ie, don't have registrations for events we're not just about to delete.)
179
+		return array_map(
180
+			'intval',
181
+			$wpdb->get_col(
182
+				"SELECT
183 183
                       DISTINCT t.TXN_ID
184 184
                     FROM
185 185
                       {$wpdb->prefix}esp_transaction t INNER JOIN
@@ -197,84 +197,84 @@  discard block
 block discarded – undo
197 197
                            tsub.TXN_ID=t.TXN_ID AND
198 198
                            rsub.EVT_ID NOT IN ({$imploded_sanitized_event_ids})
199 199
                        )"
200
-            )
201
-        );
202
-    }
200
+			)
201
+		);
202
+	}
203 203
 
204
-    /**
205
-     * Performs another step of the job
206
-     * @param JobParameters $job_parameters
207
-     * @param int $batch_size
208
-     * @return JobStepResponse
209
-     * @throws BatchRequestException
210
-     */
211
-    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
212
-    {
213
-        // Serializing and unserializing is what really makes this drag on (eg on localhost, the ajax requests took
214
-        // about 4 seconds when the batch size was 250, but 3 seconds when the batch size was 50. So like
215
-        // 50% of the request is just serializing and unserializing.) So, make the batches much bigger.
216
-        $batch_size *= 3;
217
-        $units_processed = 0;
218
-        foreach ($job_parameters->extra_datum('roots', array()) as $root_node) {
219
-            if ($units_processed >= $batch_size) {
220
-                break;
221
-            }
222
-            if (!$root_node instanceof ModelObjNode) {
223
-                throw new InvalidClassException('ModelObjNode');
224
-            }
225
-            if ($root_node->isComplete()) {
226
-                continue;
227
-            }
228
-            $units_processed += $root_node->visit($batch_size - $units_processed);
229
-        }
230
-        $job_parameters->mark_processed($units_processed);
231
-        // If the most-recently processed root node is complete, we must be all done because we're doing them
232
-        // sequentially.
233
-        if (isset($root_node) && $root_node instanceof ModelObjNode && $root_node->isComplete()) {
234
-            $job_parameters->set_status(JobParameters::status_complete);
235
-            // Show a full progress bar.
236
-            $job_parameters->set_units_processed($job_parameters->job_size());
237
-            $deletion_job_code = $job_parameters->request_datum('deletion_job_code');
238
-            $this->model_obj_node_group_persister->persistModelObjNodesGroup(
239
-                $job_parameters->extra_datum('roots'),
240
-                $deletion_job_code
241
-            );
242
-            return new JobStepResponse(
243
-                $job_parameters,
244
-                esc_html__('Finished identifying items for deletion.', 'event_espresso'),
245
-                [
246
-                    'deletion_job_code' => $deletion_job_code
247
-                ]
248
-            );
249
-        } else {
250
-            // Because the job size was a guess, it may have likely been provden wrong. We don't want to show more work
251
-            // done than we originally said there would be. So adjust the estimate.
252
-            if (($job_parameters->units_processed() / $job_parameters->job_size()) > .8) {
253
-                $job_parameters->set_job_size($job_parameters->job_size() * 2);
254
-            }
255
-            return new JobStepResponse(
256
-                $job_parameters,
257
-                sprintf(
258
-                    esc_html__('Identified %d items for deletion.', 'event_espresso'),
259
-                    $units_processed
260
-                )
261
-            );
262
-        }
263
-    }
204
+	/**
205
+	 * Performs another step of the job
206
+	 * @param JobParameters $job_parameters
207
+	 * @param int $batch_size
208
+	 * @return JobStepResponse
209
+	 * @throws BatchRequestException
210
+	 */
211
+	public function continue_job(JobParameters $job_parameters, $batch_size = 50)
212
+	{
213
+		// Serializing and unserializing is what really makes this drag on (eg on localhost, the ajax requests took
214
+		// about 4 seconds when the batch size was 250, but 3 seconds when the batch size was 50. So like
215
+		// 50% of the request is just serializing and unserializing.) So, make the batches much bigger.
216
+		$batch_size *= 3;
217
+		$units_processed = 0;
218
+		foreach ($job_parameters->extra_datum('roots', array()) as $root_node) {
219
+			if ($units_processed >= $batch_size) {
220
+				break;
221
+			}
222
+			if (!$root_node instanceof ModelObjNode) {
223
+				throw new InvalidClassException('ModelObjNode');
224
+			}
225
+			if ($root_node->isComplete()) {
226
+				continue;
227
+			}
228
+			$units_processed += $root_node->visit($batch_size - $units_processed);
229
+		}
230
+		$job_parameters->mark_processed($units_processed);
231
+		// If the most-recently processed root node is complete, we must be all done because we're doing them
232
+		// sequentially.
233
+		if (isset($root_node) && $root_node instanceof ModelObjNode && $root_node->isComplete()) {
234
+			$job_parameters->set_status(JobParameters::status_complete);
235
+			// Show a full progress bar.
236
+			$job_parameters->set_units_processed($job_parameters->job_size());
237
+			$deletion_job_code = $job_parameters->request_datum('deletion_job_code');
238
+			$this->model_obj_node_group_persister->persistModelObjNodesGroup(
239
+				$job_parameters->extra_datum('roots'),
240
+				$deletion_job_code
241
+			);
242
+			return new JobStepResponse(
243
+				$job_parameters,
244
+				esc_html__('Finished identifying items for deletion.', 'event_espresso'),
245
+				[
246
+					'deletion_job_code' => $deletion_job_code
247
+				]
248
+			);
249
+		} else {
250
+			// Because the job size was a guess, it may have likely been provden wrong. We don't want to show more work
251
+			// done than we originally said there would be. So adjust the estimate.
252
+			if (($job_parameters->units_processed() / $job_parameters->job_size()) > .8) {
253
+				$job_parameters->set_job_size($job_parameters->job_size() * 2);
254
+			}
255
+			return new JobStepResponse(
256
+				$job_parameters,
257
+				sprintf(
258
+					esc_html__('Identified %d items for deletion.', 'event_espresso'),
259
+					$units_processed
260
+				)
261
+			);
262
+		}
263
+	}
264 264
 
265
-    /**
266
-     * Performs any clean-up logic when we know the job is completed
267
-     * @param JobParameters $job_parameters
268
-     * @return JobStepResponse
269
-     */
270
-    public function cleanup_job(JobParameters $job_parameters)
271
-    {
272
-        // Nothing much to do. We can't delete the option with the built tree because we may need it in a moment for the deletion
273
-        return new JobStepResponse(
274
-            $job_parameters,
275
-            esc_html__('All done', 'event_espresso')
276
-        );
277
-    }
265
+	/**
266
+	 * Performs any clean-up logic when we know the job is completed
267
+	 * @param JobParameters $job_parameters
268
+	 * @return JobStepResponse
269
+	 */
270
+	public function cleanup_job(JobParameters $job_parameters)
271
+	{
272
+		// Nothing much to do. We can't delete the option with the built tree because we may need it in a moment for the deletion
273
+		return new JobStepResponse(
274
+			$job_parameters,
275
+			esc_html__('All done', 'event_espresso')
276
+		);
277
+	}
278 278
 }
279 279
 // End of file EventDeletion.php
280 280
 // Location: EventEspressoBatchRequest\JobHandlers/EventDeletion.php
Please login to merge, or discard this patch.
libraries/messages/data_class/EE_Messages_Preview_incoming_data.class.php 1 patch
Indentation   +580 added lines, -580 removed lines patch added patch discarded remove patch
@@ -18,584 +18,584 @@
 block discarded – undo
18 18
 class EE_Messages_Preview_incoming_data extends EE_Messages_incoming_data
19 19
 {
20 20
 
21
-    // some specific properties we need for this class
22
-    private $_events = array();
23
-    private $_attendees = array();
24
-    private $_registrations = array();
25
-
26
-
27
-    /**
28
-     * For the constructor of this special preview class.  We're either looking for an event id or empty data.  If we
29
-     * have an event id (or ids) then we'll use that as the source for the "dummy" data.  If the data is empty then
30
-     * we'll get the first three published events from the users database and use that as a source.
31
-     *
32
-     * @param array $data
33
-     */
34
-    public function __construct($data = array())
35
-    {
36
-        $this->_data = isset($data['event_ids']) ? $data['event_ids'] : array();
37
-        $this->_setup_attendees_events();
38
-        parent::__construct($data);
39
-    }
40
-
41
-
42
-    /**
43
-     * Returns database safe representation of the data later used to when instantiating this object.
44
-     *
45
-     * @param array $data The incoming data to be prepped.
46
-     *
47
-     * @return array   The prepped data for db
48
-     */
49
-    public static function convert_data_for_persistent_storage($data)
50
-    {
51
-        return $data;
52
-    }
53
-
54
-
55
-    /**
56
-     * Data that has been stored in persistent storage that was prepped by _convert_data_for_persistent_storage
57
-     * can be sent into this method and converted back into the format used for instantiating with this data handler.
58
-     *
59
-     * @param array $data
60
-     *
61
-     * @return array
62
-     */
63
-    public static function convert_data_from_persistent_storage($data)
64
-    {
65
-        return $data;
66
-    }
67
-
68
-
69
-    /**
70
-     * This will just setup the _events property in the expected format.
71
-     *
72
-     * @throws \EE_Error
73
-     */
74
-    private function _setup_attendees_events()
75
-    {
76
-
77
-        // setup some attendee objects
78
-        $attendees = $this->_get_some_attendees();
79
-
80
-        // if empty $data we'll do a query to get some events from the server. otherwise we'll retrieve the event data for the given ids.
81
-        $events = $this->_get_some_events($this->_data);
82
-
83
-        $answers_n_questions = $this->_get_some_q_and_as();
84
-
85
-        if (count($events) < 1) {
86
-            throw new EE_Error(__(
87
-                'We can\'t generate a preview for you because there are no active events in your database',
88
-                'event_espresso'
89
-            ));
90
-        }
91
-
92
-
93
-        // now let's loop and set up the _events property.  At the same time we'll set up attendee properties.
94
-
95
-
96
-        // we'll actually use the generated line_item identifiers for our loop
97
-        $dtts = $tkts = array();
98
-        foreach ($events as $id => $event) {
99
-            if (! $event instanceof EE_Event) {
100
-                continue;
101
-            }
102
-            $this->_events[ $id ]['ID']       = $id;
103
-            $this->_events[ $id ]['name']     = $event->get('EVT_name');
104
-            $datetime                       = $event->get_first_related('Datetime');
105
-            $tickets                        = $datetime instanceof EE_Datetime ? $datetime->get_many_related(
106
-                'Ticket',
107
-                array('default_where_conditions' => 'none')
108
-            ) : array();
109
-            $this->_events[ $id ]['event']    = $event;
110
-            $this->_events[ $id ]['reg_objs'] = array();
111
-            $this->_events[ $id ]['tkt_objs'] = $tickets;
112
-            $this->_events[ $id ]['dtt_objs'] = array();
113
-
114
-            $dttcache = array();
115
-            $tkts     = array();
116
-            foreach ($tickets as $ticket) {
117
-                if (! $ticket instanceof EE_Ticket) {
118
-                    continue;
119
-                }
120
-                $reldatetime                     = $ticket->datetimes();
121
-                $tkts[ $ticket->ID() ]             = array();
122
-                $tkts[ $ticket->ID() ]['ticket']   = $ticket;
123
-                $tkts[ $ticket->ID() ]['dtt_objs'] = $reldatetime;
124
-                $tkts[ $ticket->ID() ]['att_objs'] = $attendees;
125
-                $tkts[ $ticket->ID() ]['count']    = count($attendees);
126
-                $tkts[ $ticket->ID() ]['EE_Event'] = $event;
127
-                foreach ($reldatetime as $datetime) {
128
-                    if ($datetime instanceof EE_Datetime && ! isset($dtts[ $datetime->ID() ])) {
129
-                        $this->_events[ $id ]['dtt_objs'][ $datetime->ID() ] = $datetime;
130
-                        $dtts[ $datetime->ID() ]['datetime']               = $datetime;
131
-                        $dtts[ $datetime->ID() ]['tkt_objs'][]             = $ticket;
132
-                        $dtts[ $datetime->ID() ]['evt_objs'][]             = $event;
133
-                        $dttcache[ $datetime->ID() ]                       = $datetime;
134
-                    }
135
-                }
136
-            }
137
-
138
-            $this->_events[ $id ]['total_attendees'] = count($attendees);
139
-            $this->_events[ $id ]['att_objs']        = $attendees;
140
-
141
-            // let's also setup the dummy attendees property!
142
-            foreach ($attendees as $att_key => $attendee) {
143
-                if (! $attendee instanceof EE_Attendee) {
144
-                    continue;
145
-                }
146
-                $this->_attendees[ $att_key ]['line_ref'][] = $id;  // so later it can be determined what events this attendee registered for!
147
-                $this->_attendees[ $att_key ]['evt_objs'][] = $event;
148
-                $this->_attendees[ $att_key ]['att_obj']    = $attendee;
149
-                // $this->_attendees[$att_key]['registration_id'] = 0;
150
-                $this->_attendees[ $att_key ]['attendee_email'] = $attendee->email();
151
-                $this->_attendees[ $att_key ]['tkt_objs']       = $tickets;
152
-                if ($att_key == 999999991) {
153
-                    $this->_attendees[ $att_key ]['ans_objs'][999]  = $answers_n_questions['answers'][999];
154
-                    $this->_attendees[ $att_key ]['ans_objs'][1002] = $answers_n_questions['answers'][1002];
155
-                    $this->_attendees[ $att_key ]['ans_objs'][1005] = $answers_n_questions['answers'][1005];
156
-                } elseif ($att_key == 999999992) {
157
-                    $this->_attendees[ $att_key ]['ans_objs'][1000] = $answers_n_questions['answers'][1000];
158
-                    $this->_attendees[ $att_key ]['ans_objs'][1003] = $answers_n_questions['answers'][1003];
159
-                    $this->_attendees[ $att_key ]['ans_objs'][1006] = $answers_n_questions['answers'][1006];
160
-                } elseif ($att_key == 999999993) {
161
-                    $this->_attendees[ $att_key ]['ans_objs'][1001] = $answers_n_questions['answers'][1001];
162
-                    $this->_attendees[ $att_key ]['ans_objs'][1004] = $answers_n_questions['answers'][1004];
163
-                    $this->_attendees[ $att_key ]['ans_objs'][1007] = $answers_n_questions['answers'][1007];
164
-                }
165
-            }
166
-        }
167
-
168
-        $this->tickets            = $tkts;
169
-        $this->datetimes          = $dtts;
170
-        $this->answers            = $answers_n_questions['answers'];
171
-        $this->questions          = $answers_n_questions['questions'];
172
-        $this->total_ticket_count = count($tkts) * count($this->_attendees);
173
-    }
174
-
175
-
176
-    /**
177
-     * This just returns an array of dummy attendee objects that we'll use to attach to events for our preview data
178
-     *
179
-     * @access private
180
-     * @return array an array of attendee objects
181
-     */
182
-    private function _get_some_attendees()
183
-    {
184
-        // let's just setup a dummy array of various attendee details
185
-        $dummy_attendees = array(
186
-            0 => array(
187
-                'Luke',
188
-                'Skywalker',
189
-                '[email protected]',
190
-                '804 Bantha Dr.',
191
-                'Complex 8',
192
-                'Mos Eisley',
193
-                32,
194
-                'US',
195
-                'f0r3e',
196
-                '222-333-4763',
197
-                false,
198
-                '999999991'
199
-            ),
200
-            1 => array(
201
-                'Princess',
202
-                'Leia',
203
-                '[email protected]',
204
-                '1456 Valley Way Boulevard',
205
-                'Suite 9',
206
-                'Alderaan',
207
-                15,
208
-                'US',
209
-                'c1h2c',
210
-                '78-123-111-1111',
211
-                false,
212
-                '999999992'
213
-            ),
214
-            2 => array(
215
-                'Yoda',
216
-                'I Am',
217
-                '[email protected]',
218
-                '4th Tree',
219
-                '5th Knot',
220
-                'Marsh',
221
-                22,
222
-                'US',
223
-                'l18n',
224
-                '999-999-9999',
225
-                false,
226
-                '999999993'
227
-            ),
228
-        );
229
-
230
-        // let's generate the attendee objects
231
-        $attendees = array();
232
-        $var_array = array(
233
-            'fname',
234
-            'lname',
235
-            'email',
236
-            'address',
237
-            'address2',
238
-            'city',
239
-            'staid',
240
-            'cntry',
241
-            'zip',
242
-            'phone',
243
-            'deleted',
244
-            'attid'
245
-        );
246
-
247
-        // EE_Registry::instance()->load_class( 'Attendee', array(), FALSE, false, TRUE );
248
-        foreach ($dummy_attendees as $dummy) {
249
-            $att = array_combine($var_array, $dummy);
250
-            extract($att);
251
-            /** @var $fname string */
252
-            /** @var $lname string */
253
-            /** @var $address string */
254
-            /** @var $address2 string */
255
-            /** @var $city string */
256
-            /** @var $staid string */
257
-            /** @var $cntry string */
258
-            /** @var $zip string */
259
-            /** @var $email string */
260
-            /** @var $phone string */
261
-            /** @var $attid string */
262
-            $attendees[ $attid ] = EE_Attendee::new_instance(
263
-                array(
264
-                    'ATT_fname'    => $fname,
265
-                    'ATT_lname'    => $lname,
266
-                    'ATT_address'  => $address,
267
-                    'ATT_address2' => $address2,
268
-                    'ATT_city'     => $city,
269
-                    'STA_ID'       => $staid,
270
-                    'CNT_ISO'      => $cntry,
271
-                    'ATT_zip'      => $zip,
272
-                    'ATT_email'    => $email,
273
-                    'ATT_phone'    => $phone,
274
-                    'ATT_ID'       => $attid
275
-                )
276
-            );
277
-        }
278
-
279
-        return $attendees;
280
-    }
281
-
282
-
283
-    /**
284
-     * Return an array of dummy question objects indexed by answer id and dummy answer objects indexed by answer id.
285
-     * This will be used in our dummy data setup
286
-     * @return array
287
-     */
288
-    private function _get_some_q_and_as()
289
-    {
290
-
291
-
292
-        $quests_array = array(
293
-            0 => array(
294
-                555,
295
-                __('What is your favorite planet?', 'event_espresso'),
296
-                0
297
-            ),
298
-            1 => array(
299
-                556,
300
-                __('What is your favorite food?', 'event_espresso'),
301
-                0
302
-            ),
303
-            2 => array(
304
-                557,
305
-                __('How many lightyears have you travelled', 'event_espresso'),
306
-                0
307
-            )
308
-        );
309
-
310
-
311
-        $ans_array = array(
312
-            0 => array(
313
-                999,
314
-                555,
315
-                'Tattoine'
316
-            ),
317
-            1 => array(
318
-                1000,
319
-                555,
320
-                'Alderaan'
321
-            ),
322
-            2 => array(
323
-                1001,
324
-                555,
325
-                'Dantooine'
326
-            ),
327
-            3 => array(
328
-                1002,
329
-                556,
330
-                'Fish Fingers'
331
-            ),
332
-            4 => array(
333
-                1003,
334
-                556,
335
-                'Sushi'
336
-            ),
337
-            5 => array(
338
-                1004,
339
-                556,
340
-                'Water'
341
-            ),
342
-            6 => array(
343
-                1005,
344
-                557,
345
-                'A lot',
346
-            ),
347
-            7 => array(
348
-                1006,
349
-                557,
350
-                "That's none of your business."
351
-            ),
352
-            8 => array(
353
-                1007,
354
-                557,
355
-                "People less travel me then."
356
-            )
357
-        );
358
-
359
-        $qst_columns = array('QST_ID', 'QST_display_text', 'QST_system');
360
-        $ans_columns = array('ANS_ID', 'QST_ID', 'ANS_value');
361
-
362
-        // EE_Registry::instance()->load_class( 'Question', array(), FALSE, TRUE, TRUE );
363
-        // EE_Registry::instance()->load_class( 'Answer', array(), FALSE, TRUE, TRUE );
364
-
365
-        $qsts = array();
366
-        // first the questions
367
-        foreach ($quests_array as $qst) {
368
-            $qstobj                  = array_combine($qst_columns, $qst);
369
-            $qsts[ $qstobj['QST_ID'] ] = EE_Question::new_instance($qstobj);
370
-        }
371
-
372
-        // now the answers (and we'll setup our arrays)
373
-        $q_n_as = array();
374
-        foreach ($ans_array as $ans) {
375
-            $ansobj                             = array_combine($ans_columns, $ans);
376
-            $ansobj                             = EE_Answer::new_instance($ansobj);
377
-            $q_n_as['answers'][ $ansobj->ID() ]   = $ansobj;
378
-            $q_n_as['questions'][ $ansobj->ID() ] = $qsts[ $ansobj->get('QST_ID') ];
379
-        }
380
-
381
-        return $q_n_as;
382
-    }
383
-
384
-
385
-    /**
386
-     * Return an array of event objects from the database
387
-     *
388
-     * If event ids are not included then we'll just retrieve the first published event from the database.
389
-     *
390
-     * @param  array $event_ids if set, this will be an array of event ids to obtain events for.
391
-     *
392
-     * @return array    An array of event objects from the db.
393
-     */
394
-    private function _get_some_events($event_ids = array())
395
-    {
396
-
397
-        // HEY, if we have an evt_id then we want to make sure we use that for the preview (because a specific event template is being viewed);
398
-        $event_ids = isset($_REQUEST['evt_id']) && ! empty($_REQUEST['evt_id'])
399
-            ? array($_REQUEST['evt_id'])
400
-            : $event_ids;
401
-
402
-        $limit = ! empty($event_ids)
403
-            ? null
404
-            : apply_filters('FHEE__EE_Messages_Preview_incoming_data___get_some_events__limit', '0,1');
405
-
406
-        $where = ! empty($event_ids)
407
-            ? array(
408
-                'EVT_ID'                 => array('IN', $event_ids),
409
-                'Datetime.Ticket.TKT_ID' => array('>', 1)
410
-            )
411
-            : array('Datetime.Ticket.TKT_ID' => array('>', 1));
412
-
413
-        $events = EE_Registry::instance()->load_model('Event')->get_all(array($where, 'limit' => $limit));
414
-
415
-        return $events;
416
-    }
417
-
418
-
419
-    protected function _setup_data()
420
-    {
421
-
422
-        // need to figure out the running total for test purposes so... we're going to create a temp cart and add the tickets to it!
423
-        if (EE_Registry::instance()->SSN instanceof EE_Session) {
424
-            EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
425
-            $session = EE_Registry::instance()->SSN;
426
-        } else {
427
-            $session = EE_Registry::instance()->load_core('Session');
428
-        }
429
-        $cart = EE_Cart::instance(null, $session);
430
-
431
-
432
-        // add tickets to cart
433
-        foreach ($this->tickets as $ticket) {
434
-            $cart->add_ticket_to_cart($ticket['ticket']);
435
-        }
436
-
437
-
438
-        // setup txn property
439
-        $this->txn = EE_Transaction::new_instance(
440
-            array(
441
-                'TXN_timestamp'    => time(), // unix timestamp
442
-                'TXN_total'        => 0, // txn_total
443
-                'TXN_paid'         => 0, // txn_paid
444
-                'STS_ID'           => EEM_Transaction::incomplete_status_code, // sts_id
445
-                'TXN_session_data' => null, // dump of txn session object (we're just going to leave blank here)
446
-                'TXN_hash_salt'    => null, // hash salt blank as well
447
-                'TXN_ID'           => 999999
448
-            )
449
-        );
450
-
451
-
452
-        // setup reg_objects
453
-        // note we're setting up a reg object for each attendee in each event but ALSO adding to the reg_object array.
454
-        $this->reg_objs = array();
455
-        $regid          = 9999990;
456
-        foreach ($this->_attendees as $key => $attendee) {
457
-            // note we need to setup reg_objects for each event this attendee belongs to
458
-            $regatt = $attendee['att_obj'] instanceof EE_Attendee ? $attendee['att_obj']->ID() : null;
459
-            $regtxn = $this->txn->ID();
460
-            $regcnt = 1;
461
-            foreach ($attendee['line_ref'] as $evtid) {
462
-                foreach ($this->_events[ $evtid ]['tkt_objs'] as $ticket) {
463
-                    if (! $ticket instanceof EE_Ticket) {
464
-                        continue;
465
-                    }
466
-                    $reg_array                                        = array(
467
-                        'EVT_ID'           => $evtid,
468
-                        'ATT_ID'           => $regatt,
469
-                        'TXN_ID'           => $regtxn,
470
-                        'TKT_ID'           => $ticket->ID(),
471
-                        'STS_ID'           => EEM_Registration::status_id_pending_payment,
472
-                        'REG_date'         => time(),
473
-                        'REG_final_price'  => $ticket->get('TKT_price'),
474
-                        'REG_session'      => 'dummy_session_id',
475
-                        'REG_code'         => $regid . '-dummy-generated-code',
476
-                        'REG_url_link'     => $regcnt . '-daafpapasdlfakasdfpqasdfasdf',
477
-                        'REG_count'        => $regcnt,
478
-                        'REG_group_size'   => $this->_events[ $evtid ]['total_attendees'],
479
-                        'REG_att_is_going' => true,
480
-                        'REG_ID'           => $regid
481
-                    );
482
-                    $REG_OBJ                                          = EE_Registration::new_instance($reg_array);
483
-                    $this->_attendees[ $key ]['reg_objs'][ $regid ]       = $REG_OBJ;
484
-                    $this->_events[ $evtid ]['reg_objs'][]              = $REG_OBJ;
485
-                    $this->reg_objs[]                                 = $REG_OBJ;
486
-                    $this->tickets[ $ticket->ID() ]['reg_objs'][ $regid ] = $REG_OBJ;
487
-
488
-                    $regcnt++;
489
-                    $regid++;
490
-                }
491
-            }
492
-        }
493
-
494
-
495
-        // setup line items!
496
-        $line_item_total = EEH_Line_Item::create_total_line_item($this->txn);
497
-
498
-        // add tickets
499
-        foreach ($this->tickets as $tktid => $item) {
500
-            $qty    = $item['count'];
501
-            $ticket = $item['ticket'];
502
-            EEH_Line_Item::add_ticket_purchase($line_item_total, $ticket, $qty);
503
-        }
504
-
505
-        $shipping_line_item = EE_Line_Item::new_instance(array(
506
-            'LIN_name'       => __('Shipping Surcharge', 'event_espresso'),
507
-            'LIN_desc'       => __('Sent via Millenium Falcon', 'event_espresso'),
508
-            'LIN_unit_price' => 20,
509
-            'LIN_quantity'   => 1,
510
-            'LIN_is_taxable' => true,
511
-            'LIN_total'      => 20,
512
-            'LIN_type'       => EEM_Line_Item::type_line_item
513
-        ));
514
-        EEH_Line_Item::add_item($line_item_total, $shipping_line_item);
515
-        $this->additional_line_items = array($shipping_line_item);
516
-
517
-        // now let's add taxes
518
-        EEH_Line_Item::apply_taxes($line_item_total);
519
-
520
-        // now we should be able to get the items we need from this object
521
-        $event_line_items = EEH_Line_Item::get_pre_tax_subtotal($line_item_total)->children();
522
-        $line_items       = array();
523
-        foreach ($event_line_items as $line_id => $line_item) {
524
-            if (! $line_item instanceof EE_Line_Item || $line_item->OBJ_type() !== 'Event') {
525
-                continue;
526
-            }
527
-            $ticket_line_items = EEH_Line_Item::get_ticket_line_items($line_item);
528
-            foreach ($ticket_line_items as $ticket_line_id => $ticket_line_item) {
529
-                if (! $ticket_line_item instanceof EE_Line_Item) {
530
-                    continue;
531
-                }
532
-                $this->tickets[ $ticket_line_item->OBJ_ID() ]['line_item']      = $ticket_line_item;
533
-                $this->tickets[ $ticket_line_item->OBJ_ID() ]['sub_line_items'] = $ticket_line_item->children();
534
-                $line_items[ $ticket_line_item->ID() ]['children']              = $ticket_line_item->children();
535
-                $line_items[ $ticket_line_item->ID() ]['EE_Ticket']             = $this->tickets[ $ticket_line_item->OBJ_ID() ]['ticket'];
536
-            }
537
-        }
538
-
539
-        $this->line_items_with_children = $line_items;
540
-        $this->tax_line_items           = $line_item_total->tax_descendants();
541
-
542
-        // add proper total to transaction object.
543
-        $grand_total                 = $line_item_total->recalculate_total_including_taxes();
544
-        $this->grand_total_line_item = $line_item_total;
545
-        $this->txn->set_total($grand_total);
546
-
547
-
548
-        // add additional details for each registration
549
-        foreach ($this->reg_objs as $reg) {
550
-            if (! $reg instanceof EE_Registration) {
551
-                continue;
552
-            }
553
-            $this->_registrations[ $reg->ID() ]['tkt_obj']  = $this->tickets[ $reg->get('TKT_ID') ]['ticket'];
554
-            $this->_registrations[ $reg->ID() ]['evt_obj']  = $this->_events[ $reg->get('EVT_ID') ]['event'];
555
-            $this->_registrations[ $reg->ID() ]['reg_obj']  = $reg;
556
-            $this->_registrations[ $reg->ID() ]['ans_objs'] = $this->_attendees[ $reg->get('ATT_ID') ]['ans_objs'];
557
-            $this->_registrations[ $reg->ID() ]['att_obj']  = $this->_attendees[ $reg->get('ATT_ID') ]['att_obj'];
558
-            $this->_registrations[ $reg->ID() ]['dtt_objs'] = $this->tickets[ $reg->get('TKT_ID') ]['dtt_objs'];
559
-        }
560
-
561
-
562
-        // events and attendees
563
-        $this->events        = $this->_events;
564
-        $this->attendees     = $this->_attendees;
565
-        $this->registrations = $this->_registrations;
566
-
567
-        $attendees_to_shift = $this->_attendees;
568
-
569
-        // setup primary attendee property
570
-        $this->primary_attendee_data = array(
571
-            'fname' => $this->_attendees[999999991]['att_obj'] instanceof EE_Attendee
572
-                ? $this->_attendees[999999991]['att_obj']->fname()
573
-                : '',
574
-
575
-            'lname' => $this->_attendees[999999991]['att_obj'] instanceof EE_Attendee
576
-                ? $this->_attendees[999999991]['att_obj']->lname()
577
-                : '',
578
-
579
-            'email' => $this->_attendees[999999991]['att_obj'] instanceof EE_Attendee
580
-                ? $this->_attendees[999999991]['att_obj']->email()
581
-                : '',
582
-
583
-            'att_obj' => $this->_attendees[999999991]['att_obj'],
584
-
585
-            'reg_obj' => array_shift($attendees_to_shift[999999991]['reg_objs'])
586
-        );
587
-
588
-        // reg_info property
589
-        // note this isn't referenced by any shortcode parsers so we'll ignore for now.
590
-        $this->reg_info = array();
591
-
592
-        // let's set a reg_obj for messengers expecting one.
593
-        $this->reg_obj = array_shift($this->_attendees[999999991]['reg_objs']);
594
-        // the below are just dummy items.
595
-        $this->user_id     = 1;
596
-        $this->ip_address  = '192.0.2.1';
597
-        $this->user_agent  = '';
598
-        $this->init_access = time();
599
-        $this->last_access = time();
600
-    }
21
+	// some specific properties we need for this class
22
+	private $_events = array();
23
+	private $_attendees = array();
24
+	private $_registrations = array();
25
+
26
+
27
+	/**
28
+	 * For the constructor of this special preview class.  We're either looking for an event id or empty data.  If we
29
+	 * have an event id (or ids) then we'll use that as the source for the "dummy" data.  If the data is empty then
30
+	 * we'll get the first three published events from the users database and use that as a source.
31
+	 *
32
+	 * @param array $data
33
+	 */
34
+	public function __construct($data = array())
35
+	{
36
+		$this->_data = isset($data['event_ids']) ? $data['event_ids'] : array();
37
+		$this->_setup_attendees_events();
38
+		parent::__construct($data);
39
+	}
40
+
41
+
42
+	/**
43
+	 * Returns database safe representation of the data later used to when instantiating this object.
44
+	 *
45
+	 * @param array $data The incoming data to be prepped.
46
+	 *
47
+	 * @return array   The prepped data for db
48
+	 */
49
+	public static function convert_data_for_persistent_storage($data)
50
+	{
51
+		return $data;
52
+	}
53
+
54
+
55
+	/**
56
+	 * Data that has been stored in persistent storage that was prepped by _convert_data_for_persistent_storage
57
+	 * can be sent into this method and converted back into the format used for instantiating with this data handler.
58
+	 *
59
+	 * @param array $data
60
+	 *
61
+	 * @return array
62
+	 */
63
+	public static function convert_data_from_persistent_storage($data)
64
+	{
65
+		return $data;
66
+	}
67
+
68
+
69
+	/**
70
+	 * This will just setup the _events property in the expected format.
71
+	 *
72
+	 * @throws \EE_Error
73
+	 */
74
+	private function _setup_attendees_events()
75
+	{
76
+
77
+		// setup some attendee objects
78
+		$attendees = $this->_get_some_attendees();
79
+
80
+		// if empty $data we'll do a query to get some events from the server. otherwise we'll retrieve the event data for the given ids.
81
+		$events = $this->_get_some_events($this->_data);
82
+
83
+		$answers_n_questions = $this->_get_some_q_and_as();
84
+
85
+		if (count($events) < 1) {
86
+			throw new EE_Error(__(
87
+				'We can\'t generate a preview for you because there are no active events in your database',
88
+				'event_espresso'
89
+			));
90
+		}
91
+
92
+
93
+		// now let's loop and set up the _events property.  At the same time we'll set up attendee properties.
94
+
95
+
96
+		// we'll actually use the generated line_item identifiers for our loop
97
+		$dtts = $tkts = array();
98
+		foreach ($events as $id => $event) {
99
+			if (! $event instanceof EE_Event) {
100
+				continue;
101
+			}
102
+			$this->_events[ $id ]['ID']       = $id;
103
+			$this->_events[ $id ]['name']     = $event->get('EVT_name');
104
+			$datetime                       = $event->get_first_related('Datetime');
105
+			$tickets                        = $datetime instanceof EE_Datetime ? $datetime->get_many_related(
106
+				'Ticket',
107
+				array('default_where_conditions' => 'none')
108
+			) : array();
109
+			$this->_events[ $id ]['event']    = $event;
110
+			$this->_events[ $id ]['reg_objs'] = array();
111
+			$this->_events[ $id ]['tkt_objs'] = $tickets;
112
+			$this->_events[ $id ]['dtt_objs'] = array();
113
+
114
+			$dttcache = array();
115
+			$tkts     = array();
116
+			foreach ($tickets as $ticket) {
117
+				if (! $ticket instanceof EE_Ticket) {
118
+					continue;
119
+				}
120
+				$reldatetime                     = $ticket->datetimes();
121
+				$tkts[ $ticket->ID() ]             = array();
122
+				$tkts[ $ticket->ID() ]['ticket']   = $ticket;
123
+				$tkts[ $ticket->ID() ]['dtt_objs'] = $reldatetime;
124
+				$tkts[ $ticket->ID() ]['att_objs'] = $attendees;
125
+				$tkts[ $ticket->ID() ]['count']    = count($attendees);
126
+				$tkts[ $ticket->ID() ]['EE_Event'] = $event;
127
+				foreach ($reldatetime as $datetime) {
128
+					if ($datetime instanceof EE_Datetime && ! isset($dtts[ $datetime->ID() ])) {
129
+						$this->_events[ $id ]['dtt_objs'][ $datetime->ID() ] = $datetime;
130
+						$dtts[ $datetime->ID() ]['datetime']               = $datetime;
131
+						$dtts[ $datetime->ID() ]['tkt_objs'][]             = $ticket;
132
+						$dtts[ $datetime->ID() ]['evt_objs'][]             = $event;
133
+						$dttcache[ $datetime->ID() ]                       = $datetime;
134
+					}
135
+				}
136
+			}
137
+
138
+			$this->_events[ $id ]['total_attendees'] = count($attendees);
139
+			$this->_events[ $id ]['att_objs']        = $attendees;
140
+
141
+			// let's also setup the dummy attendees property!
142
+			foreach ($attendees as $att_key => $attendee) {
143
+				if (! $attendee instanceof EE_Attendee) {
144
+					continue;
145
+				}
146
+				$this->_attendees[ $att_key ]['line_ref'][] = $id;  // so later it can be determined what events this attendee registered for!
147
+				$this->_attendees[ $att_key ]['evt_objs'][] = $event;
148
+				$this->_attendees[ $att_key ]['att_obj']    = $attendee;
149
+				// $this->_attendees[$att_key]['registration_id'] = 0;
150
+				$this->_attendees[ $att_key ]['attendee_email'] = $attendee->email();
151
+				$this->_attendees[ $att_key ]['tkt_objs']       = $tickets;
152
+				if ($att_key == 999999991) {
153
+					$this->_attendees[ $att_key ]['ans_objs'][999]  = $answers_n_questions['answers'][999];
154
+					$this->_attendees[ $att_key ]['ans_objs'][1002] = $answers_n_questions['answers'][1002];
155
+					$this->_attendees[ $att_key ]['ans_objs'][1005] = $answers_n_questions['answers'][1005];
156
+				} elseif ($att_key == 999999992) {
157
+					$this->_attendees[ $att_key ]['ans_objs'][1000] = $answers_n_questions['answers'][1000];
158
+					$this->_attendees[ $att_key ]['ans_objs'][1003] = $answers_n_questions['answers'][1003];
159
+					$this->_attendees[ $att_key ]['ans_objs'][1006] = $answers_n_questions['answers'][1006];
160
+				} elseif ($att_key == 999999993) {
161
+					$this->_attendees[ $att_key ]['ans_objs'][1001] = $answers_n_questions['answers'][1001];
162
+					$this->_attendees[ $att_key ]['ans_objs'][1004] = $answers_n_questions['answers'][1004];
163
+					$this->_attendees[ $att_key ]['ans_objs'][1007] = $answers_n_questions['answers'][1007];
164
+				}
165
+			}
166
+		}
167
+
168
+		$this->tickets            = $tkts;
169
+		$this->datetimes          = $dtts;
170
+		$this->answers            = $answers_n_questions['answers'];
171
+		$this->questions          = $answers_n_questions['questions'];
172
+		$this->total_ticket_count = count($tkts) * count($this->_attendees);
173
+	}
174
+
175
+
176
+	/**
177
+	 * This just returns an array of dummy attendee objects that we'll use to attach to events for our preview data
178
+	 *
179
+	 * @access private
180
+	 * @return array an array of attendee objects
181
+	 */
182
+	private function _get_some_attendees()
183
+	{
184
+		// let's just setup a dummy array of various attendee details
185
+		$dummy_attendees = array(
186
+			0 => array(
187
+				'Luke',
188
+				'Skywalker',
189
+				'[email protected]',
190
+				'804 Bantha Dr.',
191
+				'Complex 8',
192
+				'Mos Eisley',
193
+				32,
194
+				'US',
195
+				'f0r3e',
196
+				'222-333-4763',
197
+				false,
198
+				'999999991'
199
+			),
200
+			1 => array(
201
+				'Princess',
202
+				'Leia',
203
+				'[email protected]',
204
+				'1456 Valley Way Boulevard',
205
+				'Suite 9',
206
+				'Alderaan',
207
+				15,
208
+				'US',
209
+				'c1h2c',
210
+				'78-123-111-1111',
211
+				false,
212
+				'999999992'
213
+			),
214
+			2 => array(
215
+				'Yoda',
216
+				'I Am',
217
+				'[email protected]',
218
+				'4th Tree',
219
+				'5th Knot',
220
+				'Marsh',
221
+				22,
222
+				'US',
223
+				'l18n',
224
+				'999-999-9999',
225
+				false,
226
+				'999999993'
227
+			),
228
+		);
229
+
230
+		// let's generate the attendee objects
231
+		$attendees = array();
232
+		$var_array = array(
233
+			'fname',
234
+			'lname',
235
+			'email',
236
+			'address',
237
+			'address2',
238
+			'city',
239
+			'staid',
240
+			'cntry',
241
+			'zip',
242
+			'phone',
243
+			'deleted',
244
+			'attid'
245
+		);
246
+
247
+		// EE_Registry::instance()->load_class( 'Attendee', array(), FALSE, false, TRUE );
248
+		foreach ($dummy_attendees as $dummy) {
249
+			$att = array_combine($var_array, $dummy);
250
+			extract($att);
251
+			/** @var $fname string */
252
+			/** @var $lname string */
253
+			/** @var $address string */
254
+			/** @var $address2 string */
255
+			/** @var $city string */
256
+			/** @var $staid string */
257
+			/** @var $cntry string */
258
+			/** @var $zip string */
259
+			/** @var $email string */
260
+			/** @var $phone string */
261
+			/** @var $attid string */
262
+			$attendees[ $attid ] = EE_Attendee::new_instance(
263
+				array(
264
+					'ATT_fname'    => $fname,
265
+					'ATT_lname'    => $lname,
266
+					'ATT_address'  => $address,
267
+					'ATT_address2' => $address2,
268
+					'ATT_city'     => $city,
269
+					'STA_ID'       => $staid,
270
+					'CNT_ISO'      => $cntry,
271
+					'ATT_zip'      => $zip,
272
+					'ATT_email'    => $email,
273
+					'ATT_phone'    => $phone,
274
+					'ATT_ID'       => $attid
275
+				)
276
+			);
277
+		}
278
+
279
+		return $attendees;
280
+	}
281
+
282
+
283
+	/**
284
+	 * Return an array of dummy question objects indexed by answer id and dummy answer objects indexed by answer id.
285
+	 * This will be used in our dummy data setup
286
+	 * @return array
287
+	 */
288
+	private function _get_some_q_and_as()
289
+	{
290
+
291
+
292
+		$quests_array = array(
293
+			0 => array(
294
+				555,
295
+				__('What is your favorite planet?', 'event_espresso'),
296
+				0
297
+			),
298
+			1 => array(
299
+				556,
300
+				__('What is your favorite food?', 'event_espresso'),
301
+				0
302
+			),
303
+			2 => array(
304
+				557,
305
+				__('How many lightyears have you travelled', 'event_espresso'),
306
+				0
307
+			)
308
+		);
309
+
310
+
311
+		$ans_array = array(
312
+			0 => array(
313
+				999,
314
+				555,
315
+				'Tattoine'
316
+			),
317
+			1 => array(
318
+				1000,
319
+				555,
320
+				'Alderaan'
321
+			),
322
+			2 => array(
323
+				1001,
324
+				555,
325
+				'Dantooine'
326
+			),
327
+			3 => array(
328
+				1002,
329
+				556,
330
+				'Fish Fingers'
331
+			),
332
+			4 => array(
333
+				1003,
334
+				556,
335
+				'Sushi'
336
+			),
337
+			5 => array(
338
+				1004,
339
+				556,
340
+				'Water'
341
+			),
342
+			6 => array(
343
+				1005,
344
+				557,
345
+				'A lot',
346
+			),
347
+			7 => array(
348
+				1006,
349
+				557,
350
+				"That's none of your business."
351
+			),
352
+			8 => array(
353
+				1007,
354
+				557,
355
+				"People less travel me then."
356
+			)
357
+		);
358
+
359
+		$qst_columns = array('QST_ID', 'QST_display_text', 'QST_system');
360
+		$ans_columns = array('ANS_ID', 'QST_ID', 'ANS_value');
361
+
362
+		// EE_Registry::instance()->load_class( 'Question', array(), FALSE, TRUE, TRUE );
363
+		// EE_Registry::instance()->load_class( 'Answer', array(), FALSE, TRUE, TRUE );
364
+
365
+		$qsts = array();
366
+		// first the questions
367
+		foreach ($quests_array as $qst) {
368
+			$qstobj                  = array_combine($qst_columns, $qst);
369
+			$qsts[ $qstobj['QST_ID'] ] = EE_Question::new_instance($qstobj);
370
+		}
371
+
372
+		// now the answers (and we'll setup our arrays)
373
+		$q_n_as = array();
374
+		foreach ($ans_array as $ans) {
375
+			$ansobj                             = array_combine($ans_columns, $ans);
376
+			$ansobj                             = EE_Answer::new_instance($ansobj);
377
+			$q_n_as['answers'][ $ansobj->ID() ]   = $ansobj;
378
+			$q_n_as['questions'][ $ansobj->ID() ] = $qsts[ $ansobj->get('QST_ID') ];
379
+		}
380
+
381
+		return $q_n_as;
382
+	}
383
+
384
+
385
+	/**
386
+	 * Return an array of event objects from the database
387
+	 *
388
+	 * If event ids are not included then we'll just retrieve the first published event from the database.
389
+	 *
390
+	 * @param  array $event_ids if set, this will be an array of event ids to obtain events for.
391
+	 *
392
+	 * @return array    An array of event objects from the db.
393
+	 */
394
+	private function _get_some_events($event_ids = array())
395
+	{
396
+
397
+		// HEY, if we have an evt_id then we want to make sure we use that for the preview (because a specific event template is being viewed);
398
+		$event_ids = isset($_REQUEST['evt_id']) && ! empty($_REQUEST['evt_id'])
399
+			? array($_REQUEST['evt_id'])
400
+			: $event_ids;
401
+
402
+		$limit = ! empty($event_ids)
403
+			? null
404
+			: apply_filters('FHEE__EE_Messages_Preview_incoming_data___get_some_events__limit', '0,1');
405
+
406
+		$where = ! empty($event_ids)
407
+			? array(
408
+				'EVT_ID'                 => array('IN', $event_ids),
409
+				'Datetime.Ticket.TKT_ID' => array('>', 1)
410
+			)
411
+			: array('Datetime.Ticket.TKT_ID' => array('>', 1));
412
+
413
+		$events = EE_Registry::instance()->load_model('Event')->get_all(array($where, 'limit' => $limit));
414
+
415
+		return $events;
416
+	}
417
+
418
+
419
+	protected function _setup_data()
420
+	{
421
+
422
+		// need to figure out the running total for test purposes so... we're going to create a temp cart and add the tickets to it!
423
+		if (EE_Registry::instance()->SSN instanceof EE_Session) {
424
+			EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
425
+			$session = EE_Registry::instance()->SSN;
426
+		} else {
427
+			$session = EE_Registry::instance()->load_core('Session');
428
+		}
429
+		$cart = EE_Cart::instance(null, $session);
430
+
431
+
432
+		// add tickets to cart
433
+		foreach ($this->tickets as $ticket) {
434
+			$cart->add_ticket_to_cart($ticket['ticket']);
435
+		}
436
+
437
+
438
+		// setup txn property
439
+		$this->txn = EE_Transaction::new_instance(
440
+			array(
441
+				'TXN_timestamp'    => time(), // unix timestamp
442
+				'TXN_total'        => 0, // txn_total
443
+				'TXN_paid'         => 0, // txn_paid
444
+				'STS_ID'           => EEM_Transaction::incomplete_status_code, // sts_id
445
+				'TXN_session_data' => null, // dump of txn session object (we're just going to leave blank here)
446
+				'TXN_hash_salt'    => null, // hash salt blank as well
447
+				'TXN_ID'           => 999999
448
+			)
449
+		);
450
+
451
+
452
+		// setup reg_objects
453
+		// note we're setting up a reg object for each attendee in each event but ALSO adding to the reg_object array.
454
+		$this->reg_objs = array();
455
+		$regid          = 9999990;
456
+		foreach ($this->_attendees as $key => $attendee) {
457
+			// note we need to setup reg_objects for each event this attendee belongs to
458
+			$regatt = $attendee['att_obj'] instanceof EE_Attendee ? $attendee['att_obj']->ID() : null;
459
+			$regtxn = $this->txn->ID();
460
+			$regcnt = 1;
461
+			foreach ($attendee['line_ref'] as $evtid) {
462
+				foreach ($this->_events[ $evtid ]['tkt_objs'] as $ticket) {
463
+					if (! $ticket instanceof EE_Ticket) {
464
+						continue;
465
+					}
466
+					$reg_array                                        = array(
467
+						'EVT_ID'           => $evtid,
468
+						'ATT_ID'           => $regatt,
469
+						'TXN_ID'           => $regtxn,
470
+						'TKT_ID'           => $ticket->ID(),
471
+						'STS_ID'           => EEM_Registration::status_id_pending_payment,
472
+						'REG_date'         => time(),
473
+						'REG_final_price'  => $ticket->get('TKT_price'),
474
+						'REG_session'      => 'dummy_session_id',
475
+						'REG_code'         => $regid . '-dummy-generated-code',
476
+						'REG_url_link'     => $regcnt . '-daafpapasdlfakasdfpqasdfasdf',
477
+						'REG_count'        => $regcnt,
478
+						'REG_group_size'   => $this->_events[ $evtid ]['total_attendees'],
479
+						'REG_att_is_going' => true,
480
+						'REG_ID'           => $regid
481
+					);
482
+					$REG_OBJ                                          = EE_Registration::new_instance($reg_array);
483
+					$this->_attendees[ $key ]['reg_objs'][ $regid ]       = $REG_OBJ;
484
+					$this->_events[ $evtid ]['reg_objs'][]              = $REG_OBJ;
485
+					$this->reg_objs[]                                 = $REG_OBJ;
486
+					$this->tickets[ $ticket->ID() ]['reg_objs'][ $regid ] = $REG_OBJ;
487
+
488
+					$regcnt++;
489
+					$regid++;
490
+				}
491
+			}
492
+		}
493
+
494
+
495
+		// setup line items!
496
+		$line_item_total = EEH_Line_Item::create_total_line_item($this->txn);
497
+
498
+		// add tickets
499
+		foreach ($this->tickets as $tktid => $item) {
500
+			$qty    = $item['count'];
501
+			$ticket = $item['ticket'];
502
+			EEH_Line_Item::add_ticket_purchase($line_item_total, $ticket, $qty);
503
+		}
504
+
505
+		$shipping_line_item = EE_Line_Item::new_instance(array(
506
+			'LIN_name'       => __('Shipping Surcharge', 'event_espresso'),
507
+			'LIN_desc'       => __('Sent via Millenium Falcon', 'event_espresso'),
508
+			'LIN_unit_price' => 20,
509
+			'LIN_quantity'   => 1,
510
+			'LIN_is_taxable' => true,
511
+			'LIN_total'      => 20,
512
+			'LIN_type'       => EEM_Line_Item::type_line_item
513
+		));
514
+		EEH_Line_Item::add_item($line_item_total, $shipping_line_item);
515
+		$this->additional_line_items = array($shipping_line_item);
516
+
517
+		// now let's add taxes
518
+		EEH_Line_Item::apply_taxes($line_item_total);
519
+
520
+		// now we should be able to get the items we need from this object
521
+		$event_line_items = EEH_Line_Item::get_pre_tax_subtotal($line_item_total)->children();
522
+		$line_items       = array();
523
+		foreach ($event_line_items as $line_id => $line_item) {
524
+			if (! $line_item instanceof EE_Line_Item || $line_item->OBJ_type() !== 'Event') {
525
+				continue;
526
+			}
527
+			$ticket_line_items = EEH_Line_Item::get_ticket_line_items($line_item);
528
+			foreach ($ticket_line_items as $ticket_line_id => $ticket_line_item) {
529
+				if (! $ticket_line_item instanceof EE_Line_Item) {
530
+					continue;
531
+				}
532
+				$this->tickets[ $ticket_line_item->OBJ_ID() ]['line_item']      = $ticket_line_item;
533
+				$this->tickets[ $ticket_line_item->OBJ_ID() ]['sub_line_items'] = $ticket_line_item->children();
534
+				$line_items[ $ticket_line_item->ID() ]['children']              = $ticket_line_item->children();
535
+				$line_items[ $ticket_line_item->ID() ]['EE_Ticket']             = $this->tickets[ $ticket_line_item->OBJ_ID() ]['ticket'];
536
+			}
537
+		}
538
+
539
+		$this->line_items_with_children = $line_items;
540
+		$this->tax_line_items           = $line_item_total->tax_descendants();
541
+
542
+		// add proper total to transaction object.
543
+		$grand_total                 = $line_item_total->recalculate_total_including_taxes();
544
+		$this->grand_total_line_item = $line_item_total;
545
+		$this->txn->set_total($grand_total);
546
+
547
+
548
+		// add additional details for each registration
549
+		foreach ($this->reg_objs as $reg) {
550
+			if (! $reg instanceof EE_Registration) {
551
+				continue;
552
+			}
553
+			$this->_registrations[ $reg->ID() ]['tkt_obj']  = $this->tickets[ $reg->get('TKT_ID') ]['ticket'];
554
+			$this->_registrations[ $reg->ID() ]['evt_obj']  = $this->_events[ $reg->get('EVT_ID') ]['event'];
555
+			$this->_registrations[ $reg->ID() ]['reg_obj']  = $reg;
556
+			$this->_registrations[ $reg->ID() ]['ans_objs'] = $this->_attendees[ $reg->get('ATT_ID') ]['ans_objs'];
557
+			$this->_registrations[ $reg->ID() ]['att_obj']  = $this->_attendees[ $reg->get('ATT_ID') ]['att_obj'];
558
+			$this->_registrations[ $reg->ID() ]['dtt_objs'] = $this->tickets[ $reg->get('TKT_ID') ]['dtt_objs'];
559
+		}
560
+
561
+
562
+		// events and attendees
563
+		$this->events        = $this->_events;
564
+		$this->attendees     = $this->_attendees;
565
+		$this->registrations = $this->_registrations;
566
+
567
+		$attendees_to_shift = $this->_attendees;
568
+
569
+		// setup primary attendee property
570
+		$this->primary_attendee_data = array(
571
+			'fname' => $this->_attendees[999999991]['att_obj'] instanceof EE_Attendee
572
+				? $this->_attendees[999999991]['att_obj']->fname()
573
+				: '',
574
+
575
+			'lname' => $this->_attendees[999999991]['att_obj'] instanceof EE_Attendee
576
+				? $this->_attendees[999999991]['att_obj']->lname()
577
+				: '',
578
+
579
+			'email' => $this->_attendees[999999991]['att_obj'] instanceof EE_Attendee
580
+				? $this->_attendees[999999991]['att_obj']->email()
581
+				: '',
582
+
583
+			'att_obj' => $this->_attendees[999999991]['att_obj'],
584
+
585
+			'reg_obj' => array_shift($attendees_to_shift[999999991]['reg_objs'])
586
+		);
587
+
588
+		// reg_info property
589
+		// note this isn't referenced by any shortcode parsers so we'll ignore for now.
590
+		$this->reg_info = array();
591
+
592
+		// let's set a reg_obj for messengers expecting one.
593
+		$this->reg_obj = array_shift($this->_attendees[999999991]['reg_objs']);
594
+		// the below are just dummy items.
595
+		$this->user_id     = 1;
596
+		$this->ip_address  = '192.0.2.1';
597
+		$this->user_agent  = '';
598
+		$this->init_access = time();
599
+		$this->last_access = time();
600
+	}
601 601
 }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page.core.php 1 patch
Indentation   +4083 added lines, -4083 removed lines patch added patch discarded remove patch
@@ -18,4152 +18,4152 @@
 block discarded – undo
18 18
  */
19 19
 abstract class EE_Admin_Page extends EE_Base implements InterminableInterface
20 20
 {
21
-    /**
22
-     * @var EE_Admin_Config
23
-     */
24
-    protected $admin_config;
21
+	/**
22
+	 * @var EE_Admin_Config
23
+	 */
24
+	protected $admin_config;
25 25
 
26
-    /**
27
-     * @var LoaderInterface $loader
28
-     */
29
-    protected $loader;
26
+	/**
27
+	 * @var LoaderInterface $loader
28
+	 */
29
+	protected $loader;
30 30
 
31
-    // set in _init_page_props()
32
-    public $page_slug;
31
+	// set in _init_page_props()
32
+	public $page_slug;
33 33
 
34
-    public $page_label;
34
+	public $page_label;
35 35
 
36
-    public $page_folder;
36
+	public $page_folder;
37 37
 
38
-    // set in define_page_props()
39
-    protected $_admin_base_url;
38
+	// set in define_page_props()
39
+	protected $_admin_base_url;
40 40
 
41
-    protected $_admin_base_path;
41
+	protected $_admin_base_path;
42 42
 
43
-    protected $_admin_page_title;
43
+	protected $_admin_page_title;
44 44
 
45
-    protected $_labels;
45
+	protected $_labels;
46 46
 
47 47
 
48
-    // set early within EE_Admin_Init
49
-    protected $_wp_page_slug;
48
+	// set early within EE_Admin_Init
49
+	protected $_wp_page_slug;
50 50
 
51
-    // nav tabs
52
-    protected $_nav_tabs;
51
+	// nav tabs
52
+	protected $_nav_tabs;
53 53
 
54
-    protected $_default_nav_tab_name;
54
+	protected $_default_nav_tab_name;
55 55
 
56
-    /**
57
-     * @var array $_help_tour
58
-     */
59
-    protected $_help_tour = array();
56
+	/**
57
+	 * @var array $_help_tour
58
+	 */
59
+	protected $_help_tour = array();
60 60
 
61 61
 
62
-    // template variables (used by templates)
63
-    protected $_template_path;
62
+	// template variables (used by templates)
63
+	protected $_template_path;
64 64
 
65
-    protected $_column_template_path;
65
+	protected $_column_template_path;
66 66
 
67
-    /**
68
-     * @var array $_template_args
69
-     */
70
-    protected $_template_args = array();
67
+	/**
68
+	 * @var array $_template_args
69
+	 */
70
+	protected $_template_args = array();
71 71
 
72
-    /**
73
-     * this will hold the list table object for a given view.
74
-     *
75
-     * @var EE_Admin_List_Table $_list_table_object
76
-     */
77
-    protected $_list_table_object;
72
+	/**
73
+	 * this will hold the list table object for a given view.
74
+	 *
75
+	 * @var EE_Admin_List_Table $_list_table_object
76
+	 */
77
+	protected $_list_table_object;
78 78
 
79
-    // boolean
80
-    protected $_is_UI_request; // this starts at null so we can have no header routes progress through two states.
79
+	// boolean
80
+	protected $_is_UI_request; // this starts at null so we can have no header routes progress through two states.
81 81
 
82
-    protected $_routing;
82
+	protected $_routing;
83 83
 
84
-    // list table args
85
-    protected $_view;
84
+	// list table args
85
+	protected $_view;
86 86
 
87
-    protected $_views;
87
+	protected $_views;
88 88
 
89 89
 
90
-    // action => method pairs used for routing incoming requests
91
-    protected $_page_routes;
90
+	// action => method pairs used for routing incoming requests
91
+	protected $_page_routes;
92 92
 
93
-    /**
94
-     * @var array $_page_config
95
-     */
96
-    protected $_page_config;
93
+	/**
94
+	 * @var array $_page_config
95
+	 */
96
+	protected $_page_config;
97 97
 
98
-    /**
99
-     * the current page route and route config
100
-     *
101
-     * @var string $_route
102
-     */
103
-    protected $_route;
98
+	/**
99
+	 * the current page route and route config
100
+	 *
101
+	 * @var string $_route
102
+	 */
103
+	protected $_route;
104 104
 
105
-    /**
106
-     * @var string $_cpt_route
107
-     */
108
-    protected $_cpt_route;
105
+	/**
106
+	 * @var string $_cpt_route
107
+	 */
108
+	protected $_cpt_route;
109 109
 
110
-    /**
111
-     * @var array $_route_config
112
-     */
113
-    protected $_route_config;
110
+	/**
111
+	 * @var array $_route_config
112
+	 */
113
+	protected $_route_config;
114 114
 
115
-    /**
116
-     * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
117
-     * actions.
118
-     *
119
-     * @since 4.6.x
120
-     * @var array.
121
-     */
122
-    protected $_default_route_query_args;
123
-
124
-    // set via request page and action args.
125
-    protected $_current_page;
126
-
127
-    protected $_current_view;
128
-
129
-    protected $_current_page_view_url;
130
-
131
-    // sanitized request action (and nonce)
132
-
133
-    /**
134
-     * @var string $_req_action
135
-     */
136
-    protected $_req_action;
137
-
138
-    /**
139
-     * @var string $_req_nonce
140
-     */
141
-    protected $_req_nonce;
142
-
143
-    // search related
144
-    protected $_search_btn_label;
145
-
146
-    protected $_search_box_callback;
147
-
148
-    /**
149
-     * WP Current Screen object
150
-     *
151
-     * @var WP_Screen
152
-     */
153
-    protected $_current_screen;
154
-
155
-    // for holding EE_Admin_Hooks object when needed (set via set_hook_object())
156
-    protected $_hook_obj;
157
-
158
-    // for holding incoming request data
159
-    protected $_req_data = [];
160
-
161
-    // yes / no array for admin form fields
162
-    protected $_yes_no_values = array();
163
-
164
-    // some default things shared by all child classes
165
-    protected $_default_espresso_metaboxes;
166
-
167
-    /**
168
-     *    EE_Registry Object
169
-     *
170
-     * @var    EE_Registry
171
-     */
172
-    protected $EE;
173
-
174
-
175
-    /**
176
-     * This is just a property that flags whether the given route is a caffeinated route or not.
177
-     *
178
-     * @var boolean
179
-     */
180
-    protected $_is_caf = false;
181
-
182
-
183
-    /**
184
-     * @Constructor
185
-     * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
186
-     * @throws InvalidArgumentException
187
-     * @throws InvalidDataTypeException
188
-     * @throws InvalidInterfaceException
189
-     * @throws ReflectionException
190
-     */
191
-    public function __construct($routing = true)
192
-    {
193
-        $this->loader = LoaderFactory::getLoader();
194
-        $this->admin_config = $this->loader->getShared('EE_Admin_Config');
195
-        if (strpos($this->_get_dir(), 'caffeinated') !== false) {
196
-            $this->_is_caf = true;
197
-        }
198
-        $this->_yes_no_values = array(
199
-            array('id' => true, 'text' => esc_html__('Yes', 'event_espresso')),
200
-            array('id' => false, 'text' => esc_html__('No', 'event_espresso')),
201
-        );
202
-        // set the _req_data property.
203
-        $this->_req_data = array_merge($_GET, $_POST);
204
-        // routing enabled?
205
-        $this->_routing = $routing;
206
-    }
207
-
208
-
209
-    /**
210
-     * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
211
-     * for child classes that needed to set properties prior to these methods getting called,
212
-     * but also needed the parent class to have its construction completed as well.
213
-     * Bottom line is that constructors should ONLY be used for setting initial properties
214
-     * and any complex initialization logic should only run after instantiation is complete.
215
-     *
216
-     * This method gets called immediately after construction from within
217
-     *      EE_Admin_Page_Init::_initialize_admin_page()
218
-     *
219
-     * @throws EE_Error
220
-     * @throws InvalidArgumentException
221
-     * @throws InvalidDataTypeException
222
-     * @throws InvalidInterfaceException
223
-     * @throws ReflectionException
224
-     * @since $VID:$
225
-     */
226
-    public function initializePage()
227
-    {
228
-        // set initial page props (child method)
229
-        $this->_init_page_props();
230
-        // set global defaults
231
-        $this->_set_defaults();
232
-        // set early because incoming requests could be ajax related and we need to register those hooks.
233
-        $this->_global_ajax_hooks();
234
-        $this->_ajax_hooks();
235
-        // other_page_hooks have to be early too.
236
-        $this->_do_other_page_hooks();
237
-        // This just allows us to have extending classes do something specific
238
-        // before the parent constructor runs _page_setup().
239
-        if (method_exists($this, '_before_page_setup')) {
240
-            $this->_before_page_setup();
241
-        }
242
-        // set up page dependencies
243
-        $this->_page_setup();
244
-    }
245
-
246
-
247
-    /**
248
-     * _init_page_props
249
-     * Child classes use to set at least the following properties:
250
-     * $page_slug.
251
-     * $page_label.
252
-     *
253
-     * @abstract
254
-     * @return void
255
-     */
256
-    abstract protected function _init_page_props();
257
-
258
-
259
-    /**
260
-     * _ajax_hooks
261
-     * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
262
-     * Note: within the ajax callback methods.
263
-     *
264
-     * @abstract
265
-     * @return void
266
-     */
267
-    abstract protected function _ajax_hooks();
268
-
269
-
270
-    /**
271
-     * _define_page_props
272
-     * child classes define page properties in here.  Must include at least:
273
-     * $_admin_base_url = base_url for all admin pages
274
-     * $_admin_page_title = default admin_page_title for admin pages
275
-     * $_labels = array of default labels for various automatically generated elements:
276
-     *    array(
277
-     *        'buttons' => array(
278
-     *            'add' => esc_html__('label for add new button'),
279
-     *            'edit' => esc_html__('label for edit button'),
280
-     *            'delete' => esc_html__('label for delete button')
281
-     *            )
282
-     *        )
283
-     *
284
-     * @abstract
285
-     * @return void
286
-     */
287
-    abstract protected function _define_page_props();
288
-
289
-
290
-    /**
291
-     * _set_page_routes
292
-     * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
293
-     * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
294
-     * have a 'default' route. Here's the format
295
-     * $this->_page_routes = array(
296
-     *        'default' => array(
297
-     *            'func' => '_default_method_handling_route',
298
-     *            'args' => array('array','of','args'),
299
-     *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
300
-     *            ajax request, backend processing)
301
-     *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
302
-     *            headers route after.  The string you enter here should match the defined route reference for a
303
-     *            headers sent route.
304
-     *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
305
-     *            this route.
306
-     *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
307
-     *            checks).
308
-     *        ),
309
-     *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
310
-     *        handling method.
311
-     *        )
312
-     * )
313
-     *
314
-     * @abstract
315
-     * @return void
316
-     */
317
-    abstract protected function _set_page_routes();
318
-
319
-
320
-    /**
321
-     * _set_page_config
322
-     * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
323
-     * array corresponds to the page_route for the loaded page. Format:
324
-     * $this->_page_config = array(
325
-     *        'default' => array(
326
-     *            'labels' => array(
327
-     *                'buttons' => array(
328
-     *                    'add' => esc_html__('label for adding item'),
329
-     *                    'edit' => esc_html__('label for editing item'),
330
-     *                    'delete' => esc_html__('label for deleting item')
331
-     *                ),
332
-     *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
333
-     *            ), //optional an array of custom labels for various automatically generated elements to use on the
334
-     *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
335
-     *            _define_page_props() method
336
-     *            'nav' => array(
337
-     *                'label' => esc_html__('Label for Tab', 'event_espresso').
338
-     *                'url' => 'http://someurl', //automatically generated UNLESS you define
339
-     *                'css_class' => 'css-class', //automatically generated UNLESS you define
340
-     *                'order' => 10, //required to indicate tab position.
341
-     *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
342
-     *                displayed then add this parameter.
343
-     *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
344
-     *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
345
-     *            metaboxes set for eventespresso admin pages.
346
-     *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
347
-     *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
348
-     *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
349
-     *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
350
-     *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
351
-     *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
352
-     *            array indicates the max number of columns (4) and the default number of columns on page load (2).
353
-     *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
354
-     *            want to display.
355
-     *            'help_tabs' => array( //this is used for adding help tabs to a page
356
-     *                'tab_id' => array(
357
-     *                    'title' => 'tab_title',
358
-     *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
359
-     *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
360
-     *                    should match a file in the admin folder's "help_tabs" dir (ie..
361
-     *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
362
-     *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
363
-     *                    attempt to use the callback which should match the name of a method in the class
364
-     *                    ),
365
-     *                'tab2_id' => array(
366
-     *                    'title' => 'tab2 title',
367
-     *                    'filename' => 'file_name_2'
368
-     *                    'callback' => 'callback_method_for_content',
369
-     *                 ),
370
-     *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
371
-     *            help tab area on an admin page. @link
372
-     *            http://make.wordpress.org/core/2011/12/06/help-and-screen-api-changes-in-3-3/
373
-     *            'help_tour' => array(
374
-     *                'name_of_help_tour_class', //all help tours shoudl be a child class of EE_Help_Tour and located
375
-     *                in a folder for this admin page named "help_tours", a file name matching the key given here
376
-     *                (name_of_help_tour_class.class.php), and class matching key given here (name_of_help_tour_class)
377
-     *            ),
378
-     *            'require_nonce' => TRUE //this is used if you want to set a route to NOT require a nonce (default is
379
-     *            true if it isn't present).  To remove the requirement for a nonce check when this route is visited
380
-     *            just set
381
-     *            'require_nonce' to FALSE
382
-     *            )
383
-     * )
384
-     *
385
-     * @abstract
386
-     * @return void
387
-     */
388
-    abstract protected function _set_page_config();
389
-
390
-
391
-
392
-
393
-
394
-    /** end sample help_tour methods **/
395
-    /**
396
-     * _add_screen_options
397
-     * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
398
-     * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
399
-     * to a particular view.
400
-     *
401
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
402
-     *         see also WP_Screen object documents...
403
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
404
-     * @abstract
405
-     * @return void
406
-     */
407
-    abstract protected function _add_screen_options();
408
-
409
-
410
-    /**
411
-     * _add_feature_pointers
412
-     * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
413
-     * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
414
-     * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
415
-     * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
416
-     * extended) also see:
417
-     *
418
-     * @link   http://eamann.com/tech/wordpress-portland/
419
-     * @abstract
420
-     * @return void
421
-     */
422
-    abstract protected function _add_feature_pointers();
423
-
424
-
425
-    /**
426
-     * load_scripts_styles
427
-     * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
428
-     * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
429
-     * scripts/styles per view by putting them in a dynamic function in this format
430
-     * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
431
-     *
432
-     * @abstract
433
-     * @return void
434
-     */
435
-    abstract public function load_scripts_styles();
436
-
437
-
438
-    /**
439
-     * admin_init
440
-     * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
441
-     * all pages/views loaded by child class.
442
-     *
443
-     * @abstract
444
-     * @return void
445
-     */
446
-    abstract public function admin_init();
447
-
448
-
449
-    /**
450
-     * admin_notices
451
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
452
-     * all pages/views loaded by child class.
453
-     *
454
-     * @abstract
455
-     * @return void
456
-     */
457
-    abstract public function admin_notices();
458
-
459
-
460
-    /**
461
-     * admin_footer_scripts
462
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
463
-     * will apply to all pages/views loaded by child class.
464
-     *
465
-     * @return void
466
-     */
467
-    abstract public function admin_footer_scripts();
468
-
469
-
470
-    /**
471
-     * admin_footer
472
-     * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
473
-     * apply to all pages/views loaded by child class.
474
-     *
475
-     * @return void
476
-     */
477
-    public function admin_footer()
478
-    {
479
-    }
480
-
481
-
482
-    /**
483
-     * _global_ajax_hooks
484
-     * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
485
-     * Note: within the ajax callback methods.
486
-     *
487
-     * @abstract
488
-     * @return void
489
-     */
490
-    protected function _global_ajax_hooks()
491
-    {
492
-        // for lazy loading of metabox content
493
-        add_action('wp_ajax_espresso-ajax-content', array($this, 'ajax_metabox_content'), 10);
494
-
495
-        add_action(
496
-            'wp_ajax_espresso_hide_status_change_notice',
497
-            [$this, 'hideStatusChangeNotice']
498
-        );
499
-        add_action(
500
-            'wp_ajax_nopriv_espresso_hide_status_change_notice',
501
-            [$this, 'hideStatusChangeNotice']
502
-        );
503
-    }
504
-
505
-
506
-    public function ajax_metabox_content()
507
-    {
508
-        $contentid = isset($this->_req_data['contentid']) ? $this->_req_data['contentid'] : '';
509
-        $url = isset($this->_req_data['contenturl']) ? $this->_req_data['contenturl'] : '';
510
-        EE_Admin_Page::cached_rss_display($contentid, $url);
511
-        wp_die();
512
-    }
513
-
514
-
515
-    public function hideStatusChangeNotice()
516
-    {
517
-        $response = [];
518
-        try {
519
-            /** @var EventEspresso\core\admin\StatusChangeNotice $status_change_notice */
520
-            $status_change_notice = $this->loader->getShared('EventEspresso\core\admin\StatusChangeNotice');
521
-            $response['success'] = $status_change_notice->dismiss() > -1;
522
-        } catch (Exception $exception) {
523
-            $response['errors'] = $exception->getMessage();
524
-        }
525
-        echo wp_json_encode($response);
526
-        exit();
527
-    }
528
-
529
-
530
-    /**
531
-     * _page_setup
532
-     * Makes sure any things that need to be loaded early get handled.  We also escape early here if the page requested
533
-     * doesn't match the object.
534
-     *
535
-     * @final
536
-     * @return void
537
-     * @throws EE_Error
538
-     * @throws InvalidArgumentException
539
-     * @throws ReflectionException
540
-     * @throws InvalidDataTypeException
541
-     * @throws InvalidInterfaceException
542
-     */
543
-    final protected function _page_setup()
544
-    {
545
-        // requires?
546
-        // admin_init stuff - global - we're setting this REALLY early
547
-        // so if EE_Admin pages have to hook into other WP pages they can.
548
-        // But keep in mind, not everything is available from the EE_Admin Page object at this point.
549
-        add_action('admin_init', array($this, 'admin_init_global'), 5);
550
-        // next verify if we need to load anything...
551
-        $this->_current_page = ! empty($_GET['page']) ? sanitize_key($_GET['page']) : '';
552
-        $this->page_folder = strtolower(
553
-            str_replace(array('_Admin_Page', 'Extend_'), '', get_class($this))
554
-        );
555
-        global $ee_menu_slugs;
556
-        $ee_menu_slugs = (array) $ee_menu_slugs;
557
-        if (! defined('DOING_AJAX') && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))) {
558
-            return;
559
-        }
560
-        // becuz WP List tables have two duplicate select inputs for choosing bulk actions, we need to copy the action from the second to the first
561
-        if (isset($this->_req_data['action2']) && $this->_req_data['action'] === '-1') {
562
-            $this->_req_data['action'] = ! empty($this->_req_data['action2']) && $this->_req_data['action2'] !== '-1'
563
-                ? $this->_req_data['action2']
564
-                : $this->_req_data['action'];
565
-        }
566
-        // then set blank or -1 action values to 'default'
567
-        $this->_req_action = isset($this->_req_data['action'])
568
-                             && ! empty($this->_req_data['action'])
569
-                             && $this->_req_data['action'] !== '-1'
570
-            ? sanitize_key($this->_req_data['action'])
571
-            : 'default';
572
-        // if action is 'default' after the above BUT we have  'route' var set, then let's use the route as the action.
573
-        //  This covers cases where we're coming in from a list table that isn't on the default route.
574
-        $this->_req_action = $this->_req_action === 'default' && isset($this->_req_data['route'])
575
-            ? $this->_req_data['route'] : $this->_req_action;
576
-        // however if we are doing_ajax and we've got a 'route' set then that's what the req_action will be
577
-        $this->_req_action = defined('DOING_AJAX') && isset($this->_req_data['route'])
578
-            ? $this->_req_data['route']
579
-            : $this->_req_action;
580
-        $this->_current_view = $this->_req_action;
581
-        $this->_req_nonce = $this->_req_action . '_nonce';
582
-        $this->_define_page_props();
583
-        $this->_current_page_view_url = add_query_arg(
584
-            array('page' => $this->_current_page, 'action' => $this->_current_view),
585
-            $this->_admin_base_url
586
-        );
587
-        // default things
588
-        $this->_default_espresso_metaboxes = array(
589
-            '_espresso_news_post_box',
590
-            '_espresso_links_post_box',
591
-            '_espresso_ratings_request',
592
-            '_espresso_sponsors_post_box',
593
-        );
594
-        // set page configs
595
-        $this->_set_page_routes();
596
-        $this->_set_page_config();
597
-        // let's include any referrer data in our default_query_args for this route for "stickiness".
598
-        if (isset($this->_req_data['wp_referer'])) {
599
-            $this->_default_route_query_args['wp_referer'] = $this->_req_data['wp_referer'];
600
-        }
601
-        // for caffeinated and other extended functionality.
602
-        //  If there is a _extend_page_config method
603
-        // then let's run that to modify the all the various page configuration arrays
604
-        if (method_exists($this, '_extend_page_config')) {
605
-            $this->_extend_page_config();
606
-        }
607
-        // for CPT and other extended functionality.
608
-        // If there is an _extend_page_config_for_cpt
609
-        // then let's run that to modify all the various page configuration arrays.
610
-        if (method_exists($this, '_extend_page_config_for_cpt')) {
611
-            $this->_extend_page_config_for_cpt();
612
-        }
613
-        // filter routes and page_config so addons can add their stuff. Filtering done per class
614
-        $this->_page_routes = apply_filters(
615
-            'FHEE__' . get_class($this) . '__page_setup__page_routes',
616
-            $this->_page_routes,
617
-            $this
618
-        );
619
-        $this->_page_config = apply_filters(
620
-            'FHEE__' . get_class($this) . '__page_setup__page_config',
621
-            $this->_page_config,
622
-            $this
623
-        );
624
-        // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
625
-        // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
626
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
627
-            add_action(
628
-                'AHEE__EE_Admin_Page__route_admin_request',
629
-                array($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view),
630
-                10,
631
-                2
632
-            );
633
-        }
634
-        // next route only if routing enabled
635
-        if ($this->_routing && ! defined('DOING_AJAX')) {
636
-            $this->_verify_routes();
637
-            // next let's just check user_access and kill if no access
638
-            $this->check_user_access();
639
-            if ($this->_is_UI_request) {
640
-                // admin_init stuff - global, all views for this page class, specific view
641
-                add_action('admin_init', array($this, 'admin_init'), 10);
642
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
643
-                    add_action('admin_init', array($this, 'admin_init_' . $this->_current_view), 15);
644
-                }
645
-            } else {
646
-                // hijack regular WP loading and route admin request immediately
647
-                @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
648
-                $this->route_admin_request();
649
-            }
650
-        }
651
-    }
652
-
653
-
654
-    /**
655
-     * Provides a way for related child admin pages to load stuff on the loaded admin page.
656
-     *
657
-     * @return void
658
-     * @throws EE_Error
659
-     */
660
-    private function _do_other_page_hooks()
661
-    {
662
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, array());
663
-        foreach ($registered_pages as $page) {
664
-            // now let's setup the file name and class that should be present
665
-            $classname = str_replace('.class.php', '', $page);
666
-            // autoloaders should take care of loading file
667
-            if (! class_exists($classname)) {
668
-                $error_msg[] = sprintf(
669
-                    esc_html__(
670
-                        'Something went wrong with loading the %s admin hooks page.',
671
-                        'event_espresso'
672
-                    ),
673
-                    $page
674
-                );
675
-                $error_msg[] = $error_msg[0]
676
-                               . "\r\n"
677
-                               . sprintf(
678
-                                   esc_html__(
679
-                                       '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',
680
-                                       'event_espresso'
681
-                                   ),
682
-                                   $page,
683
-                                   '<br />',
684
-                                   '<strong>' . $classname . '</strong>'
685
-                               );
686
-                throw new EE_Error(implode('||', $error_msg));
687
-            }
688
-            // // notice we are passing the instance of this class to the hook object.
689
-            $this->loader->getShared($classname, [$this]);
690
-        }
691
-    }
692
-
693
-
694
-    /**
695
-     * @throws DomainException
696
-     * @throws EE_Error
697
-     * @throws InvalidArgumentException
698
-     * @throws InvalidDataTypeException
699
-     * @throws InvalidInterfaceException
700
-     * @throws ReflectionException
701
-     * @since $VID:$
702
-     */
703
-    public function load_page_dependencies()
704
-    {
705
-        try {
706
-            $this->_load_page_dependencies();
707
-        } catch (EE_Error $e) {
708
-            $e->get_error();
709
-        }
710
-    }
711
-
712
-
713
-    /**
714
-     * load_page_dependencies
715
-     * loads things specific to this page class when its loaded.  Really helps with efficiency.
716
-     *
717
-     * @return void
718
-     * @throws DomainException
719
-     * @throws EE_Error
720
-     * @throws InvalidArgumentException
721
-     * @throws InvalidDataTypeException
722
-     * @throws InvalidInterfaceException
723
-     * @throws ReflectionException
724
-     */
725
-    protected function _load_page_dependencies()
726
-    {
727
-        // let's set the current_screen and screen options to override what WP set
728
-        $this->_current_screen = get_current_screen();
729
-        // load admin_notices - global, page class, and view specific
730
-        add_action('admin_notices', array($this, 'admin_notices_global'), 5);
731
-        add_action('admin_notices', array($this, 'admin_notices'), 10);
732
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
733
-            add_action('admin_notices', array($this, 'admin_notices_' . $this->_current_view), 15);
734
-        }
735
-        // load network admin_notices - global, page class, and view specific
736
-        add_action('network_admin_notices', array($this, 'network_admin_notices_global'), 5);
737
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
738
-            add_action('network_admin_notices', array($this, 'network_admin_notices_' . $this->_current_view));
739
-        }
740
-        // this will save any per_page screen options if they are present
741
-        $this->_set_per_page_screen_options();
742
-        // setup list table properties
743
-        $this->_set_list_table();
744
-        // child classes can "register" a metabox to be automatically handled via the _page_config array property.
745
-        // However in some cases the metaboxes will need to be added within a route handling callback.
746
-        $this->_add_registered_meta_boxes();
747
-        $this->_add_screen_columns();
748
-        // add screen options - global, page child class, and view specific
749
-        $this->_add_global_screen_options();
750
-        $this->_add_screen_options();
751
-        $add_screen_options = "_add_screen_options_{$this->_current_view}";
752
-        if (method_exists($this, $add_screen_options)) {
753
-            $this->{$add_screen_options}();
754
-        }
755
-        // add help tab(s) and tours- set via page_config and qtips.
756
-        // $this->_add_help_tour();
757
-        $this->_add_help_tabs();
758
-        $this->_add_qtips();
759
-        // add feature_pointers - global, page child class, and view specific
760
-        $this->_add_feature_pointers();
761
-        $this->_add_global_feature_pointers();
762
-        $add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
763
-        if (method_exists($this, $add_feature_pointer)) {
764
-            $this->{$add_feature_pointer}();
765
-        }
766
-        // enqueue scripts/styles - global, page class, and view specific
767
-        add_action('admin_enqueue_scripts', array($this, 'load_global_scripts_styles'), 5);
768
-        add_action('admin_enqueue_scripts', array($this, 'load_scripts_styles'), 10);
769
-        if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
770
-            add_action('admin_enqueue_scripts', array($this, "load_scripts_styles_{$this->_current_view}"), 15);
771
-        }
772
-        add_action('admin_enqueue_scripts', array($this, 'admin_footer_scripts_eei18n_js_strings'), 100);
773
-        // admin_print_footer_scripts - global, page child class, and view specific.
774
-        // NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
775
-        // In most cases that's doing_it_wrong().  But adding hidden container elements etc.
776
-        // is a good use case. Notice the late priority we're giving these
777
-        add_action('admin_print_footer_scripts', array($this, 'admin_footer_scripts_global'), 99);
778
-        add_action('admin_print_footer_scripts', array($this, 'admin_footer_scripts'), 100);
779
-        if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
780
-            add_action('admin_print_footer_scripts', array($this, "admin_footer_scripts_{$this->_current_view}"), 101);
781
-        }
782
-        // admin footer scripts
783
-        add_action('admin_footer', array($this, 'admin_footer_global'), 99);
784
-        add_action('admin_footer', array($this, 'admin_footer'), 100);
785
-        if (method_exists($this, "admin_footer_{$this->_current_view}")) {
786
-            add_action('admin_footer', array($this, "admin_footer_{$this->_current_view}"), 101);
787
-        }
788
-        do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
789
-        // targeted hook
790
-        do_action(
791
-            "FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
792
-        );
793
-    }
794
-
795
-
796
-    /**
797
-     * _set_defaults
798
-     * This sets some global defaults for class properties.
799
-     */
800
-    private function _set_defaults()
801
-    {
802
-        $this->_current_screen = $this->_admin_page_title = $this->_req_action = $this->_req_nonce = null;
803
-        $this->_event = $this->_template_path = $this->_column_template_path = null;
804
-        $this->_nav_tabs = $this->_views = $this->_page_routes = array();
805
-        $this->_page_config = $this->_default_route_query_args = array();
806
-        $this->_default_nav_tab_name = 'overview';
807
-        // init template args
808
-        $this->_template_args = array(
809
-            'admin_page_header'  => '',
810
-            'admin_page_content' => '',
811
-            'post_body_content'  => '',
812
-            'before_list_table'  => '',
813
-            'after_list_table'   => '',
814
-        );
815
-    }
816
-
817
-
818
-    /**
819
-     * route_admin_request
820
-     *
821
-     * @see    _route_admin_request()
822
-     * @return exception|void error
823
-     * @throws InvalidArgumentException
824
-     * @throws InvalidInterfaceException
825
-     * @throws InvalidDataTypeException
826
-     * @throws EE_Error
827
-     * @throws ReflectionException
828
-     */
829
-    public function route_admin_request()
830
-    {
831
-        try {
832
-            $this->_route_admin_request();
833
-        } catch (EE_Error $e) {
834
-            $e->get_error();
835
-        }
836
-    }
837
-
838
-
839
-    public function set_wp_page_slug($wp_page_slug)
840
-    {
841
-        $this->_wp_page_slug = $wp_page_slug;
842
-        // if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
843
-        if (is_network_admin()) {
844
-            $this->_wp_page_slug .= '-network';
845
-        }
846
-    }
847
-
848
-
849
-    /**
850
-     * _verify_routes
851
-     * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
852
-     * we know if we need to drop out.
853
-     *
854
-     * @return bool
855
-     * @throws EE_Error
856
-     */
857
-    protected function _verify_routes()
858
-    {
859
-        if (! $this->_current_page && ! defined('DOING_AJAX')) {
860
-            return false;
861
-        }
862
-        $this->_route = false;
863
-        // check that the page_routes array is not empty
864
-        if (empty($this->_page_routes)) {
865
-            // user error msg
866
-            $error_msg = sprintf(
867
-                esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
868
-                $this->_admin_page_title
869
-            );
870
-            // developer error msg
871
-            $error_msg .= '||' . $error_msg
872
-                          . esc_html__(
873
-                              ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
874
-                              'event_espresso'
875
-                          );
876
-            throw new EE_Error($error_msg);
877
-        }
878
-        // and that the requested page route exists
879
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
880
-            $this->_route = $this->_page_routes[ $this->_req_action ];
881
-            $this->_route_config = isset($this->_page_config[ $this->_req_action ])
882
-                ? $this->_page_config[ $this->_req_action ] : array();
883
-        } else {
884
-            // user error msg
885
-            $error_msg = sprintf(
886
-                esc_html__(
887
-                    'The requested page route does not exist for the %s admin page.',
888
-                    'event_espresso'
889
-                ),
890
-                $this->_admin_page_title
891
-            );
892
-            // developer error msg
893
-            $error_msg .= '||' . $error_msg
894
-                          . sprintf(
895
-                              esc_html__(
896
-                                  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
897
-                                  'event_espresso'
898
-                              ),
899
-                              $this->_req_action
900
-                          );
901
-            throw new EE_Error($error_msg);
902
-        }
903
-        // and that a default route exists
904
-        if (! array_key_exists('default', $this->_page_routes)) {
905
-            // user error msg
906
-            $error_msg = sprintf(
907
-                esc_html__(
908
-                    'A default page route has not been set for the % admin page.',
909
-                    'event_espresso'
910
-                ),
911
-                $this->_admin_page_title
912
-            );
913
-            // developer error msg
914
-            $error_msg .= '||' . $error_msg
915
-                          . esc_html__(
916
-                              ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
917
-                              'event_espresso'
918
-                          );
919
-            throw new EE_Error($error_msg);
920
-        }
921
-
922
-        // first lets' catch if the UI request has EVER been set.
923
-        if ($this->_is_UI_request === null) {
924
-            // lets set if this is a UI request or not.
925
-            $this->_is_UI_request = ! isset($this->_req_data['noheader']) || $this->_req_data['noheader'] !== true;
926
-            // wait a minute... we might have a noheader in the route array
927
-            $this->_is_UI_request = is_array($this->_route)
928
-                                    && isset($this->_route['noheader'])
929
-                                    && $this->_route['noheader'] ? false : $this->_is_UI_request;
930
-        }
931
-        $this->_set_current_labels();
932
-        return true;
933
-    }
934
-
935
-
936
-    /**
937
-     * this method simply verifies a given route and makes sure its an actual route available for the loaded page
938
-     *
939
-     * @param  string $route the route name we're verifying
940
-     * @return mixed (bool|Exception)      we'll throw an exception if this isn't a valid route.
941
-     * @throws EE_Error
942
-     */
943
-    protected function _verify_route($route)
944
-    {
945
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
946
-            return true;
947
-        }
948
-        // user error msg
949
-        $error_msg = sprintf(
950
-            esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
951
-            $this->_admin_page_title
952
-        );
953
-        // developer error msg
954
-        $error_msg .= '||' . $error_msg
955
-                      . sprintf(
956
-                          esc_html__(
957
-                              ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
958
-                              'event_espresso'
959
-                          ),
960
-                          $route
961
-                      );
962
-        throw new EE_Error($error_msg);
963
-    }
964
-
965
-
966
-    /**
967
-     * perform nonce verification
968
-     * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
969
-     * using this method (and save retyping!)
970
-     *
971
-     * @param string $nonce     The nonce sent
972
-     * @param string $nonce_ref The nonce reference string (name0)
973
-     * @return void
974
-     * @throws EE_Error
975
-     * @throws InvalidArgumentException
976
-     * @throws InvalidDataTypeException
977
-     * @throws InvalidInterfaceException
978
-     */
979
-    protected function _verify_nonce($nonce, $nonce_ref)
980
-    {
981
-        // verify nonce against expected value
982
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
983
-            // these are not the droids you are looking for !!!
984
-            $msg = sprintf(
985
-                esc_html__('%sNonce Fail.%s', 'event_espresso'),
986
-                '<a href="http://www.youtube.com/watch?v=56_S0WeTkzs">',
987
-                '</a>'
988
-            );
989
-            if (WP_DEBUG) {
990
-                $msg .= "\n  "
991
-                        . sprintf(
992
-                            esc_html__(
993
-                                'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
994
-                                'event_espresso'
995
-                            ),
996
-                            EE_Admin_Page::class
997
-                        );
998
-            }
999
-            if (! defined('DOING_AJAX')) {
1000
-                wp_die($msg);
1001
-            } else {
1002
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1003
-                $this->_return_json();
1004
-            }
1005
-        }
1006
-    }
1007
-
1008
-
1009
-    /**
1010
-     * _route_admin_request()
1011
-     * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1012
-     * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1013
-     * in the page routes and then will try to load the corresponding method.
1014
-     *
1015
-     * @return void
1016
-     * @throws EE_Error
1017
-     * @throws InvalidArgumentException
1018
-     * @throws InvalidDataTypeException
1019
-     * @throws InvalidInterfaceException
1020
-     * @throws ReflectionException
1021
-     */
1022
-    protected function _route_admin_request()
1023
-    {
1024
-        if (! $this->_is_UI_request) {
1025
-            $this->_verify_routes();
1026
-        }
1027
-        $nonce_check = isset($this->_route_config['require_nonce'])
1028
-            ? $this->_route_config['require_nonce']
1029
-            : true;
1030
-        if ($this->_req_action !== 'default' && $nonce_check) {
1031
-            // set nonce from post data
1032
-            $nonce = isset($this->_req_data[ $this->_req_nonce ])
1033
-                ? sanitize_text_field($this->_req_data[ $this->_req_nonce ])
1034
-                : '';
1035
-            $this->_verify_nonce($nonce, $this->_req_nonce);
1036
-        }
1037
-        // set the nav_tabs array but ONLY if this is  UI_request
1038
-        if ($this->_is_UI_request) {
1039
-            $this->_set_nav_tabs();
1040
-        }
1041
-        // grab callback function
1042
-        $func = is_array($this->_route) && isset($this->_route['func']) ? $this->_route['func'] : $this->_route;
1043
-        // check if callback has args
1044
-        $args = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : array();
1045
-        $error_msg = '';
1046
-        // action right before calling route
1047
-        // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1048
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1049
-            do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1050
-        }
1051
-        // right before calling the route, let's remove _wp_http_referer from the
1052
-        // $_SERVER[REQUEST_URI] global (its now in _req_data for route processing).
1053
-        $_SERVER['REQUEST_URI'] = remove_query_arg(
1054
-            '_wp_http_referer',
1055
-            wp_unslash($_SERVER['REQUEST_URI'])
1056
-        );
1057
-        if (! empty($func)) {
1058
-            if (is_array($func)) {
1059
-                list($class, $method) = $func;
1060
-            } elseif (strpos($func, '::') !== false) {
1061
-                list($class, $method) = explode('::', $func);
1062
-            } else {
1063
-                $class = $this;
1064
-                $method = $func;
1065
-            }
1066
-            if (! (is_object($class) && $class === $this)) {
1067
-                // send along this admin page object for access by addons.
1068
-                $args['admin_page_object'] = $this;
1069
-            }
1070
-            // is it a method on a class that doesn't work?
1071
-            if (
1072
-                ((method_exists($class, $method)
1073
-                  && call_user_func_array(array($class, $method), $args) === false)
1074
-                 && (// is it a standalone function that doesn't work?
1075
-                     function_exists($method)
1076
-                     && call_user_func_array(
1077
-                         $func,
1078
-                         array_merge(array('admin_page_object' => $this), $args)
1079
-                     ) === false
1080
-                 )) || (// is it neither a class method NOR a standalone function?
1081
-                    ! function_exists($method)
1082
-                    && ! method_exists($class, $method)
1083
-                )
1084
-            ) {
1085
-                // user error msg
1086
-                $error_msg = esc_html__(
1087
-                    'An error occurred. The  requested page route could not be found.',
1088
-                    'event_espresso'
1089
-                );
1090
-                // developer error msg
1091
-                $error_msg .= '||';
1092
-                $error_msg .= sprintf(
1093
-                    esc_html__(
1094
-                        'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1095
-                        'event_espresso'
1096
-                    ),
1097
-                    $method
1098
-                );
1099
-            }
1100
-            if (! empty($error_msg)) {
1101
-                throw new EE_Error($error_msg);
1102
-            }
1103
-        }
1104
-        // if we've routed and this route has a no headers route AND a sent_headers_route,
1105
-        // then we need to reset the routing properties to the new route.
1106
-        // 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.
1107
-        if (
1108
-            $this->_is_UI_request === false
1109
-            && is_array($this->_route)
1110
-            && ! empty($this->_route['headers_sent_route'])
1111
-        ) {
1112
-            $this->_reset_routing_properties($this->_route['headers_sent_route']);
1113
-        }
1114
-    }
1115
-
1116
-
1117
-    /**
1118
-     * This method just allows the resetting of page properties in the case where a no headers
1119
-     * route redirects to a headers route in its route config.
1120
-     *
1121
-     * @since   4.3.0
1122
-     * @param  string $new_route New (non header) route to redirect to.
1123
-     * @return   void
1124
-     * @throws ReflectionException
1125
-     * @throws InvalidArgumentException
1126
-     * @throws InvalidInterfaceException
1127
-     * @throws InvalidDataTypeException
1128
-     * @throws EE_Error
1129
-     */
1130
-    protected function _reset_routing_properties($new_route)
1131
-    {
1132
-        $this->_is_UI_request = true;
1133
-        // now we set the current route to whatever the headers_sent_route is set at
1134
-        $this->_req_data['action'] = $new_route;
1135
-        // rerun page setup
1136
-        $this->_page_setup();
1137
-    }
1138
-
1139
-
1140
-    /**
1141
-     * _add_query_arg
1142
-     * adds nonce to array of arguments then calls WP add_query_arg function
1143
-     *(internally just uses EEH_URL's function with the same name)
1144
-     *
1145
-     * @param array  $args
1146
-     * @param string $url
1147
-     * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1148
-     *                                        generated url in an associative array indexed by the key 'wp_referer';
1149
-     *                                        Example usage: If the current page is:
1150
-     *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1151
-     *                                        &action=default&event_id=20&month_range=March%202015
1152
-     *                                        &_wpnonce=5467821
1153
-     *                                        and you call:
1154
-     *                                        EE_Admin_Page::add_query_args_and_nonce(
1155
-     *                                        array(
1156
-     *                                        'action' => 'resend_something',
1157
-     *                                        'page=>espresso_registrations'
1158
-     *                                        ),
1159
-     *                                        $some_url,
1160
-     *                                        true
1161
-     *                                        );
1162
-     *                                        It will produce a url in this structure:
1163
-     *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1164
-     *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1165
-     *                                        month_range]=March%202015
1166
-     * @param   bool $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1167
-     * @return string
1168
-     */
1169
-    public static function add_query_args_and_nonce(
1170
-        $args = array(),
1171
-        $url = '',
1172
-        $sticky = false,
1173
-        $exclude_nonce = false
1174
-    ) {
1175
-        // if there is a _wp_http_referer include the values from the request but only if sticky = true
1176
-        if ($sticky) {
1177
-            $request = $_REQUEST;
1178
-            unset($request['_wp_http_referer'], $request['wp_referer']);
1179
-            foreach ($request as $key => $value) {
1180
-                // do not add nonces
1181
-                if (strpos($key, 'nonce') !== false) {
1182
-                    continue;
1183
-                }
1184
-                $args[ 'wp_referer[' . $key . ']' ] = $value;
1185
-            }
1186
-        }
1187
-        return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce);
1188
-    }
1189
-
1190
-
1191
-    /**
1192
-     * This returns a generated link that will load the related help tab.
1193
-     *
1194
-     * @param  string $help_tab_id the id for the connected help tab
1195
-     * @param  string $icon_style  (optional) include css class for the style you want to use for the help icon.
1196
-     * @param  string $help_text   (optional) send help text you want to use for the link if default not to be used
1197
-     * @uses EEH_Template::get_help_tab_link()
1198
-     * @return string              generated link
1199
-     */
1200
-    protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1201
-    {
1202
-        return EEH_Template::get_help_tab_link(
1203
-            $help_tab_id,
1204
-            $this->page_slug,
1205
-            $this->_req_action,
1206
-            $icon_style,
1207
-            $help_text
1208
-        );
1209
-    }
1210
-
1211
-
1212
-    /**
1213
-     * _add_help_tabs
1214
-     * Note child classes define their help tabs within the page_config array.
1215
-     *
1216
-     * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1217
-     * @return void
1218
-     * @throws DomainException
1219
-     * @throws EE_Error
1220
-     * @throws ReflectionException
1221
-     */
1222
-    protected function _add_help_tabs()
1223
-    {
1224
-        $tour_buttons = '';
1225
-        if (isset($this->_page_config[ $this->_req_action ])) {
1226
-            $config = $this->_page_config[ $this->_req_action ];
1227
-            // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1228
-            // is there a help tour for the current route?  if there is let's setup the tour buttons
1229
-            // if (isset($this->_help_tour[ $this->_req_action ])) {
1230
-            //     $tb = array();
1231
-            //     $tour_buttons = '<div class="ee-abs-container"><div class="ee-help-tour-restart-buttons">';
1232
-            //     foreach ($this->_help_tour['tours'] as $tour) {
1233
-            //         // if this is the end tour then we don't need to setup a button
1234
-            //         if ($tour instanceof EE_Help_Tour_final_stop || ! $tour instanceof EE_Help_Tour) {
1235
-            //             continue;
1236
-            //         }
1237
-            //         $tb[] = '<button id="trigger-tour-'
1238
-            //                 . $tour->get_slug()
1239
-            //                 . '" class="button-primary trigger-ee-help-tour">'
1240
-            //                 . $tour->get_label()
1241
-            //                 . '</button>';
1242
-            //     }
1243
-            //     $tour_buttons .= implode('<br />', $tb);
1244
-            //     $tour_buttons .= '</div></div>';
1245
-            // }
1246
-            // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1247
-            if (is_array($config) && isset($config['help_sidebar'])) {
1248
-                // check that the callback given is valid
1249
-                if (! method_exists($this, $config['help_sidebar'])) {
1250
-                    throw new EE_Error(
1251
-                        sprintf(
1252
-                            esc_html__(
1253
-                                'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1254
-                                'event_espresso'
1255
-                            ),
1256
-                            $config['help_sidebar'],
1257
-                            get_class($this)
1258
-                        )
1259
-                    );
1260
-                }
1261
-                $content = apply_filters(
1262
-                    'FHEE__' . get_class($this) . '__add_help_tabs__help_sidebar',
1263
-                    $this->{$config['help_sidebar']}()
1264
-                );
1265
-                $content .= $tour_buttons; // add help tour buttons.
1266
-                // do we have any help tours setup?  Cause if we do we want to add the buttons
1267
-                $this->_current_screen->set_help_sidebar($content);
1268
-            }
1269
-            // if there ARE tour buttons...
1270
-            if (! empty($tour_buttons)) {
1271
-                // if we DON'T have config help sidebar then we'll just add the tour buttons to the sidebar.
1272
-                if (! isset($config['help_sidebar'])) {
1273
-                    $this->_current_screen->set_help_sidebar($tour_buttons);
1274
-                }
1275
-                // handle if no help_tabs are set so the sidebar will still show for the help tour buttons
1276
-                if (! isset($config['help_tabs'])) {
1277
-                    $_ht['id'] = $this->page_slug;
1278
-                    $_ht['title'] = esc_html__('Help Tours', 'event_espresso');
1279
-                    $_ht['content'] = '<p>'
1280
-                                      . esc_html__(
1281
-                                          'The buttons to the right allow you to start/restart any help tours available for this page',
1282
-                                          'event_espresso'
1283
-                                      ) . '</p>';
1284
-                    $this->_current_screen->add_help_tab($_ht);
1285
-                }
1286
-            }
1287
-            if (! isset($config['help_tabs'])) {
1288
-                return;
1289
-            } //no help tabs for this route
1290
-            foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1291
-                // we're here so there ARE help tabs!
1292
-                // make sure we've got what we need
1293
-                if (! isset($cfg['title'])) {
1294
-                    throw new EE_Error(
1295
-                        esc_html__(
1296
-                            'The _page_config array is not set up properly for help tabs.  It is missing a title',
1297
-                            'event_espresso'
1298
-                        )
1299
-                    );
1300
-                }
1301
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1302
-                    throw new EE_Error(
1303
-                        esc_html__(
1304
-                            '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',
1305
-                            'event_espresso'
1306
-                        )
1307
-                    );
1308
-                }
1309
-                // first priority goes to content.
1310
-                if (! empty($cfg['content'])) {
1311
-                    $content = ! empty($cfg['content']) ? $cfg['content'] : null;
1312
-                    // second priority goes to filename
1313
-                } elseif (! empty($cfg['filename'])) {
1314
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1315
-                    // 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)
1316
-                    $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1317
-                                                             . basename($this->_get_dir())
1318
-                                                             . '/help_tabs/'
1319
-                                                             . $cfg['filename']
1320
-                                                             . '.help_tab.php' : $file_path;
1321
-                    // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1322
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1323
-                        EE_Error::add_error(
1324
-                            sprintf(
1325
-                                esc_html__(
1326
-                                    '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',
1327
-                                    'event_espresso'
1328
-                                ),
1329
-                                $tab_id,
1330
-                                key($config),
1331
-                                $file_path
1332
-                            ),
1333
-                            __FILE__,
1334
-                            __FUNCTION__,
1335
-                            __LINE__
1336
-                        );
1337
-                        return;
1338
-                    }
1339
-                    $template_args['admin_page_obj'] = $this;
1340
-                    $content = EEH_Template::display_template(
1341
-                        $file_path,
1342
-                        $template_args,
1343
-                        true
1344
-                    );
1345
-                } else {
1346
-                    $content = '';
1347
-                }
1348
-                // check if callback is valid
1349
-                if (
1350
-                    empty($content) && (
1351
-                        ! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1352
-                    )
1353
-                ) {
1354
-                    EE_Error::add_error(
1355
-                        sprintf(
1356
-                            esc_html__(
1357
-                                '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.',
1358
-                                'event_espresso'
1359
-                            ),
1360
-                            $cfg['title']
1361
-                        ),
1362
-                        __FILE__,
1363
-                        __FUNCTION__,
1364
-                        __LINE__
1365
-                    );
1366
-                    return;
1367
-                }
1368
-                // setup config array for help tab method
1369
-                $id = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1370
-                $_ht = array(
1371
-                    'id'       => $id,
1372
-                    'title'    => $cfg['title'],
1373
-                    'callback' => isset($cfg['callback']) && empty($content) ? array($this, $cfg['callback']) : null,
1374
-                    'content'  => $content,
1375
-                );
1376
-                $this->_current_screen->add_help_tab($_ht);
1377
-            }
1378
-        }
1379
-    }
1380
-
1381
-
1382
-    /**
1383
-     * This basically checks loaded $_page_config property to see if there are any help_tours defined.  "help_tours" is
1384
-     * an array with properties for setting up usage of the joyride plugin
1385
-     *
1386
-     * @link   http://zurb.com/playground/jquery-joyride-feature-tour-plugin
1387
-     * @see    instructions regarding the format and construction of the "help_tour" array element is found in the
1388
-     *         _set_page_config() comments
1389
-     * @return void
1390
-     * @throws EE_Error
1391
-     * @throws InvalidArgumentException
1392
-     * @throws InvalidDataTypeException
1393
-     * @throws InvalidInterfaceException
1394
-     * @throws ReflectionException
1395
-     */
1396
-    protected function _add_help_tour()
1397
-    {
1398
-        // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1399
-        // $tours = array();
1400
-        // $this->_help_tour = array();
1401
-        // // exit early if help tours are turned off globally
1402
-        // if ((defined('EE_DISABLE_HELP_TOURS') && EE_DISABLE_HELP_TOURS)
1403
-        //     || ! EE_Registry::instance()->CFG->admin->help_tour_activation
1404
-        // ) {
1405
-        //     return;
1406
-        // }
1407
-        // // loop through _page_config to find any help_tour defined
1408
-        // foreach ($this->_page_config as $route => $config) {
1409
-        //     // we're only going to set things up for this route
1410
-        //     if ($route !== $this->_req_action) {
1411
-        //         continue;
1412
-        //     }
1413
-        //     if (isset($config['help_tour'])) {
1414
-        //         foreach ($config['help_tour'] as $tour) {
1415
-        //             $file_path = $this->_get_dir() . '/help_tours/' . $tour . '.class.php';
1416
-        //             // let's see if we can get that file...
1417
-        //             // if not its possible this is a decaf route not set in caffeinated
1418
-        //             // so lets try and get the caffeinated equivalent
1419
-        //             $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1420
-        //                                                      . basename($this->_get_dir())
1421
-        //                                                      . '/help_tours/'
1422
-        //                                                      . $tour
1423
-        //                                                      . '.class.php' : $file_path;
1424
-        //             // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1425
-        //             if (! is_readable($file_path)) {
1426
-        //                 EE_Error::add_error(
1427
-        //                     sprintf(
1428
-        //                         esc_html__(
1429
-        //                             'The file path given for the help tour (%s) is not a valid path.  Please check that the string you set for the help tour on this route (%s) is the correct spelling',
1430
-        //                             'event_espresso'
1431
-        //                         ),
1432
-        //                         $file_path,
1433
-        //                         $tour
1434
-        //                     ),
1435
-        //                     __FILE__,
1436
-        //                     __FUNCTION__,
1437
-        //                     __LINE__
1438
-        //                 );
1439
-        //                 return;
1440
-        //             }
1441
-        //             require_once $file_path;
1442
-        //             if (! class_exists($tour)) {
1443
-        //                 $error_msg[] = sprintf(
1444
-        //                     esc_html__('Something went wrong with loading the %s Help Tour Class.', 'event_espresso'),
1445
-        //                     $tour
1446
-        //                 );
1447
-        //                 $error_msg[] = $error_msg[0] . "\r\n"
1448
-        //                                . sprintf(
1449
-        //                                    esc_html__(
1450
-        //                                        'There is no class in place for the %s help tour.%s Make sure you have <strong>%s</strong> defined in the "help_tour" array for the %s route of the % admin page.',
1451
-        //                                        'event_espresso'
1452
-        //                                    ),
1453
-        //                                    $tour,
1454
-        //                                    '<br />',
1455
-        //                                    $tour,
1456
-        //                                    $this->_req_action,
1457
-        //                                    get_class($this)
1458
-        //                                );
1459
-        //                 throw new EE_Error(implode('||', $error_msg));
1460
-        //             }
1461
-        //             $tour_obj = new $tour($this->_is_caf);
1462
-        //             $tours[] = $tour_obj;
1463
-        //             $this->_help_tour[ $route ][] = EEH_Template::help_tour_stops_generator($tour_obj);
1464
-        //         }
1465
-        //         // let's inject the end tour stop element common to all pages... this will only get seen once per machine.
1466
-        //         $end_stop_tour = new EE_Help_Tour_final_stop($this->_is_caf);
1467
-        //         $tours[] = $end_stop_tour;
1468
-        //         $this->_help_tour[ $route ][] = EEH_Template::help_tour_stops_generator($end_stop_tour);
1469
-        //     }
1470
-        // }
1471
-        //
1472
-        // if (! empty($tours)) {
1473
-        //     $this->_help_tour['tours'] = $tours;
1474
-        // }
1475
-        // // that's it!  Now that the $_help_tours property is set (or not)
1476
-        // // the scripts and html should be taken care of automatically.
1477
-        //
1478
-        // /**
1479
-        //  * Allow extending the help tours variable.
1480
-        //  *
1481
-        //  * @param Array $_help_tour The array containing all help tour information to be displayed.
1482
-        //  */
1483
-        // $this->_help_tour = apply_filters('FHEE__EE_Admin_Page___add_help_tour___help_tour', $this->_help_tour);
1484
-    }
1485
-
1486
-
1487
-    /**
1488
-     * This simply sets up any qtips that have been defined in the page config
1489
-     *
1490
-     * @return void
1491
-     * @throws ReflectionException
1492
-     * @throws EE_Error
1493
-     */
1494
-    protected function _add_qtips()
1495
-    {
1496
-        if (isset($this->_route_config['qtips'])) {
1497
-            $qtips = (array) $this->_route_config['qtips'];
1498
-            // load qtip loader
1499
-            $path = array(
1500
-                $this->_get_dir() . '/qtips/',
1501
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1502
-            );
1503
-            $qtip_loader = EEH_Qtip_Loader::instance();
1504
-            if ($qtip_loader instanceof EEH_Qtip_Loader) {
1505
-                $qtip_loader->register($qtips, $path);
1506
-            }
1507
-        }
1508
-    }
1509
-
1510
-
1511
-    /**
1512
-     * _set_nav_tabs
1513
-     * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1514
-     * wish to add additional tabs or modify accordingly.
1515
-     *
1516
-     * @return void
1517
-     * @throws InvalidArgumentException
1518
-     * @throws InvalidInterfaceException
1519
-     * @throws InvalidDataTypeException
1520
-     */
1521
-    protected function _set_nav_tabs()
1522
-    {
1523
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1524
-        $i = 0;
1525
-        foreach ($this->_page_config as $slug => $config) {
1526
-            if (
1527
-                ! is_array($config)
1528
-                || (
1529
-                    is_array($config)
1530
-                    && (
1531
-                        (isset($config['nav']) && ! $config['nav'])
1532
-                        || ! isset($config['nav'])
1533
-                    )
1534
-                )
1535
-            ) {
1536
-                continue;
1537
-            }
1538
-            // no nav tab for this config
1539
-            // check for persistent flag
1540
-            if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1541
-                // nav tab is only to appear when route requested.
1542
-                continue;
1543
-            }
1544
-            if (! $this->check_user_access($slug, true)) {
1545
-                // no nav tab because current user does not have access.
1546
-                continue;
1547
-            }
1548
-            $css_class = isset($config['css_class']) ? $config['css_class'] . ' ' : '';
1549
-            $this->_nav_tabs[ $slug ] = array(
1550
-                'url'       => isset($config['nav']['url'])
1551
-                    ? $config['nav']['url']
1552
-                    : EE_Admin_Page::add_query_args_and_nonce(
1553
-                        array('action' => $slug),
1554
-                        $this->_admin_base_url
1555
-                    ),
1556
-                'link_text' => isset($config['nav']['label'])
1557
-                    ? $config['nav']['label']
1558
-                    : ucwords(
1559
-                        str_replace('_', ' ', $slug)
1560
-                    ),
1561
-                'css_class' => $this->_req_action === $slug ? $css_class . 'nav-tab-active' : $css_class,
1562
-                'order'     => isset($config['nav']['order']) ? $config['nav']['order'] : $i,
1563
-            );
1564
-            $i++;
1565
-        }
1566
-        // if $this->_nav_tabs is empty then lets set the default
1567
-        if (empty($this->_nav_tabs)) {
1568
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = array(
1569
-                'url'       => $this->_admin_base_url,
1570
-                'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1571
-                'css_class' => 'nav-tab-active',
1572
-                'order'     => 10,
1573
-            );
1574
-        }
1575
-        // now let's sort the tabs according to order
1576
-        usort($this->_nav_tabs, array($this, '_sort_nav_tabs'));
1577
-    }
1578
-
1579
-
1580
-    /**
1581
-     * _set_current_labels
1582
-     * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1583
-     * property array
1584
-     *
1585
-     * @return void
1586
-     */
1587
-    private function _set_current_labels()
1588
-    {
1589
-        if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1590
-            foreach ($this->_route_config['labels'] as $label => $text) {
1591
-                if (is_array($text)) {
1592
-                    foreach ($text as $sublabel => $subtext) {
1593
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1594
-                    }
1595
-                } else {
1596
-                    $this->_labels[ $label ] = $text;
1597
-                }
1598
-            }
1599
-        }
1600
-    }
1601
-
1602
-
1603
-    /**
1604
-     *        verifies user access for this admin page
1605
-     *
1606
-     * @param string $route_to_check if present then the capability for the route matching this string is checked.
1607
-     * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1608
-     *                               return false if verify fail.
1609
-     * @return bool
1610
-     * @throws InvalidArgumentException
1611
-     * @throws InvalidDataTypeException
1612
-     * @throws InvalidInterfaceException
1613
-     */
1614
-    public function check_user_access($route_to_check = '', $verify_only = false)
1615
-    {
1616
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1617
-        $route_to_check = empty($route_to_check) ? $this->_req_action : $route_to_check;
1618
-        $capability = ! empty($route_to_check) && isset($this->_page_routes[ $route_to_check ])
1619
-                      && is_array(
1620
-                          $this->_page_routes[ $route_to_check ]
1621
-                      )
1622
-                      && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1623
-            ? $this->_page_routes[ $route_to_check ]['capability'] : null;
1624
-        if (empty($capability) && empty($route_to_check)) {
1625
-            $capability = is_array($this->_route) && empty($this->_route['capability']) ? 'manage_options'
1626
-                : $this->_route['capability'];
1627
-        } else {
1628
-            $capability = empty($capability) ? 'manage_options' : $capability;
1629
-        }
1630
-        $id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1631
-        if (
1632
-            ! defined('DOING_AJAX')
1633
-            && (
1634
-                ! function_exists('is_admin')
1635
-                || ! EE_Registry::instance()->CAP->current_user_can(
1636
-                    $capability,
1637
-                    $this->page_slug
1638
-                    . '_'
1639
-                    . $route_to_check,
1640
-                    $id
1641
-                )
1642
-            )
1643
-        ) {
1644
-            if ($verify_only) {
1645
-                return false;
1646
-            }
1647
-            if (is_user_logged_in()) {
1648
-                wp_die(__('You do not have access to this route.', 'event_espresso'));
1649
-            } else {
1650
-                return false;
1651
-            }
1652
-        }
1653
-        return true;
1654
-    }
1655
-
1656
-
1657
-    /**
1658
-     * admin_init_global
1659
-     * This runs all the code that we want executed within the WP admin_init hook.
1660
-     * This method executes for ALL EE Admin pages.
1661
-     *
1662
-     * @return void
1663
-     */
1664
-    public function admin_init_global()
1665
-    {
1666
-    }
1667
-
1668
-
1669
-    /**
1670
-     * wp_loaded_global
1671
-     * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1672
-     * EE_Admin page and will execute on every EE Admin Page load
1673
-     *
1674
-     * @return void
1675
-     */
1676
-    public function wp_loaded()
1677
-    {
1678
-    }
1679
-
1680
-
1681
-    /**
1682
-     * admin_notices
1683
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1684
-     * ALL EE_Admin pages.
1685
-     *
1686
-     * @return void
1687
-     */
1688
-    public function admin_notices_global()
1689
-    {
1690
-        $this->_display_no_javascript_warning();
1691
-        $this->_display_espresso_notices();
1692
-    }
1693
-
1694
-
1695
-    public function network_admin_notices_global()
1696
-    {
1697
-        $this->_display_no_javascript_warning();
1698
-        $this->_display_espresso_notices();
1699
-    }
1700
-
1701
-
1702
-    /**
1703
-     * admin_footer_scripts_global
1704
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1705
-     * will apply on ALL EE_Admin pages.
1706
-     *
1707
-     * @return void
1708
-     */
1709
-    public function admin_footer_scripts_global()
1710
-    {
1711
-        $this->_add_admin_page_ajax_loading_img();
1712
-        $this->_add_admin_page_overlay();
1713
-        // if metaboxes are present we need to add the nonce field
1714
-        if (
1715
-            isset($this->_route_config['metaboxes'])
1716
-            || isset($this->_route_config['list_table'])
1717
-            || (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1718
-        ) {
1719
-            wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1720
-            wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1721
-        }
1722
-    }
1723
-
1724
-
1725
-    /**
1726
-     * admin_footer_global
1727
-     * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1728
-     * This particular method will apply on ALL EE_Admin Pages.
1729
-     *
1730
-     * @return void
1731
-     * @throws InvalidArgumentException
1732
-     * @throws InvalidDataTypeException
1733
-     * @throws InvalidInterfaceException
1734
-     */
1735
-    public function admin_footer_global()
1736
-    {
1737
-        // dialog container for dialog helper
1738
-        $d_cont = '<div class="ee-admin-dialog-container auto-hide hidden">' . "\n";
1739
-        $d_cont .= '<div class="ee-notices"></div>';
1740
-        $d_cont .= '<div class="ee-admin-dialog-container-inner-content"></div>';
1741
-        $d_cont .= '</div>';
1742
-        echo $d_cont;
1743
-        // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1744
-        // help tour stuff?
1745
-        // if (isset($this->_help_tour[ $this->_req_action ])) {
1746
-        //     echo implode('<br />', $this->_help_tour[ $this->_req_action ]);
1747
-        // }
1748
-        // current set timezone for timezone js
1749
-        echo '<span id="current_timezone" class="hidden">' . EEH_DTT_Helper::get_timezone() . '</span>';
1750
-    }
1751
-
1752
-
1753
-    /**
1754
-     * This function sees if there is a method for help popup content existing for the given route.  If there is then
1755
-     * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1756
-     * help popups then in your templates or your content you set "triggers" for the content using the
1757
-     * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1758
-     * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1759
-     * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1760
-     * for the
1761
-     * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1762
-     * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1763
-     *    'help_trigger_id' => array(
1764
-     *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1765
-     *        'content' => esc_html__('localized content for popup', 'event_espresso')
1766
-     *    )
1767
-     * );
1768
-     * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1769
-     *
1770
-     * @param array $help_array
1771
-     * @param bool  $display
1772
-     * @return string content
1773
-     * @throws DomainException
1774
-     * @throws EE_Error
1775
-     */
1776
-    protected function _set_help_popup_content($help_array = array(), $display = false)
1777
-    {
1778
-        $content = '';
1779
-        $help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1780
-        // loop through the array and setup content
1781
-        foreach ($help_array as $trigger => $help) {
1782
-            // make sure the array is setup properly
1783
-            if (! isset($help['title'], $help['content'])) {
1784
-                throw new EE_Error(
1785
-                    esc_html__(
1786
-                        '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',
1787
-                        'event_espresso'
1788
-                    )
1789
-                );
1790
-            }
1791
-            // we're good so let'd setup the template vars and then assign parsed template content to our content.
1792
-            $template_args = array(
1793
-                'help_popup_id'      => $trigger,
1794
-                'help_popup_title'   => $help['title'],
1795
-                'help_popup_content' => $help['content'],
1796
-            );
1797
-            $content .= EEH_Template::display_template(
1798
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1799
-                $template_args,
1800
-                true
1801
-            );
1802
-        }
1803
-        if ($display) {
1804
-            echo $content;
1805
-            return '';
1806
-        }
1807
-        return $content;
1808
-    }
1809
-
1810
-
1811
-    /**
1812
-     * All this does is retrieve the help content array if set by the EE_Admin_Page child
1813
-     *
1814
-     * @return array properly formatted array for help popup content
1815
-     * @throws EE_Error
1816
-     */
1817
-    private function _get_help_content()
1818
-    {
1819
-        // what is the method we're looking for?
1820
-        $method_name = '_help_popup_content_' . $this->_req_action;
1821
-        // if method doesn't exist let's get out.
1822
-        if (! method_exists($this, $method_name)) {
1823
-            return array();
1824
-        }
1825
-        // k we're good to go let's retrieve the help array
1826
-        $help_array = $this->{$method_name}();
1827
-        // make sure we've got an array!
1828
-        if (! is_array($help_array)) {
1829
-            throw new EE_Error(
1830
-                esc_html__(
1831
-                    'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1832
-                    'event_espresso'
1833
-                )
1834
-            );
1835
-        }
1836
-        return $help_array;
1837
-    }
1838
-
1839
-
1840
-    /**
1841
-     * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1842
-     * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1843
-     * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1844
-     *
1845
-     * @param string  $trigger_id reference for retrieving the trigger content for the popup
1846
-     * @param boolean $display    if false then we return the trigger string
1847
-     * @param array   $dimensions an array of dimensions for the box (array(h,w))
1848
-     * @return string
1849
-     * @throws DomainException
1850
-     * @throws EE_Error
1851
-     */
1852
-    protected function _set_help_trigger($trigger_id, $display = true, $dimensions = array('400', '640'))
1853
-    {
1854
-        if (defined('DOING_AJAX')) {
1855
-            return '';
1856
-        }
1857
-        // 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
1858
-        $help_array = $this->_get_help_content();
1859
-        $help_content = '';
1860
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1861
-            $help_array[ $trigger_id ] = array(
1862
-                'title'   => esc_html__('Missing Content', 'event_espresso'),
1863
-                'content' => esc_html__(
1864
-                    '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.)',
1865
-                    'event_espresso'
1866
-                ),
1867
-            );
1868
-            $help_content = $this->_set_help_popup_content($help_array);
1869
-        }
1870
-        // let's setup the trigger
1871
-        $content = '<a class="ee-dialog" href="?height='
1872
-                   . $dimensions[0]
1873
-                   . '&width='
1874
-                   . $dimensions[1]
1875
-                   . '&inlineId='
1876
-                   . $trigger_id
1877
-                   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1878
-        $content .= $help_content;
1879
-        if ($display) {
1880
-            echo $content;
1881
-            return '';
1882
-        }
1883
-        return $content;
1884
-    }
1885
-
1886
-
1887
-    /**
1888
-     * _add_global_screen_options
1889
-     * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1890
-     * This particular method will add_screen_options on ALL EE_Admin Pages
1891
-     *
1892
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1893
-     *         see also WP_Screen object documents...
1894
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1895
-     * @abstract
1896
-     * @return void
1897
-     */
1898
-    private function _add_global_screen_options()
1899
-    {
1900
-    }
1901
-
1902
-
1903
-    /**
1904
-     * _add_global_feature_pointers
1905
-     * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1906
-     * This particular method will implement feature pointers for ALL EE_Admin pages.
1907
-     * Note: this is just a placeholder for now.  Implementation will come down the road
1908
-     *
1909
-     * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1910
-     *         extended) also see:
1911
-     * @link   http://eamann.com/tech/wordpress-portland/
1912
-     * @abstract
1913
-     * @return void
1914
-     */
1915
-    private function _add_global_feature_pointers()
1916
-    {
1917
-    }
1918
-
1919
-
1920
-    /**
1921
-     * load_global_scripts_styles
1922
-     * The scripts and styles enqueued in here will be loaded on every EE Admin page
1923
-     *
1924
-     * @return void
1925
-     * @throws EE_Error
1926
-     */
1927
-    public function load_global_scripts_styles()
1928
-    {
1929
-        // add debugging styles
1930
-        if (WP_DEBUG) {
1931
-            add_action('admin_head', array($this, 'add_xdebug_style'));
1932
-        }
1933
-        // taking care of metaboxes
1934
-        if (
1935
-            empty($this->_cpt_route)
1936
-            && (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1937
-        ) {
1938
-            wp_enqueue_script('dashboard');
1939
-        }
1940
-
1941
-        // LOCALIZED DATA
1942
-        // localize script for ajax lazy loading
1943
-        wp_localize_script(
1944
-            EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1945
-            'eeLazyLoadingContainers',
1946
-            apply_filters(
1947
-                'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1948
-                ['espresso_news_post_box_content']
1949
-            )
1950
-        );
1951
-        // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1952
-        // /**
1953
-        //  * help tour stuff
1954
-        //  */
1955
-        // if (! empty($this->_help_tour)) {
1956
-        //     // register the js for kicking things off
1957
-        //     wp_enqueue_script(
1958
-        //         'ee-help-tour',
1959
-        //         EE_ADMIN_URL . 'assets/ee-help-tour.js',
1960
-        //         array('jquery-joyride'),
1961
-        //         EVENT_ESPRESSO_VERSION,
1962
-        //         true
1963
-        //     );
1964
-        //     $tours = array();
1965
-        //     // setup tours for the js tour object
1966
-        //     foreach ($this->_help_tour['tours'] as $tour) {
1967
-        //         if ($tour instanceof EE_Help_Tour) {
1968
-        //             $tours[] = array(
1969
-        //                 'id'      => $tour->get_slug(),
1970
-        //                 'options' => $tour->get_options(),
1971
-        //             );
1972
-        //         }
1973
-        //     }
1974
-        //     wp_localize_script('ee-help-tour', 'EE_HELP_TOUR', array('tours' => $tours));
1975
-        //     // admin_footer_global will take care of making sure our help_tour skeleton gets printed via the info stored in $this->_help_tour
1976
-        // }
1977
-    }
1978
-
1979
-
1980
-    /**
1981
-     *        admin_footer_scripts_eei18n_js_strings
1982
-     *
1983
-     * @return        void
1984
-     */
1985
-    public function admin_footer_scripts_eei18n_js_strings()
1986
-    {
1987
-        EE_Registry::$i18n_js_strings['ajax_url'] = WP_AJAX_URL;
1988
-        EE_Registry::$i18n_js_strings['confirm_delete'] = esc_html__(
1989
-            '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!!!',
1990
-            'event_espresso'
1991
-        );
1992
-        EE_Registry::$i18n_js_strings['January'] = esc_html__('January', 'event_espresso');
1993
-        EE_Registry::$i18n_js_strings['February'] = esc_html__('February', 'event_espresso');
1994
-        EE_Registry::$i18n_js_strings['March'] = esc_html__('March', 'event_espresso');
1995
-        EE_Registry::$i18n_js_strings['April'] = esc_html__('April', 'event_espresso');
1996
-        EE_Registry::$i18n_js_strings['May'] = esc_html__('May', 'event_espresso');
1997
-        EE_Registry::$i18n_js_strings['June'] = esc_html__('June', 'event_espresso');
1998
-        EE_Registry::$i18n_js_strings['July'] = esc_html__('July', 'event_espresso');
1999
-        EE_Registry::$i18n_js_strings['August'] = esc_html__('August', 'event_espresso');
2000
-        EE_Registry::$i18n_js_strings['September'] = esc_html__('September', 'event_espresso');
2001
-        EE_Registry::$i18n_js_strings['October'] = esc_html__('October', 'event_espresso');
2002
-        EE_Registry::$i18n_js_strings['November'] = esc_html__('November', 'event_espresso');
2003
-        EE_Registry::$i18n_js_strings['December'] = esc_html__('December', 'event_espresso');
2004
-        EE_Registry::$i18n_js_strings['Jan'] = esc_html__('Jan', 'event_espresso');
2005
-        EE_Registry::$i18n_js_strings['Feb'] = esc_html__('Feb', 'event_espresso');
2006
-        EE_Registry::$i18n_js_strings['Mar'] = esc_html__('Mar', 'event_espresso');
2007
-        EE_Registry::$i18n_js_strings['Apr'] = esc_html__('Apr', 'event_espresso');
2008
-        EE_Registry::$i18n_js_strings['May'] = esc_html__('May', 'event_espresso');
2009
-        EE_Registry::$i18n_js_strings['Jun'] = esc_html__('Jun', 'event_espresso');
2010
-        EE_Registry::$i18n_js_strings['Jul'] = esc_html__('Jul', 'event_espresso');
2011
-        EE_Registry::$i18n_js_strings['Aug'] = esc_html__('Aug', 'event_espresso');
2012
-        EE_Registry::$i18n_js_strings['Sep'] = esc_html__('Sep', 'event_espresso');
2013
-        EE_Registry::$i18n_js_strings['Oct'] = esc_html__('Oct', 'event_espresso');
2014
-        EE_Registry::$i18n_js_strings['Nov'] = esc_html__('Nov', 'event_espresso');
2015
-        EE_Registry::$i18n_js_strings['Dec'] = esc_html__('Dec', 'event_espresso');
2016
-        EE_Registry::$i18n_js_strings['Sunday'] = esc_html__('Sunday', 'event_espresso');
2017
-        EE_Registry::$i18n_js_strings['Monday'] = esc_html__('Monday', 'event_espresso');
2018
-        EE_Registry::$i18n_js_strings['Tuesday'] = esc_html__('Tuesday', 'event_espresso');
2019
-        EE_Registry::$i18n_js_strings['Wednesday'] = esc_html__('Wednesday', 'event_espresso');
2020
-        EE_Registry::$i18n_js_strings['Thursday'] = esc_html__('Thursday', 'event_espresso');
2021
-        EE_Registry::$i18n_js_strings['Friday'] = esc_html__('Friday', 'event_espresso');
2022
-        EE_Registry::$i18n_js_strings['Saturday'] = esc_html__('Saturday', 'event_espresso');
2023
-        EE_Registry::$i18n_js_strings['Sun'] = esc_html__('Sun', 'event_espresso');
2024
-        EE_Registry::$i18n_js_strings['Mon'] = esc_html__('Mon', 'event_espresso');
2025
-        EE_Registry::$i18n_js_strings['Tue'] = esc_html__('Tue', 'event_espresso');
2026
-        EE_Registry::$i18n_js_strings['Wed'] = esc_html__('Wed', 'event_espresso');
2027
-        EE_Registry::$i18n_js_strings['Thu'] = esc_html__('Thu', 'event_espresso');
2028
-        EE_Registry::$i18n_js_strings['Fri'] = esc_html__('Fri', 'event_espresso');
2029
-        EE_Registry::$i18n_js_strings['Sat'] = esc_html__('Sat', 'event_espresso');
2030
-    }
2031
-
2032
-
2033
-    /**
2034
-     *        load enhanced xdebug styles for ppl with failing eyesight
2035
-     *
2036
-     * @return        void
2037
-     */
2038
-    public function add_xdebug_style()
2039
-    {
2040
-        echo '<style>.xdebug-error { font-size:1.5em; }</style>';
2041
-    }
2042
-
2043
-
2044
-    /************************/
2045
-    /** LIST TABLE METHODS **/
2046
-    /************************/
2047
-    /**
2048
-     * this sets up the list table if the current view requires it.
2049
-     *
2050
-     * @return void
2051
-     * @throws EE_Error
2052
-     * @throws InvalidArgumentException
2053
-     * @throws InvalidDataTypeException
2054
-     * @throws InvalidInterfaceException
2055
-     */
2056
-    protected function _set_list_table()
2057
-    {
2058
-        // first is this a list_table view?
2059
-        if (! isset($this->_route_config['list_table'])) {
2060
-            return;
2061
-        } //not a list_table view so get out.
2062
-        // list table functions are per view specific (because some admin pages might have more than one list table!)
2063
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
2064
-        if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2065
-            // user error msg
2066
-            $error_msg = esc_html__(
2067
-                'An error occurred. The requested list table views could not be found.',
2068
-                'event_espresso'
2069
-            );
2070
-            // developer error msg
2071
-            $error_msg .= '||'
2072
-                          . sprintf(
2073
-                              esc_html__(
2074
-                                  '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.',
2075
-                                  'event_espresso'
2076
-                              ),
2077
-                              $this->_req_action,
2078
-                              $list_table_view
2079
-                          );
2080
-            throw new EE_Error($error_msg);
2081
-        }
2082
-        // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2083
-        $this->_views = apply_filters(
2084
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2085
-            $this->_views
2086
-        );
2087
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2088
-        $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2089
-        $this->_set_list_table_view();
2090
-        $this->_set_list_table_object();
2091
-    }
2092
-
2093
-
2094
-    /**
2095
-     * set current view for List Table
2096
-     *
2097
-     * @return void
2098
-     */
2099
-    protected function _set_list_table_view()
2100
-    {
2101
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2102
-        // looking at active items or dumpster diving ?
2103
-        if (! isset($this->_req_data['status']) || ! array_key_exists($this->_req_data['status'], $this->_views)) {
2104
-            $this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2105
-        } else {
2106
-            $this->_view = sanitize_key($this->_req_data['status']);
2107
-        }
2108
-    }
2109
-
2110
-
2111
-    /**
2112
-     * _set_list_table_object
2113
-     * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2114
-     *
2115
-     * @throws InvalidInterfaceException
2116
-     * @throws InvalidArgumentException
2117
-     * @throws InvalidDataTypeException
2118
-     * @throws EE_Error
2119
-     * @throws InvalidInterfaceException
2120
-     */
2121
-    protected function _set_list_table_object()
2122
-    {
2123
-        if (isset($this->_route_config['list_table'])) {
2124
-            if (! class_exists($this->_route_config['list_table'])) {
2125
-                throw new EE_Error(
2126
-                    sprintf(
2127
-                        esc_html__(
2128
-                            '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.',
2129
-                            'event_espresso'
2130
-                        ),
2131
-                        $this->_route_config['list_table'],
2132
-                        get_class($this)
2133
-                    )
2134
-                );
2135
-            }
2136
-            $this->_list_table_object = $this->loader->getShared(
2137
-                $this->_route_config['list_table'],
2138
-                array($this)
2139
-            );
2140
-        }
2141
-    }
2142
-
2143
-
2144
-    /**
2145
-     * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2146
-     *
2147
-     * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2148
-     *                                                    urls.  The array should be indexed by the view it is being
2149
-     *                                                    added to.
2150
-     * @return array
2151
-     */
2152
-    public function get_list_table_view_RLs($extra_query_args = array())
2153
-    {
2154
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2155
-        if (empty($this->_views)) {
2156
-            $this->_views = array();
2157
-        }
2158
-        // cycle thru views
2159
-        foreach ($this->_views as $key => $view) {
2160
-            $query_args = array();
2161
-            // check for current view
2162
-            $this->_views[ $key ]['class'] = $this->_view === $view['slug'] ? 'current' : '';
2163
-            $query_args['action'] = $this->_req_action;
2164
-            $query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2165
-            $query_args['status'] = $view['slug'];
2166
-            // merge any other arguments sent in.
2167
-            if (isset($extra_query_args[ $view['slug'] ])) {
2168
-                foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2169
-                    $query_args[] = $extra_query_arg;
2170
-                }
2171
-            }
2172
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2173
-        }
2174
-        return $this->_views;
2175
-    }
2176
-
2177
-
2178
-    /**
2179
-     * _entries_per_page_dropdown
2180
-     * generates a drop down box for selecting the number of visible rows in an admin page list table
2181
-     *
2182
-     * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2183
-     *         WP does it.
2184
-     * @param int $max_entries total number of rows in the table
2185
-     * @return string
2186
-     */
2187
-    protected function _entries_per_page_dropdown($max_entries = 0)
2188
-    {
2189
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2190
-        $values = array(10, 25, 50, 100);
2191
-        $per_page = (! empty($this->_req_data['per_page'])) ? absint($this->_req_data['per_page']) : 10;
2192
-        if ($max_entries) {
2193
-            $values[] = $max_entries;
2194
-            sort($values);
2195
-        }
2196
-        $entries_per_page_dropdown = '
115
+	/**
116
+	 * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
117
+	 * actions.
118
+	 *
119
+	 * @since 4.6.x
120
+	 * @var array.
121
+	 */
122
+	protected $_default_route_query_args;
123
+
124
+	// set via request page and action args.
125
+	protected $_current_page;
126
+
127
+	protected $_current_view;
128
+
129
+	protected $_current_page_view_url;
130
+
131
+	// sanitized request action (and nonce)
132
+
133
+	/**
134
+	 * @var string $_req_action
135
+	 */
136
+	protected $_req_action;
137
+
138
+	/**
139
+	 * @var string $_req_nonce
140
+	 */
141
+	protected $_req_nonce;
142
+
143
+	// search related
144
+	protected $_search_btn_label;
145
+
146
+	protected $_search_box_callback;
147
+
148
+	/**
149
+	 * WP Current Screen object
150
+	 *
151
+	 * @var WP_Screen
152
+	 */
153
+	protected $_current_screen;
154
+
155
+	// for holding EE_Admin_Hooks object when needed (set via set_hook_object())
156
+	protected $_hook_obj;
157
+
158
+	// for holding incoming request data
159
+	protected $_req_data = [];
160
+
161
+	// yes / no array for admin form fields
162
+	protected $_yes_no_values = array();
163
+
164
+	// some default things shared by all child classes
165
+	protected $_default_espresso_metaboxes;
166
+
167
+	/**
168
+	 *    EE_Registry Object
169
+	 *
170
+	 * @var    EE_Registry
171
+	 */
172
+	protected $EE;
173
+
174
+
175
+	/**
176
+	 * This is just a property that flags whether the given route is a caffeinated route or not.
177
+	 *
178
+	 * @var boolean
179
+	 */
180
+	protected $_is_caf = false;
181
+
182
+
183
+	/**
184
+	 * @Constructor
185
+	 * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
186
+	 * @throws InvalidArgumentException
187
+	 * @throws InvalidDataTypeException
188
+	 * @throws InvalidInterfaceException
189
+	 * @throws ReflectionException
190
+	 */
191
+	public function __construct($routing = true)
192
+	{
193
+		$this->loader = LoaderFactory::getLoader();
194
+		$this->admin_config = $this->loader->getShared('EE_Admin_Config');
195
+		if (strpos($this->_get_dir(), 'caffeinated') !== false) {
196
+			$this->_is_caf = true;
197
+		}
198
+		$this->_yes_no_values = array(
199
+			array('id' => true, 'text' => esc_html__('Yes', 'event_espresso')),
200
+			array('id' => false, 'text' => esc_html__('No', 'event_espresso')),
201
+		);
202
+		// set the _req_data property.
203
+		$this->_req_data = array_merge($_GET, $_POST);
204
+		// routing enabled?
205
+		$this->_routing = $routing;
206
+	}
207
+
208
+
209
+	/**
210
+	 * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
211
+	 * for child classes that needed to set properties prior to these methods getting called,
212
+	 * but also needed the parent class to have its construction completed as well.
213
+	 * Bottom line is that constructors should ONLY be used for setting initial properties
214
+	 * and any complex initialization logic should only run after instantiation is complete.
215
+	 *
216
+	 * This method gets called immediately after construction from within
217
+	 *      EE_Admin_Page_Init::_initialize_admin_page()
218
+	 *
219
+	 * @throws EE_Error
220
+	 * @throws InvalidArgumentException
221
+	 * @throws InvalidDataTypeException
222
+	 * @throws InvalidInterfaceException
223
+	 * @throws ReflectionException
224
+	 * @since $VID:$
225
+	 */
226
+	public function initializePage()
227
+	{
228
+		// set initial page props (child method)
229
+		$this->_init_page_props();
230
+		// set global defaults
231
+		$this->_set_defaults();
232
+		// set early because incoming requests could be ajax related and we need to register those hooks.
233
+		$this->_global_ajax_hooks();
234
+		$this->_ajax_hooks();
235
+		// other_page_hooks have to be early too.
236
+		$this->_do_other_page_hooks();
237
+		// This just allows us to have extending classes do something specific
238
+		// before the parent constructor runs _page_setup().
239
+		if (method_exists($this, '_before_page_setup')) {
240
+			$this->_before_page_setup();
241
+		}
242
+		// set up page dependencies
243
+		$this->_page_setup();
244
+	}
245
+
246
+
247
+	/**
248
+	 * _init_page_props
249
+	 * Child classes use to set at least the following properties:
250
+	 * $page_slug.
251
+	 * $page_label.
252
+	 *
253
+	 * @abstract
254
+	 * @return void
255
+	 */
256
+	abstract protected function _init_page_props();
257
+
258
+
259
+	/**
260
+	 * _ajax_hooks
261
+	 * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
262
+	 * Note: within the ajax callback methods.
263
+	 *
264
+	 * @abstract
265
+	 * @return void
266
+	 */
267
+	abstract protected function _ajax_hooks();
268
+
269
+
270
+	/**
271
+	 * _define_page_props
272
+	 * child classes define page properties in here.  Must include at least:
273
+	 * $_admin_base_url = base_url for all admin pages
274
+	 * $_admin_page_title = default admin_page_title for admin pages
275
+	 * $_labels = array of default labels for various automatically generated elements:
276
+	 *    array(
277
+	 *        'buttons' => array(
278
+	 *            'add' => esc_html__('label for add new button'),
279
+	 *            'edit' => esc_html__('label for edit button'),
280
+	 *            'delete' => esc_html__('label for delete button')
281
+	 *            )
282
+	 *        )
283
+	 *
284
+	 * @abstract
285
+	 * @return void
286
+	 */
287
+	abstract protected function _define_page_props();
288
+
289
+
290
+	/**
291
+	 * _set_page_routes
292
+	 * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
293
+	 * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
294
+	 * have a 'default' route. Here's the format
295
+	 * $this->_page_routes = array(
296
+	 *        'default' => array(
297
+	 *            'func' => '_default_method_handling_route',
298
+	 *            'args' => array('array','of','args'),
299
+	 *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
300
+	 *            ajax request, backend processing)
301
+	 *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
302
+	 *            headers route after.  The string you enter here should match the defined route reference for a
303
+	 *            headers sent route.
304
+	 *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
305
+	 *            this route.
306
+	 *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
307
+	 *            checks).
308
+	 *        ),
309
+	 *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
310
+	 *        handling method.
311
+	 *        )
312
+	 * )
313
+	 *
314
+	 * @abstract
315
+	 * @return void
316
+	 */
317
+	abstract protected function _set_page_routes();
318
+
319
+
320
+	/**
321
+	 * _set_page_config
322
+	 * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
323
+	 * array corresponds to the page_route for the loaded page. Format:
324
+	 * $this->_page_config = array(
325
+	 *        'default' => array(
326
+	 *            'labels' => array(
327
+	 *                'buttons' => array(
328
+	 *                    'add' => esc_html__('label for adding item'),
329
+	 *                    'edit' => esc_html__('label for editing item'),
330
+	 *                    'delete' => esc_html__('label for deleting item')
331
+	 *                ),
332
+	 *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
333
+	 *            ), //optional an array of custom labels for various automatically generated elements to use on the
334
+	 *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
335
+	 *            _define_page_props() method
336
+	 *            'nav' => array(
337
+	 *                'label' => esc_html__('Label for Tab', 'event_espresso').
338
+	 *                'url' => 'http://someurl', //automatically generated UNLESS you define
339
+	 *                'css_class' => 'css-class', //automatically generated UNLESS you define
340
+	 *                'order' => 10, //required to indicate tab position.
341
+	 *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
342
+	 *                displayed then add this parameter.
343
+	 *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
344
+	 *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
345
+	 *            metaboxes set for eventespresso admin pages.
346
+	 *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
347
+	 *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
348
+	 *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
349
+	 *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
350
+	 *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
351
+	 *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
352
+	 *            array indicates the max number of columns (4) and the default number of columns on page load (2).
353
+	 *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
354
+	 *            want to display.
355
+	 *            'help_tabs' => array( //this is used for adding help tabs to a page
356
+	 *                'tab_id' => array(
357
+	 *                    'title' => 'tab_title',
358
+	 *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
359
+	 *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
360
+	 *                    should match a file in the admin folder's "help_tabs" dir (ie..
361
+	 *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
362
+	 *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
363
+	 *                    attempt to use the callback which should match the name of a method in the class
364
+	 *                    ),
365
+	 *                'tab2_id' => array(
366
+	 *                    'title' => 'tab2 title',
367
+	 *                    'filename' => 'file_name_2'
368
+	 *                    'callback' => 'callback_method_for_content',
369
+	 *                 ),
370
+	 *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
371
+	 *            help tab area on an admin page. @link
372
+	 *            http://make.wordpress.org/core/2011/12/06/help-and-screen-api-changes-in-3-3/
373
+	 *            'help_tour' => array(
374
+	 *                'name_of_help_tour_class', //all help tours shoudl be a child class of EE_Help_Tour and located
375
+	 *                in a folder for this admin page named "help_tours", a file name matching the key given here
376
+	 *                (name_of_help_tour_class.class.php), and class matching key given here (name_of_help_tour_class)
377
+	 *            ),
378
+	 *            'require_nonce' => TRUE //this is used if you want to set a route to NOT require a nonce (default is
379
+	 *            true if it isn't present).  To remove the requirement for a nonce check when this route is visited
380
+	 *            just set
381
+	 *            'require_nonce' to FALSE
382
+	 *            )
383
+	 * )
384
+	 *
385
+	 * @abstract
386
+	 * @return void
387
+	 */
388
+	abstract protected function _set_page_config();
389
+
390
+
391
+
392
+
393
+
394
+	/** end sample help_tour methods **/
395
+	/**
396
+	 * _add_screen_options
397
+	 * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
398
+	 * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
399
+	 * to a particular view.
400
+	 *
401
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
402
+	 *         see also WP_Screen object documents...
403
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
404
+	 * @abstract
405
+	 * @return void
406
+	 */
407
+	abstract protected function _add_screen_options();
408
+
409
+
410
+	/**
411
+	 * _add_feature_pointers
412
+	 * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
413
+	 * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
414
+	 * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
415
+	 * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
416
+	 * extended) also see:
417
+	 *
418
+	 * @link   http://eamann.com/tech/wordpress-portland/
419
+	 * @abstract
420
+	 * @return void
421
+	 */
422
+	abstract protected function _add_feature_pointers();
423
+
424
+
425
+	/**
426
+	 * load_scripts_styles
427
+	 * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
428
+	 * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
429
+	 * scripts/styles per view by putting them in a dynamic function in this format
430
+	 * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
431
+	 *
432
+	 * @abstract
433
+	 * @return void
434
+	 */
435
+	abstract public function load_scripts_styles();
436
+
437
+
438
+	/**
439
+	 * admin_init
440
+	 * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
441
+	 * all pages/views loaded by child class.
442
+	 *
443
+	 * @abstract
444
+	 * @return void
445
+	 */
446
+	abstract public function admin_init();
447
+
448
+
449
+	/**
450
+	 * admin_notices
451
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
452
+	 * all pages/views loaded by child class.
453
+	 *
454
+	 * @abstract
455
+	 * @return void
456
+	 */
457
+	abstract public function admin_notices();
458
+
459
+
460
+	/**
461
+	 * admin_footer_scripts
462
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
463
+	 * will apply to all pages/views loaded by child class.
464
+	 *
465
+	 * @return void
466
+	 */
467
+	abstract public function admin_footer_scripts();
468
+
469
+
470
+	/**
471
+	 * admin_footer
472
+	 * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
473
+	 * apply to all pages/views loaded by child class.
474
+	 *
475
+	 * @return void
476
+	 */
477
+	public function admin_footer()
478
+	{
479
+	}
480
+
481
+
482
+	/**
483
+	 * _global_ajax_hooks
484
+	 * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
485
+	 * Note: within the ajax callback methods.
486
+	 *
487
+	 * @abstract
488
+	 * @return void
489
+	 */
490
+	protected function _global_ajax_hooks()
491
+	{
492
+		// for lazy loading of metabox content
493
+		add_action('wp_ajax_espresso-ajax-content', array($this, 'ajax_metabox_content'), 10);
494
+
495
+		add_action(
496
+			'wp_ajax_espresso_hide_status_change_notice',
497
+			[$this, 'hideStatusChangeNotice']
498
+		);
499
+		add_action(
500
+			'wp_ajax_nopriv_espresso_hide_status_change_notice',
501
+			[$this, 'hideStatusChangeNotice']
502
+		);
503
+	}
504
+
505
+
506
+	public function ajax_metabox_content()
507
+	{
508
+		$contentid = isset($this->_req_data['contentid']) ? $this->_req_data['contentid'] : '';
509
+		$url = isset($this->_req_data['contenturl']) ? $this->_req_data['contenturl'] : '';
510
+		EE_Admin_Page::cached_rss_display($contentid, $url);
511
+		wp_die();
512
+	}
513
+
514
+
515
+	public function hideStatusChangeNotice()
516
+	{
517
+		$response = [];
518
+		try {
519
+			/** @var EventEspresso\core\admin\StatusChangeNotice $status_change_notice */
520
+			$status_change_notice = $this->loader->getShared('EventEspresso\core\admin\StatusChangeNotice');
521
+			$response['success'] = $status_change_notice->dismiss() > -1;
522
+		} catch (Exception $exception) {
523
+			$response['errors'] = $exception->getMessage();
524
+		}
525
+		echo wp_json_encode($response);
526
+		exit();
527
+	}
528
+
529
+
530
+	/**
531
+	 * _page_setup
532
+	 * Makes sure any things that need to be loaded early get handled.  We also escape early here if the page requested
533
+	 * doesn't match the object.
534
+	 *
535
+	 * @final
536
+	 * @return void
537
+	 * @throws EE_Error
538
+	 * @throws InvalidArgumentException
539
+	 * @throws ReflectionException
540
+	 * @throws InvalidDataTypeException
541
+	 * @throws InvalidInterfaceException
542
+	 */
543
+	final protected function _page_setup()
544
+	{
545
+		// requires?
546
+		// admin_init stuff - global - we're setting this REALLY early
547
+		// so if EE_Admin pages have to hook into other WP pages they can.
548
+		// But keep in mind, not everything is available from the EE_Admin Page object at this point.
549
+		add_action('admin_init', array($this, 'admin_init_global'), 5);
550
+		// next verify if we need to load anything...
551
+		$this->_current_page = ! empty($_GET['page']) ? sanitize_key($_GET['page']) : '';
552
+		$this->page_folder = strtolower(
553
+			str_replace(array('_Admin_Page', 'Extend_'), '', get_class($this))
554
+		);
555
+		global $ee_menu_slugs;
556
+		$ee_menu_slugs = (array) $ee_menu_slugs;
557
+		if (! defined('DOING_AJAX') && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))) {
558
+			return;
559
+		}
560
+		// becuz WP List tables have two duplicate select inputs for choosing bulk actions, we need to copy the action from the second to the first
561
+		if (isset($this->_req_data['action2']) && $this->_req_data['action'] === '-1') {
562
+			$this->_req_data['action'] = ! empty($this->_req_data['action2']) && $this->_req_data['action2'] !== '-1'
563
+				? $this->_req_data['action2']
564
+				: $this->_req_data['action'];
565
+		}
566
+		// then set blank or -1 action values to 'default'
567
+		$this->_req_action = isset($this->_req_data['action'])
568
+							 && ! empty($this->_req_data['action'])
569
+							 && $this->_req_data['action'] !== '-1'
570
+			? sanitize_key($this->_req_data['action'])
571
+			: 'default';
572
+		// if action is 'default' after the above BUT we have  'route' var set, then let's use the route as the action.
573
+		//  This covers cases where we're coming in from a list table that isn't on the default route.
574
+		$this->_req_action = $this->_req_action === 'default' && isset($this->_req_data['route'])
575
+			? $this->_req_data['route'] : $this->_req_action;
576
+		// however if we are doing_ajax and we've got a 'route' set then that's what the req_action will be
577
+		$this->_req_action = defined('DOING_AJAX') && isset($this->_req_data['route'])
578
+			? $this->_req_data['route']
579
+			: $this->_req_action;
580
+		$this->_current_view = $this->_req_action;
581
+		$this->_req_nonce = $this->_req_action . '_nonce';
582
+		$this->_define_page_props();
583
+		$this->_current_page_view_url = add_query_arg(
584
+			array('page' => $this->_current_page, 'action' => $this->_current_view),
585
+			$this->_admin_base_url
586
+		);
587
+		// default things
588
+		$this->_default_espresso_metaboxes = array(
589
+			'_espresso_news_post_box',
590
+			'_espresso_links_post_box',
591
+			'_espresso_ratings_request',
592
+			'_espresso_sponsors_post_box',
593
+		);
594
+		// set page configs
595
+		$this->_set_page_routes();
596
+		$this->_set_page_config();
597
+		// let's include any referrer data in our default_query_args for this route for "stickiness".
598
+		if (isset($this->_req_data['wp_referer'])) {
599
+			$this->_default_route_query_args['wp_referer'] = $this->_req_data['wp_referer'];
600
+		}
601
+		// for caffeinated and other extended functionality.
602
+		//  If there is a _extend_page_config method
603
+		// then let's run that to modify the all the various page configuration arrays
604
+		if (method_exists($this, '_extend_page_config')) {
605
+			$this->_extend_page_config();
606
+		}
607
+		// for CPT and other extended functionality.
608
+		// If there is an _extend_page_config_for_cpt
609
+		// then let's run that to modify all the various page configuration arrays.
610
+		if (method_exists($this, '_extend_page_config_for_cpt')) {
611
+			$this->_extend_page_config_for_cpt();
612
+		}
613
+		// filter routes and page_config so addons can add their stuff. Filtering done per class
614
+		$this->_page_routes = apply_filters(
615
+			'FHEE__' . get_class($this) . '__page_setup__page_routes',
616
+			$this->_page_routes,
617
+			$this
618
+		);
619
+		$this->_page_config = apply_filters(
620
+			'FHEE__' . get_class($this) . '__page_setup__page_config',
621
+			$this->_page_config,
622
+			$this
623
+		);
624
+		// if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
625
+		// then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
626
+		if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
627
+			add_action(
628
+				'AHEE__EE_Admin_Page__route_admin_request',
629
+				array($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view),
630
+				10,
631
+				2
632
+			);
633
+		}
634
+		// next route only if routing enabled
635
+		if ($this->_routing && ! defined('DOING_AJAX')) {
636
+			$this->_verify_routes();
637
+			// next let's just check user_access and kill if no access
638
+			$this->check_user_access();
639
+			if ($this->_is_UI_request) {
640
+				// admin_init stuff - global, all views for this page class, specific view
641
+				add_action('admin_init', array($this, 'admin_init'), 10);
642
+				if (method_exists($this, 'admin_init_' . $this->_current_view)) {
643
+					add_action('admin_init', array($this, 'admin_init_' . $this->_current_view), 15);
644
+				}
645
+			} else {
646
+				// hijack regular WP loading and route admin request immediately
647
+				@ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
648
+				$this->route_admin_request();
649
+			}
650
+		}
651
+	}
652
+
653
+
654
+	/**
655
+	 * Provides a way for related child admin pages to load stuff on the loaded admin page.
656
+	 *
657
+	 * @return void
658
+	 * @throws EE_Error
659
+	 */
660
+	private function _do_other_page_hooks()
661
+	{
662
+		$registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, array());
663
+		foreach ($registered_pages as $page) {
664
+			// now let's setup the file name and class that should be present
665
+			$classname = str_replace('.class.php', '', $page);
666
+			// autoloaders should take care of loading file
667
+			if (! class_exists($classname)) {
668
+				$error_msg[] = sprintf(
669
+					esc_html__(
670
+						'Something went wrong with loading the %s admin hooks page.',
671
+						'event_espresso'
672
+					),
673
+					$page
674
+				);
675
+				$error_msg[] = $error_msg[0]
676
+							   . "\r\n"
677
+							   . sprintf(
678
+								   esc_html__(
679
+									   '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',
680
+									   'event_espresso'
681
+								   ),
682
+								   $page,
683
+								   '<br />',
684
+								   '<strong>' . $classname . '</strong>'
685
+							   );
686
+				throw new EE_Error(implode('||', $error_msg));
687
+			}
688
+			// // notice we are passing the instance of this class to the hook object.
689
+			$this->loader->getShared($classname, [$this]);
690
+		}
691
+	}
692
+
693
+
694
+	/**
695
+	 * @throws DomainException
696
+	 * @throws EE_Error
697
+	 * @throws InvalidArgumentException
698
+	 * @throws InvalidDataTypeException
699
+	 * @throws InvalidInterfaceException
700
+	 * @throws ReflectionException
701
+	 * @since $VID:$
702
+	 */
703
+	public function load_page_dependencies()
704
+	{
705
+		try {
706
+			$this->_load_page_dependencies();
707
+		} catch (EE_Error $e) {
708
+			$e->get_error();
709
+		}
710
+	}
711
+
712
+
713
+	/**
714
+	 * load_page_dependencies
715
+	 * loads things specific to this page class when its loaded.  Really helps with efficiency.
716
+	 *
717
+	 * @return void
718
+	 * @throws DomainException
719
+	 * @throws EE_Error
720
+	 * @throws InvalidArgumentException
721
+	 * @throws InvalidDataTypeException
722
+	 * @throws InvalidInterfaceException
723
+	 * @throws ReflectionException
724
+	 */
725
+	protected function _load_page_dependencies()
726
+	{
727
+		// let's set the current_screen and screen options to override what WP set
728
+		$this->_current_screen = get_current_screen();
729
+		// load admin_notices - global, page class, and view specific
730
+		add_action('admin_notices', array($this, 'admin_notices_global'), 5);
731
+		add_action('admin_notices', array($this, 'admin_notices'), 10);
732
+		if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
733
+			add_action('admin_notices', array($this, 'admin_notices_' . $this->_current_view), 15);
734
+		}
735
+		// load network admin_notices - global, page class, and view specific
736
+		add_action('network_admin_notices', array($this, 'network_admin_notices_global'), 5);
737
+		if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
738
+			add_action('network_admin_notices', array($this, 'network_admin_notices_' . $this->_current_view));
739
+		}
740
+		// this will save any per_page screen options if they are present
741
+		$this->_set_per_page_screen_options();
742
+		// setup list table properties
743
+		$this->_set_list_table();
744
+		// child classes can "register" a metabox to be automatically handled via the _page_config array property.
745
+		// However in some cases the metaboxes will need to be added within a route handling callback.
746
+		$this->_add_registered_meta_boxes();
747
+		$this->_add_screen_columns();
748
+		// add screen options - global, page child class, and view specific
749
+		$this->_add_global_screen_options();
750
+		$this->_add_screen_options();
751
+		$add_screen_options = "_add_screen_options_{$this->_current_view}";
752
+		if (method_exists($this, $add_screen_options)) {
753
+			$this->{$add_screen_options}();
754
+		}
755
+		// add help tab(s) and tours- set via page_config and qtips.
756
+		// $this->_add_help_tour();
757
+		$this->_add_help_tabs();
758
+		$this->_add_qtips();
759
+		// add feature_pointers - global, page child class, and view specific
760
+		$this->_add_feature_pointers();
761
+		$this->_add_global_feature_pointers();
762
+		$add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
763
+		if (method_exists($this, $add_feature_pointer)) {
764
+			$this->{$add_feature_pointer}();
765
+		}
766
+		// enqueue scripts/styles - global, page class, and view specific
767
+		add_action('admin_enqueue_scripts', array($this, 'load_global_scripts_styles'), 5);
768
+		add_action('admin_enqueue_scripts', array($this, 'load_scripts_styles'), 10);
769
+		if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
770
+			add_action('admin_enqueue_scripts', array($this, "load_scripts_styles_{$this->_current_view}"), 15);
771
+		}
772
+		add_action('admin_enqueue_scripts', array($this, 'admin_footer_scripts_eei18n_js_strings'), 100);
773
+		// admin_print_footer_scripts - global, page child class, and view specific.
774
+		// NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
775
+		// In most cases that's doing_it_wrong().  But adding hidden container elements etc.
776
+		// is a good use case. Notice the late priority we're giving these
777
+		add_action('admin_print_footer_scripts', array($this, 'admin_footer_scripts_global'), 99);
778
+		add_action('admin_print_footer_scripts', array($this, 'admin_footer_scripts'), 100);
779
+		if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
780
+			add_action('admin_print_footer_scripts', array($this, "admin_footer_scripts_{$this->_current_view}"), 101);
781
+		}
782
+		// admin footer scripts
783
+		add_action('admin_footer', array($this, 'admin_footer_global'), 99);
784
+		add_action('admin_footer', array($this, 'admin_footer'), 100);
785
+		if (method_exists($this, "admin_footer_{$this->_current_view}")) {
786
+			add_action('admin_footer', array($this, "admin_footer_{$this->_current_view}"), 101);
787
+		}
788
+		do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
789
+		// targeted hook
790
+		do_action(
791
+			"FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
792
+		);
793
+	}
794
+
795
+
796
+	/**
797
+	 * _set_defaults
798
+	 * This sets some global defaults for class properties.
799
+	 */
800
+	private function _set_defaults()
801
+	{
802
+		$this->_current_screen = $this->_admin_page_title = $this->_req_action = $this->_req_nonce = null;
803
+		$this->_event = $this->_template_path = $this->_column_template_path = null;
804
+		$this->_nav_tabs = $this->_views = $this->_page_routes = array();
805
+		$this->_page_config = $this->_default_route_query_args = array();
806
+		$this->_default_nav_tab_name = 'overview';
807
+		// init template args
808
+		$this->_template_args = array(
809
+			'admin_page_header'  => '',
810
+			'admin_page_content' => '',
811
+			'post_body_content'  => '',
812
+			'before_list_table'  => '',
813
+			'after_list_table'   => '',
814
+		);
815
+	}
816
+
817
+
818
+	/**
819
+	 * route_admin_request
820
+	 *
821
+	 * @see    _route_admin_request()
822
+	 * @return exception|void error
823
+	 * @throws InvalidArgumentException
824
+	 * @throws InvalidInterfaceException
825
+	 * @throws InvalidDataTypeException
826
+	 * @throws EE_Error
827
+	 * @throws ReflectionException
828
+	 */
829
+	public function route_admin_request()
830
+	{
831
+		try {
832
+			$this->_route_admin_request();
833
+		} catch (EE_Error $e) {
834
+			$e->get_error();
835
+		}
836
+	}
837
+
838
+
839
+	public function set_wp_page_slug($wp_page_slug)
840
+	{
841
+		$this->_wp_page_slug = $wp_page_slug;
842
+		// if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
843
+		if (is_network_admin()) {
844
+			$this->_wp_page_slug .= '-network';
845
+		}
846
+	}
847
+
848
+
849
+	/**
850
+	 * _verify_routes
851
+	 * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
852
+	 * we know if we need to drop out.
853
+	 *
854
+	 * @return bool
855
+	 * @throws EE_Error
856
+	 */
857
+	protected function _verify_routes()
858
+	{
859
+		if (! $this->_current_page && ! defined('DOING_AJAX')) {
860
+			return false;
861
+		}
862
+		$this->_route = false;
863
+		// check that the page_routes array is not empty
864
+		if (empty($this->_page_routes)) {
865
+			// user error msg
866
+			$error_msg = sprintf(
867
+				esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
868
+				$this->_admin_page_title
869
+			);
870
+			// developer error msg
871
+			$error_msg .= '||' . $error_msg
872
+						  . esc_html__(
873
+							  ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
874
+							  'event_espresso'
875
+						  );
876
+			throw new EE_Error($error_msg);
877
+		}
878
+		// and that the requested page route exists
879
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
880
+			$this->_route = $this->_page_routes[ $this->_req_action ];
881
+			$this->_route_config = isset($this->_page_config[ $this->_req_action ])
882
+				? $this->_page_config[ $this->_req_action ] : array();
883
+		} else {
884
+			// user error msg
885
+			$error_msg = sprintf(
886
+				esc_html__(
887
+					'The requested page route does not exist for the %s admin page.',
888
+					'event_espresso'
889
+				),
890
+				$this->_admin_page_title
891
+			);
892
+			// developer error msg
893
+			$error_msg .= '||' . $error_msg
894
+						  . sprintf(
895
+							  esc_html__(
896
+								  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
897
+								  'event_espresso'
898
+							  ),
899
+							  $this->_req_action
900
+						  );
901
+			throw new EE_Error($error_msg);
902
+		}
903
+		// and that a default route exists
904
+		if (! array_key_exists('default', $this->_page_routes)) {
905
+			// user error msg
906
+			$error_msg = sprintf(
907
+				esc_html__(
908
+					'A default page route has not been set for the % admin page.',
909
+					'event_espresso'
910
+				),
911
+				$this->_admin_page_title
912
+			);
913
+			// developer error msg
914
+			$error_msg .= '||' . $error_msg
915
+						  . esc_html__(
916
+							  ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
917
+							  'event_espresso'
918
+						  );
919
+			throw new EE_Error($error_msg);
920
+		}
921
+
922
+		// first lets' catch if the UI request has EVER been set.
923
+		if ($this->_is_UI_request === null) {
924
+			// lets set if this is a UI request or not.
925
+			$this->_is_UI_request = ! isset($this->_req_data['noheader']) || $this->_req_data['noheader'] !== true;
926
+			// wait a minute... we might have a noheader in the route array
927
+			$this->_is_UI_request = is_array($this->_route)
928
+									&& isset($this->_route['noheader'])
929
+									&& $this->_route['noheader'] ? false : $this->_is_UI_request;
930
+		}
931
+		$this->_set_current_labels();
932
+		return true;
933
+	}
934
+
935
+
936
+	/**
937
+	 * this method simply verifies a given route and makes sure its an actual route available for the loaded page
938
+	 *
939
+	 * @param  string $route the route name we're verifying
940
+	 * @return mixed (bool|Exception)      we'll throw an exception if this isn't a valid route.
941
+	 * @throws EE_Error
942
+	 */
943
+	protected function _verify_route($route)
944
+	{
945
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
946
+			return true;
947
+		}
948
+		// user error msg
949
+		$error_msg = sprintf(
950
+			esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
951
+			$this->_admin_page_title
952
+		);
953
+		// developer error msg
954
+		$error_msg .= '||' . $error_msg
955
+					  . sprintf(
956
+						  esc_html__(
957
+							  ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
958
+							  'event_espresso'
959
+						  ),
960
+						  $route
961
+					  );
962
+		throw new EE_Error($error_msg);
963
+	}
964
+
965
+
966
+	/**
967
+	 * perform nonce verification
968
+	 * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
969
+	 * using this method (and save retyping!)
970
+	 *
971
+	 * @param string $nonce     The nonce sent
972
+	 * @param string $nonce_ref The nonce reference string (name0)
973
+	 * @return void
974
+	 * @throws EE_Error
975
+	 * @throws InvalidArgumentException
976
+	 * @throws InvalidDataTypeException
977
+	 * @throws InvalidInterfaceException
978
+	 */
979
+	protected function _verify_nonce($nonce, $nonce_ref)
980
+	{
981
+		// verify nonce against expected value
982
+		if (! wp_verify_nonce($nonce, $nonce_ref)) {
983
+			// these are not the droids you are looking for !!!
984
+			$msg = sprintf(
985
+				esc_html__('%sNonce Fail.%s', 'event_espresso'),
986
+				'<a href="http://www.youtube.com/watch?v=56_S0WeTkzs">',
987
+				'</a>'
988
+			);
989
+			if (WP_DEBUG) {
990
+				$msg .= "\n  "
991
+						. sprintf(
992
+							esc_html__(
993
+								'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
994
+								'event_espresso'
995
+							),
996
+							EE_Admin_Page::class
997
+						);
998
+			}
999
+			if (! defined('DOING_AJAX')) {
1000
+				wp_die($msg);
1001
+			} else {
1002
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1003
+				$this->_return_json();
1004
+			}
1005
+		}
1006
+	}
1007
+
1008
+
1009
+	/**
1010
+	 * _route_admin_request()
1011
+	 * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1012
+	 * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1013
+	 * in the page routes and then will try to load the corresponding method.
1014
+	 *
1015
+	 * @return void
1016
+	 * @throws EE_Error
1017
+	 * @throws InvalidArgumentException
1018
+	 * @throws InvalidDataTypeException
1019
+	 * @throws InvalidInterfaceException
1020
+	 * @throws ReflectionException
1021
+	 */
1022
+	protected function _route_admin_request()
1023
+	{
1024
+		if (! $this->_is_UI_request) {
1025
+			$this->_verify_routes();
1026
+		}
1027
+		$nonce_check = isset($this->_route_config['require_nonce'])
1028
+			? $this->_route_config['require_nonce']
1029
+			: true;
1030
+		if ($this->_req_action !== 'default' && $nonce_check) {
1031
+			// set nonce from post data
1032
+			$nonce = isset($this->_req_data[ $this->_req_nonce ])
1033
+				? sanitize_text_field($this->_req_data[ $this->_req_nonce ])
1034
+				: '';
1035
+			$this->_verify_nonce($nonce, $this->_req_nonce);
1036
+		}
1037
+		// set the nav_tabs array but ONLY if this is  UI_request
1038
+		if ($this->_is_UI_request) {
1039
+			$this->_set_nav_tabs();
1040
+		}
1041
+		// grab callback function
1042
+		$func = is_array($this->_route) && isset($this->_route['func']) ? $this->_route['func'] : $this->_route;
1043
+		// check if callback has args
1044
+		$args = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : array();
1045
+		$error_msg = '';
1046
+		// action right before calling route
1047
+		// (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1048
+		if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1049
+			do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1050
+		}
1051
+		// right before calling the route, let's remove _wp_http_referer from the
1052
+		// $_SERVER[REQUEST_URI] global (its now in _req_data for route processing).
1053
+		$_SERVER['REQUEST_URI'] = remove_query_arg(
1054
+			'_wp_http_referer',
1055
+			wp_unslash($_SERVER['REQUEST_URI'])
1056
+		);
1057
+		if (! empty($func)) {
1058
+			if (is_array($func)) {
1059
+				list($class, $method) = $func;
1060
+			} elseif (strpos($func, '::') !== false) {
1061
+				list($class, $method) = explode('::', $func);
1062
+			} else {
1063
+				$class = $this;
1064
+				$method = $func;
1065
+			}
1066
+			if (! (is_object($class) && $class === $this)) {
1067
+				// send along this admin page object for access by addons.
1068
+				$args['admin_page_object'] = $this;
1069
+			}
1070
+			// is it a method on a class that doesn't work?
1071
+			if (
1072
+				((method_exists($class, $method)
1073
+				  && call_user_func_array(array($class, $method), $args) === false)
1074
+				 && (// is it a standalone function that doesn't work?
1075
+					 function_exists($method)
1076
+					 && call_user_func_array(
1077
+						 $func,
1078
+						 array_merge(array('admin_page_object' => $this), $args)
1079
+					 ) === false
1080
+				 )) || (// is it neither a class method NOR a standalone function?
1081
+					! function_exists($method)
1082
+					&& ! method_exists($class, $method)
1083
+				)
1084
+			) {
1085
+				// user error msg
1086
+				$error_msg = esc_html__(
1087
+					'An error occurred. The  requested page route could not be found.',
1088
+					'event_espresso'
1089
+				);
1090
+				// developer error msg
1091
+				$error_msg .= '||';
1092
+				$error_msg .= sprintf(
1093
+					esc_html__(
1094
+						'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1095
+						'event_espresso'
1096
+					),
1097
+					$method
1098
+				);
1099
+			}
1100
+			if (! empty($error_msg)) {
1101
+				throw new EE_Error($error_msg);
1102
+			}
1103
+		}
1104
+		// if we've routed and this route has a no headers route AND a sent_headers_route,
1105
+		// then we need to reset the routing properties to the new route.
1106
+		// 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.
1107
+		if (
1108
+			$this->_is_UI_request === false
1109
+			&& is_array($this->_route)
1110
+			&& ! empty($this->_route['headers_sent_route'])
1111
+		) {
1112
+			$this->_reset_routing_properties($this->_route['headers_sent_route']);
1113
+		}
1114
+	}
1115
+
1116
+
1117
+	/**
1118
+	 * This method just allows the resetting of page properties in the case where a no headers
1119
+	 * route redirects to a headers route in its route config.
1120
+	 *
1121
+	 * @since   4.3.0
1122
+	 * @param  string $new_route New (non header) route to redirect to.
1123
+	 * @return   void
1124
+	 * @throws ReflectionException
1125
+	 * @throws InvalidArgumentException
1126
+	 * @throws InvalidInterfaceException
1127
+	 * @throws InvalidDataTypeException
1128
+	 * @throws EE_Error
1129
+	 */
1130
+	protected function _reset_routing_properties($new_route)
1131
+	{
1132
+		$this->_is_UI_request = true;
1133
+		// now we set the current route to whatever the headers_sent_route is set at
1134
+		$this->_req_data['action'] = $new_route;
1135
+		// rerun page setup
1136
+		$this->_page_setup();
1137
+	}
1138
+
1139
+
1140
+	/**
1141
+	 * _add_query_arg
1142
+	 * adds nonce to array of arguments then calls WP add_query_arg function
1143
+	 *(internally just uses EEH_URL's function with the same name)
1144
+	 *
1145
+	 * @param array  $args
1146
+	 * @param string $url
1147
+	 * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1148
+	 *                                        generated url in an associative array indexed by the key 'wp_referer';
1149
+	 *                                        Example usage: If the current page is:
1150
+	 *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1151
+	 *                                        &action=default&event_id=20&month_range=March%202015
1152
+	 *                                        &_wpnonce=5467821
1153
+	 *                                        and you call:
1154
+	 *                                        EE_Admin_Page::add_query_args_and_nonce(
1155
+	 *                                        array(
1156
+	 *                                        'action' => 'resend_something',
1157
+	 *                                        'page=>espresso_registrations'
1158
+	 *                                        ),
1159
+	 *                                        $some_url,
1160
+	 *                                        true
1161
+	 *                                        );
1162
+	 *                                        It will produce a url in this structure:
1163
+	 *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1164
+	 *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1165
+	 *                                        month_range]=March%202015
1166
+	 * @param   bool $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1167
+	 * @return string
1168
+	 */
1169
+	public static function add_query_args_and_nonce(
1170
+		$args = array(),
1171
+		$url = '',
1172
+		$sticky = false,
1173
+		$exclude_nonce = false
1174
+	) {
1175
+		// if there is a _wp_http_referer include the values from the request but only if sticky = true
1176
+		if ($sticky) {
1177
+			$request = $_REQUEST;
1178
+			unset($request['_wp_http_referer'], $request['wp_referer']);
1179
+			foreach ($request as $key => $value) {
1180
+				// do not add nonces
1181
+				if (strpos($key, 'nonce') !== false) {
1182
+					continue;
1183
+				}
1184
+				$args[ 'wp_referer[' . $key . ']' ] = $value;
1185
+			}
1186
+		}
1187
+		return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce);
1188
+	}
1189
+
1190
+
1191
+	/**
1192
+	 * This returns a generated link that will load the related help tab.
1193
+	 *
1194
+	 * @param  string $help_tab_id the id for the connected help tab
1195
+	 * @param  string $icon_style  (optional) include css class for the style you want to use for the help icon.
1196
+	 * @param  string $help_text   (optional) send help text you want to use for the link if default not to be used
1197
+	 * @uses EEH_Template::get_help_tab_link()
1198
+	 * @return string              generated link
1199
+	 */
1200
+	protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1201
+	{
1202
+		return EEH_Template::get_help_tab_link(
1203
+			$help_tab_id,
1204
+			$this->page_slug,
1205
+			$this->_req_action,
1206
+			$icon_style,
1207
+			$help_text
1208
+		);
1209
+	}
1210
+
1211
+
1212
+	/**
1213
+	 * _add_help_tabs
1214
+	 * Note child classes define their help tabs within the page_config array.
1215
+	 *
1216
+	 * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1217
+	 * @return void
1218
+	 * @throws DomainException
1219
+	 * @throws EE_Error
1220
+	 * @throws ReflectionException
1221
+	 */
1222
+	protected function _add_help_tabs()
1223
+	{
1224
+		$tour_buttons = '';
1225
+		if (isset($this->_page_config[ $this->_req_action ])) {
1226
+			$config = $this->_page_config[ $this->_req_action ];
1227
+			// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1228
+			// is there a help tour for the current route?  if there is let's setup the tour buttons
1229
+			// if (isset($this->_help_tour[ $this->_req_action ])) {
1230
+			//     $tb = array();
1231
+			//     $tour_buttons = '<div class="ee-abs-container"><div class="ee-help-tour-restart-buttons">';
1232
+			//     foreach ($this->_help_tour['tours'] as $tour) {
1233
+			//         // if this is the end tour then we don't need to setup a button
1234
+			//         if ($tour instanceof EE_Help_Tour_final_stop || ! $tour instanceof EE_Help_Tour) {
1235
+			//             continue;
1236
+			//         }
1237
+			//         $tb[] = '<button id="trigger-tour-'
1238
+			//                 . $tour->get_slug()
1239
+			//                 . '" class="button-primary trigger-ee-help-tour">'
1240
+			//                 . $tour->get_label()
1241
+			//                 . '</button>';
1242
+			//     }
1243
+			//     $tour_buttons .= implode('<br />', $tb);
1244
+			//     $tour_buttons .= '</div></div>';
1245
+			// }
1246
+			// let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1247
+			if (is_array($config) && isset($config['help_sidebar'])) {
1248
+				// check that the callback given is valid
1249
+				if (! method_exists($this, $config['help_sidebar'])) {
1250
+					throw new EE_Error(
1251
+						sprintf(
1252
+							esc_html__(
1253
+								'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1254
+								'event_espresso'
1255
+							),
1256
+							$config['help_sidebar'],
1257
+							get_class($this)
1258
+						)
1259
+					);
1260
+				}
1261
+				$content = apply_filters(
1262
+					'FHEE__' . get_class($this) . '__add_help_tabs__help_sidebar',
1263
+					$this->{$config['help_sidebar']}()
1264
+				);
1265
+				$content .= $tour_buttons; // add help tour buttons.
1266
+				// do we have any help tours setup?  Cause if we do we want to add the buttons
1267
+				$this->_current_screen->set_help_sidebar($content);
1268
+			}
1269
+			// if there ARE tour buttons...
1270
+			if (! empty($tour_buttons)) {
1271
+				// if we DON'T have config help sidebar then we'll just add the tour buttons to the sidebar.
1272
+				if (! isset($config['help_sidebar'])) {
1273
+					$this->_current_screen->set_help_sidebar($tour_buttons);
1274
+				}
1275
+				// handle if no help_tabs are set so the sidebar will still show for the help tour buttons
1276
+				if (! isset($config['help_tabs'])) {
1277
+					$_ht['id'] = $this->page_slug;
1278
+					$_ht['title'] = esc_html__('Help Tours', 'event_espresso');
1279
+					$_ht['content'] = '<p>'
1280
+									  . esc_html__(
1281
+										  'The buttons to the right allow you to start/restart any help tours available for this page',
1282
+										  'event_espresso'
1283
+									  ) . '</p>';
1284
+					$this->_current_screen->add_help_tab($_ht);
1285
+				}
1286
+			}
1287
+			if (! isset($config['help_tabs'])) {
1288
+				return;
1289
+			} //no help tabs for this route
1290
+			foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1291
+				// we're here so there ARE help tabs!
1292
+				// make sure we've got what we need
1293
+				if (! isset($cfg['title'])) {
1294
+					throw new EE_Error(
1295
+						esc_html__(
1296
+							'The _page_config array is not set up properly for help tabs.  It is missing a title',
1297
+							'event_espresso'
1298
+						)
1299
+					);
1300
+				}
1301
+				if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1302
+					throw new EE_Error(
1303
+						esc_html__(
1304
+							'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',
1305
+							'event_espresso'
1306
+						)
1307
+					);
1308
+				}
1309
+				// first priority goes to content.
1310
+				if (! empty($cfg['content'])) {
1311
+					$content = ! empty($cfg['content']) ? $cfg['content'] : null;
1312
+					// second priority goes to filename
1313
+				} elseif (! empty($cfg['filename'])) {
1314
+					$file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1315
+					// 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)
1316
+					$file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1317
+															 . basename($this->_get_dir())
1318
+															 . '/help_tabs/'
1319
+															 . $cfg['filename']
1320
+															 . '.help_tab.php' : $file_path;
1321
+					// if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1322
+					if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1323
+						EE_Error::add_error(
1324
+							sprintf(
1325
+								esc_html__(
1326
+									'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',
1327
+									'event_espresso'
1328
+								),
1329
+								$tab_id,
1330
+								key($config),
1331
+								$file_path
1332
+							),
1333
+							__FILE__,
1334
+							__FUNCTION__,
1335
+							__LINE__
1336
+						);
1337
+						return;
1338
+					}
1339
+					$template_args['admin_page_obj'] = $this;
1340
+					$content = EEH_Template::display_template(
1341
+						$file_path,
1342
+						$template_args,
1343
+						true
1344
+					);
1345
+				} else {
1346
+					$content = '';
1347
+				}
1348
+				// check if callback is valid
1349
+				if (
1350
+					empty($content) && (
1351
+						! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1352
+					)
1353
+				) {
1354
+					EE_Error::add_error(
1355
+						sprintf(
1356
+							esc_html__(
1357
+								'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.',
1358
+								'event_espresso'
1359
+							),
1360
+							$cfg['title']
1361
+						),
1362
+						__FILE__,
1363
+						__FUNCTION__,
1364
+						__LINE__
1365
+					);
1366
+					return;
1367
+				}
1368
+				// setup config array for help tab method
1369
+				$id = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1370
+				$_ht = array(
1371
+					'id'       => $id,
1372
+					'title'    => $cfg['title'],
1373
+					'callback' => isset($cfg['callback']) && empty($content) ? array($this, $cfg['callback']) : null,
1374
+					'content'  => $content,
1375
+				);
1376
+				$this->_current_screen->add_help_tab($_ht);
1377
+			}
1378
+		}
1379
+	}
1380
+
1381
+
1382
+	/**
1383
+	 * This basically checks loaded $_page_config property to see if there are any help_tours defined.  "help_tours" is
1384
+	 * an array with properties for setting up usage of the joyride plugin
1385
+	 *
1386
+	 * @link   http://zurb.com/playground/jquery-joyride-feature-tour-plugin
1387
+	 * @see    instructions regarding the format and construction of the "help_tour" array element is found in the
1388
+	 *         _set_page_config() comments
1389
+	 * @return void
1390
+	 * @throws EE_Error
1391
+	 * @throws InvalidArgumentException
1392
+	 * @throws InvalidDataTypeException
1393
+	 * @throws InvalidInterfaceException
1394
+	 * @throws ReflectionException
1395
+	 */
1396
+	protected function _add_help_tour()
1397
+	{
1398
+		// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1399
+		// $tours = array();
1400
+		// $this->_help_tour = array();
1401
+		// // exit early if help tours are turned off globally
1402
+		// if ((defined('EE_DISABLE_HELP_TOURS') && EE_DISABLE_HELP_TOURS)
1403
+		//     || ! EE_Registry::instance()->CFG->admin->help_tour_activation
1404
+		// ) {
1405
+		//     return;
1406
+		// }
1407
+		// // loop through _page_config to find any help_tour defined
1408
+		// foreach ($this->_page_config as $route => $config) {
1409
+		//     // we're only going to set things up for this route
1410
+		//     if ($route !== $this->_req_action) {
1411
+		//         continue;
1412
+		//     }
1413
+		//     if (isset($config['help_tour'])) {
1414
+		//         foreach ($config['help_tour'] as $tour) {
1415
+		//             $file_path = $this->_get_dir() . '/help_tours/' . $tour . '.class.php';
1416
+		//             // let's see if we can get that file...
1417
+		//             // if not its possible this is a decaf route not set in caffeinated
1418
+		//             // so lets try and get the caffeinated equivalent
1419
+		//             $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1420
+		//                                                      . basename($this->_get_dir())
1421
+		//                                                      . '/help_tours/'
1422
+		//                                                      . $tour
1423
+		//                                                      . '.class.php' : $file_path;
1424
+		//             // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1425
+		//             if (! is_readable($file_path)) {
1426
+		//                 EE_Error::add_error(
1427
+		//                     sprintf(
1428
+		//                         esc_html__(
1429
+		//                             'The file path given for the help tour (%s) is not a valid path.  Please check that the string you set for the help tour on this route (%s) is the correct spelling',
1430
+		//                             'event_espresso'
1431
+		//                         ),
1432
+		//                         $file_path,
1433
+		//                         $tour
1434
+		//                     ),
1435
+		//                     __FILE__,
1436
+		//                     __FUNCTION__,
1437
+		//                     __LINE__
1438
+		//                 );
1439
+		//                 return;
1440
+		//             }
1441
+		//             require_once $file_path;
1442
+		//             if (! class_exists($tour)) {
1443
+		//                 $error_msg[] = sprintf(
1444
+		//                     esc_html__('Something went wrong with loading the %s Help Tour Class.', 'event_espresso'),
1445
+		//                     $tour
1446
+		//                 );
1447
+		//                 $error_msg[] = $error_msg[0] . "\r\n"
1448
+		//                                . sprintf(
1449
+		//                                    esc_html__(
1450
+		//                                        'There is no class in place for the %s help tour.%s Make sure you have <strong>%s</strong> defined in the "help_tour" array for the %s route of the % admin page.',
1451
+		//                                        'event_espresso'
1452
+		//                                    ),
1453
+		//                                    $tour,
1454
+		//                                    '<br />',
1455
+		//                                    $tour,
1456
+		//                                    $this->_req_action,
1457
+		//                                    get_class($this)
1458
+		//                                );
1459
+		//                 throw new EE_Error(implode('||', $error_msg));
1460
+		//             }
1461
+		//             $tour_obj = new $tour($this->_is_caf);
1462
+		//             $tours[] = $tour_obj;
1463
+		//             $this->_help_tour[ $route ][] = EEH_Template::help_tour_stops_generator($tour_obj);
1464
+		//         }
1465
+		//         // let's inject the end tour stop element common to all pages... this will only get seen once per machine.
1466
+		//         $end_stop_tour = new EE_Help_Tour_final_stop($this->_is_caf);
1467
+		//         $tours[] = $end_stop_tour;
1468
+		//         $this->_help_tour[ $route ][] = EEH_Template::help_tour_stops_generator($end_stop_tour);
1469
+		//     }
1470
+		// }
1471
+		//
1472
+		// if (! empty($tours)) {
1473
+		//     $this->_help_tour['tours'] = $tours;
1474
+		// }
1475
+		// // that's it!  Now that the $_help_tours property is set (or not)
1476
+		// // the scripts and html should be taken care of automatically.
1477
+		//
1478
+		// /**
1479
+		//  * Allow extending the help tours variable.
1480
+		//  *
1481
+		//  * @param Array $_help_tour The array containing all help tour information to be displayed.
1482
+		//  */
1483
+		// $this->_help_tour = apply_filters('FHEE__EE_Admin_Page___add_help_tour___help_tour', $this->_help_tour);
1484
+	}
1485
+
1486
+
1487
+	/**
1488
+	 * This simply sets up any qtips that have been defined in the page config
1489
+	 *
1490
+	 * @return void
1491
+	 * @throws ReflectionException
1492
+	 * @throws EE_Error
1493
+	 */
1494
+	protected function _add_qtips()
1495
+	{
1496
+		if (isset($this->_route_config['qtips'])) {
1497
+			$qtips = (array) $this->_route_config['qtips'];
1498
+			// load qtip loader
1499
+			$path = array(
1500
+				$this->_get_dir() . '/qtips/',
1501
+				EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1502
+			);
1503
+			$qtip_loader = EEH_Qtip_Loader::instance();
1504
+			if ($qtip_loader instanceof EEH_Qtip_Loader) {
1505
+				$qtip_loader->register($qtips, $path);
1506
+			}
1507
+		}
1508
+	}
1509
+
1510
+
1511
+	/**
1512
+	 * _set_nav_tabs
1513
+	 * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1514
+	 * wish to add additional tabs or modify accordingly.
1515
+	 *
1516
+	 * @return void
1517
+	 * @throws InvalidArgumentException
1518
+	 * @throws InvalidInterfaceException
1519
+	 * @throws InvalidDataTypeException
1520
+	 */
1521
+	protected function _set_nav_tabs()
1522
+	{
1523
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1524
+		$i = 0;
1525
+		foreach ($this->_page_config as $slug => $config) {
1526
+			if (
1527
+				! is_array($config)
1528
+				|| (
1529
+					is_array($config)
1530
+					&& (
1531
+						(isset($config['nav']) && ! $config['nav'])
1532
+						|| ! isset($config['nav'])
1533
+					)
1534
+				)
1535
+			) {
1536
+				continue;
1537
+			}
1538
+			// no nav tab for this config
1539
+			// check for persistent flag
1540
+			if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1541
+				// nav tab is only to appear when route requested.
1542
+				continue;
1543
+			}
1544
+			if (! $this->check_user_access($slug, true)) {
1545
+				// no nav tab because current user does not have access.
1546
+				continue;
1547
+			}
1548
+			$css_class = isset($config['css_class']) ? $config['css_class'] . ' ' : '';
1549
+			$this->_nav_tabs[ $slug ] = array(
1550
+				'url'       => isset($config['nav']['url'])
1551
+					? $config['nav']['url']
1552
+					: EE_Admin_Page::add_query_args_and_nonce(
1553
+						array('action' => $slug),
1554
+						$this->_admin_base_url
1555
+					),
1556
+				'link_text' => isset($config['nav']['label'])
1557
+					? $config['nav']['label']
1558
+					: ucwords(
1559
+						str_replace('_', ' ', $slug)
1560
+					),
1561
+				'css_class' => $this->_req_action === $slug ? $css_class . 'nav-tab-active' : $css_class,
1562
+				'order'     => isset($config['nav']['order']) ? $config['nav']['order'] : $i,
1563
+			);
1564
+			$i++;
1565
+		}
1566
+		// if $this->_nav_tabs is empty then lets set the default
1567
+		if (empty($this->_nav_tabs)) {
1568
+			$this->_nav_tabs[ $this->_default_nav_tab_name ] = array(
1569
+				'url'       => $this->_admin_base_url,
1570
+				'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1571
+				'css_class' => 'nav-tab-active',
1572
+				'order'     => 10,
1573
+			);
1574
+		}
1575
+		// now let's sort the tabs according to order
1576
+		usort($this->_nav_tabs, array($this, '_sort_nav_tabs'));
1577
+	}
1578
+
1579
+
1580
+	/**
1581
+	 * _set_current_labels
1582
+	 * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1583
+	 * property array
1584
+	 *
1585
+	 * @return void
1586
+	 */
1587
+	private function _set_current_labels()
1588
+	{
1589
+		if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1590
+			foreach ($this->_route_config['labels'] as $label => $text) {
1591
+				if (is_array($text)) {
1592
+					foreach ($text as $sublabel => $subtext) {
1593
+						$this->_labels[ $label ][ $sublabel ] = $subtext;
1594
+					}
1595
+				} else {
1596
+					$this->_labels[ $label ] = $text;
1597
+				}
1598
+			}
1599
+		}
1600
+	}
1601
+
1602
+
1603
+	/**
1604
+	 *        verifies user access for this admin page
1605
+	 *
1606
+	 * @param string $route_to_check if present then the capability for the route matching this string is checked.
1607
+	 * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1608
+	 *                               return false if verify fail.
1609
+	 * @return bool
1610
+	 * @throws InvalidArgumentException
1611
+	 * @throws InvalidDataTypeException
1612
+	 * @throws InvalidInterfaceException
1613
+	 */
1614
+	public function check_user_access($route_to_check = '', $verify_only = false)
1615
+	{
1616
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1617
+		$route_to_check = empty($route_to_check) ? $this->_req_action : $route_to_check;
1618
+		$capability = ! empty($route_to_check) && isset($this->_page_routes[ $route_to_check ])
1619
+					  && is_array(
1620
+						  $this->_page_routes[ $route_to_check ]
1621
+					  )
1622
+					  && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1623
+			? $this->_page_routes[ $route_to_check ]['capability'] : null;
1624
+		if (empty($capability) && empty($route_to_check)) {
1625
+			$capability = is_array($this->_route) && empty($this->_route['capability']) ? 'manage_options'
1626
+				: $this->_route['capability'];
1627
+		} else {
1628
+			$capability = empty($capability) ? 'manage_options' : $capability;
1629
+		}
1630
+		$id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1631
+		if (
1632
+			! defined('DOING_AJAX')
1633
+			&& (
1634
+				! function_exists('is_admin')
1635
+				|| ! EE_Registry::instance()->CAP->current_user_can(
1636
+					$capability,
1637
+					$this->page_slug
1638
+					. '_'
1639
+					. $route_to_check,
1640
+					$id
1641
+				)
1642
+			)
1643
+		) {
1644
+			if ($verify_only) {
1645
+				return false;
1646
+			}
1647
+			if (is_user_logged_in()) {
1648
+				wp_die(__('You do not have access to this route.', 'event_espresso'));
1649
+			} else {
1650
+				return false;
1651
+			}
1652
+		}
1653
+		return true;
1654
+	}
1655
+
1656
+
1657
+	/**
1658
+	 * admin_init_global
1659
+	 * This runs all the code that we want executed within the WP admin_init hook.
1660
+	 * This method executes for ALL EE Admin pages.
1661
+	 *
1662
+	 * @return void
1663
+	 */
1664
+	public function admin_init_global()
1665
+	{
1666
+	}
1667
+
1668
+
1669
+	/**
1670
+	 * wp_loaded_global
1671
+	 * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1672
+	 * EE_Admin page and will execute on every EE Admin Page load
1673
+	 *
1674
+	 * @return void
1675
+	 */
1676
+	public function wp_loaded()
1677
+	{
1678
+	}
1679
+
1680
+
1681
+	/**
1682
+	 * admin_notices
1683
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1684
+	 * ALL EE_Admin pages.
1685
+	 *
1686
+	 * @return void
1687
+	 */
1688
+	public function admin_notices_global()
1689
+	{
1690
+		$this->_display_no_javascript_warning();
1691
+		$this->_display_espresso_notices();
1692
+	}
1693
+
1694
+
1695
+	public function network_admin_notices_global()
1696
+	{
1697
+		$this->_display_no_javascript_warning();
1698
+		$this->_display_espresso_notices();
1699
+	}
1700
+
1701
+
1702
+	/**
1703
+	 * admin_footer_scripts_global
1704
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1705
+	 * will apply on ALL EE_Admin pages.
1706
+	 *
1707
+	 * @return void
1708
+	 */
1709
+	public function admin_footer_scripts_global()
1710
+	{
1711
+		$this->_add_admin_page_ajax_loading_img();
1712
+		$this->_add_admin_page_overlay();
1713
+		// if metaboxes are present we need to add the nonce field
1714
+		if (
1715
+			isset($this->_route_config['metaboxes'])
1716
+			|| isset($this->_route_config['list_table'])
1717
+			|| (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1718
+		) {
1719
+			wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1720
+			wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1721
+		}
1722
+	}
1723
+
1724
+
1725
+	/**
1726
+	 * admin_footer_global
1727
+	 * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1728
+	 * This particular method will apply on ALL EE_Admin Pages.
1729
+	 *
1730
+	 * @return void
1731
+	 * @throws InvalidArgumentException
1732
+	 * @throws InvalidDataTypeException
1733
+	 * @throws InvalidInterfaceException
1734
+	 */
1735
+	public function admin_footer_global()
1736
+	{
1737
+		// dialog container for dialog helper
1738
+		$d_cont = '<div class="ee-admin-dialog-container auto-hide hidden">' . "\n";
1739
+		$d_cont .= '<div class="ee-notices"></div>';
1740
+		$d_cont .= '<div class="ee-admin-dialog-container-inner-content"></div>';
1741
+		$d_cont .= '</div>';
1742
+		echo $d_cont;
1743
+		// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1744
+		// help tour stuff?
1745
+		// if (isset($this->_help_tour[ $this->_req_action ])) {
1746
+		//     echo implode('<br />', $this->_help_tour[ $this->_req_action ]);
1747
+		// }
1748
+		// current set timezone for timezone js
1749
+		echo '<span id="current_timezone" class="hidden">' . EEH_DTT_Helper::get_timezone() . '</span>';
1750
+	}
1751
+
1752
+
1753
+	/**
1754
+	 * This function sees if there is a method for help popup content existing for the given route.  If there is then
1755
+	 * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1756
+	 * help popups then in your templates or your content you set "triggers" for the content using the
1757
+	 * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1758
+	 * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1759
+	 * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1760
+	 * for the
1761
+	 * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1762
+	 * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1763
+	 *    'help_trigger_id' => array(
1764
+	 *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1765
+	 *        'content' => esc_html__('localized content for popup', 'event_espresso')
1766
+	 *    )
1767
+	 * );
1768
+	 * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1769
+	 *
1770
+	 * @param array $help_array
1771
+	 * @param bool  $display
1772
+	 * @return string content
1773
+	 * @throws DomainException
1774
+	 * @throws EE_Error
1775
+	 */
1776
+	protected function _set_help_popup_content($help_array = array(), $display = false)
1777
+	{
1778
+		$content = '';
1779
+		$help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1780
+		// loop through the array and setup content
1781
+		foreach ($help_array as $trigger => $help) {
1782
+			// make sure the array is setup properly
1783
+			if (! isset($help['title'], $help['content'])) {
1784
+				throw new EE_Error(
1785
+					esc_html__(
1786
+						'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',
1787
+						'event_espresso'
1788
+					)
1789
+				);
1790
+			}
1791
+			// we're good so let'd setup the template vars and then assign parsed template content to our content.
1792
+			$template_args = array(
1793
+				'help_popup_id'      => $trigger,
1794
+				'help_popup_title'   => $help['title'],
1795
+				'help_popup_content' => $help['content'],
1796
+			);
1797
+			$content .= EEH_Template::display_template(
1798
+				EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1799
+				$template_args,
1800
+				true
1801
+			);
1802
+		}
1803
+		if ($display) {
1804
+			echo $content;
1805
+			return '';
1806
+		}
1807
+		return $content;
1808
+	}
1809
+
1810
+
1811
+	/**
1812
+	 * All this does is retrieve the help content array if set by the EE_Admin_Page child
1813
+	 *
1814
+	 * @return array properly formatted array for help popup content
1815
+	 * @throws EE_Error
1816
+	 */
1817
+	private function _get_help_content()
1818
+	{
1819
+		// what is the method we're looking for?
1820
+		$method_name = '_help_popup_content_' . $this->_req_action;
1821
+		// if method doesn't exist let's get out.
1822
+		if (! method_exists($this, $method_name)) {
1823
+			return array();
1824
+		}
1825
+		// k we're good to go let's retrieve the help array
1826
+		$help_array = $this->{$method_name}();
1827
+		// make sure we've got an array!
1828
+		if (! is_array($help_array)) {
1829
+			throw new EE_Error(
1830
+				esc_html__(
1831
+					'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1832
+					'event_espresso'
1833
+				)
1834
+			);
1835
+		}
1836
+		return $help_array;
1837
+	}
1838
+
1839
+
1840
+	/**
1841
+	 * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1842
+	 * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1843
+	 * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1844
+	 *
1845
+	 * @param string  $trigger_id reference for retrieving the trigger content for the popup
1846
+	 * @param boolean $display    if false then we return the trigger string
1847
+	 * @param array   $dimensions an array of dimensions for the box (array(h,w))
1848
+	 * @return string
1849
+	 * @throws DomainException
1850
+	 * @throws EE_Error
1851
+	 */
1852
+	protected function _set_help_trigger($trigger_id, $display = true, $dimensions = array('400', '640'))
1853
+	{
1854
+		if (defined('DOING_AJAX')) {
1855
+			return '';
1856
+		}
1857
+		// 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
1858
+		$help_array = $this->_get_help_content();
1859
+		$help_content = '';
1860
+		if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1861
+			$help_array[ $trigger_id ] = array(
1862
+				'title'   => esc_html__('Missing Content', 'event_espresso'),
1863
+				'content' => esc_html__(
1864
+					'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.)',
1865
+					'event_espresso'
1866
+				),
1867
+			);
1868
+			$help_content = $this->_set_help_popup_content($help_array);
1869
+		}
1870
+		// let's setup the trigger
1871
+		$content = '<a class="ee-dialog" href="?height='
1872
+				   . $dimensions[0]
1873
+				   . '&width='
1874
+				   . $dimensions[1]
1875
+				   . '&inlineId='
1876
+				   . $trigger_id
1877
+				   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1878
+		$content .= $help_content;
1879
+		if ($display) {
1880
+			echo $content;
1881
+			return '';
1882
+		}
1883
+		return $content;
1884
+	}
1885
+
1886
+
1887
+	/**
1888
+	 * _add_global_screen_options
1889
+	 * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1890
+	 * This particular method will add_screen_options on ALL EE_Admin Pages
1891
+	 *
1892
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1893
+	 *         see also WP_Screen object documents...
1894
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1895
+	 * @abstract
1896
+	 * @return void
1897
+	 */
1898
+	private function _add_global_screen_options()
1899
+	{
1900
+	}
1901
+
1902
+
1903
+	/**
1904
+	 * _add_global_feature_pointers
1905
+	 * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1906
+	 * This particular method will implement feature pointers for ALL EE_Admin pages.
1907
+	 * Note: this is just a placeholder for now.  Implementation will come down the road
1908
+	 *
1909
+	 * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1910
+	 *         extended) also see:
1911
+	 * @link   http://eamann.com/tech/wordpress-portland/
1912
+	 * @abstract
1913
+	 * @return void
1914
+	 */
1915
+	private function _add_global_feature_pointers()
1916
+	{
1917
+	}
1918
+
1919
+
1920
+	/**
1921
+	 * load_global_scripts_styles
1922
+	 * The scripts and styles enqueued in here will be loaded on every EE Admin page
1923
+	 *
1924
+	 * @return void
1925
+	 * @throws EE_Error
1926
+	 */
1927
+	public function load_global_scripts_styles()
1928
+	{
1929
+		// add debugging styles
1930
+		if (WP_DEBUG) {
1931
+			add_action('admin_head', array($this, 'add_xdebug_style'));
1932
+		}
1933
+		// taking care of metaboxes
1934
+		if (
1935
+			empty($this->_cpt_route)
1936
+			&& (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1937
+		) {
1938
+			wp_enqueue_script('dashboard');
1939
+		}
1940
+
1941
+		// LOCALIZED DATA
1942
+		// localize script for ajax lazy loading
1943
+		wp_localize_script(
1944
+			EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1945
+			'eeLazyLoadingContainers',
1946
+			apply_filters(
1947
+				'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1948
+				['espresso_news_post_box_content']
1949
+			)
1950
+		);
1951
+		// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
1952
+		// /**
1953
+		//  * help tour stuff
1954
+		//  */
1955
+		// if (! empty($this->_help_tour)) {
1956
+		//     // register the js for kicking things off
1957
+		//     wp_enqueue_script(
1958
+		//         'ee-help-tour',
1959
+		//         EE_ADMIN_URL . 'assets/ee-help-tour.js',
1960
+		//         array('jquery-joyride'),
1961
+		//         EVENT_ESPRESSO_VERSION,
1962
+		//         true
1963
+		//     );
1964
+		//     $tours = array();
1965
+		//     // setup tours for the js tour object
1966
+		//     foreach ($this->_help_tour['tours'] as $tour) {
1967
+		//         if ($tour instanceof EE_Help_Tour) {
1968
+		//             $tours[] = array(
1969
+		//                 'id'      => $tour->get_slug(),
1970
+		//                 'options' => $tour->get_options(),
1971
+		//             );
1972
+		//         }
1973
+		//     }
1974
+		//     wp_localize_script('ee-help-tour', 'EE_HELP_TOUR', array('tours' => $tours));
1975
+		//     // admin_footer_global will take care of making sure our help_tour skeleton gets printed via the info stored in $this->_help_tour
1976
+		// }
1977
+	}
1978
+
1979
+
1980
+	/**
1981
+	 *        admin_footer_scripts_eei18n_js_strings
1982
+	 *
1983
+	 * @return        void
1984
+	 */
1985
+	public function admin_footer_scripts_eei18n_js_strings()
1986
+	{
1987
+		EE_Registry::$i18n_js_strings['ajax_url'] = WP_AJAX_URL;
1988
+		EE_Registry::$i18n_js_strings['confirm_delete'] = esc_html__(
1989
+			'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!!!',
1990
+			'event_espresso'
1991
+		);
1992
+		EE_Registry::$i18n_js_strings['January'] = esc_html__('January', 'event_espresso');
1993
+		EE_Registry::$i18n_js_strings['February'] = esc_html__('February', 'event_espresso');
1994
+		EE_Registry::$i18n_js_strings['March'] = esc_html__('March', 'event_espresso');
1995
+		EE_Registry::$i18n_js_strings['April'] = esc_html__('April', 'event_espresso');
1996
+		EE_Registry::$i18n_js_strings['May'] = esc_html__('May', 'event_espresso');
1997
+		EE_Registry::$i18n_js_strings['June'] = esc_html__('June', 'event_espresso');
1998
+		EE_Registry::$i18n_js_strings['July'] = esc_html__('July', 'event_espresso');
1999
+		EE_Registry::$i18n_js_strings['August'] = esc_html__('August', 'event_espresso');
2000
+		EE_Registry::$i18n_js_strings['September'] = esc_html__('September', 'event_espresso');
2001
+		EE_Registry::$i18n_js_strings['October'] = esc_html__('October', 'event_espresso');
2002
+		EE_Registry::$i18n_js_strings['November'] = esc_html__('November', 'event_espresso');
2003
+		EE_Registry::$i18n_js_strings['December'] = esc_html__('December', 'event_espresso');
2004
+		EE_Registry::$i18n_js_strings['Jan'] = esc_html__('Jan', 'event_espresso');
2005
+		EE_Registry::$i18n_js_strings['Feb'] = esc_html__('Feb', 'event_espresso');
2006
+		EE_Registry::$i18n_js_strings['Mar'] = esc_html__('Mar', 'event_espresso');
2007
+		EE_Registry::$i18n_js_strings['Apr'] = esc_html__('Apr', 'event_espresso');
2008
+		EE_Registry::$i18n_js_strings['May'] = esc_html__('May', 'event_espresso');
2009
+		EE_Registry::$i18n_js_strings['Jun'] = esc_html__('Jun', 'event_espresso');
2010
+		EE_Registry::$i18n_js_strings['Jul'] = esc_html__('Jul', 'event_espresso');
2011
+		EE_Registry::$i18n_js_strings['Aug'] = esc_html__('Aug', 'event_espresso');
2012
+		EE_Registry::$i18n_js_strings['Sep'] = esc_html__('Sep', 'event_espresso');
2013
+		EE_Registry::$i18n_js_strings['Oct'] = esc_html__('Oct', 'event_espresso');
2014
+		EE_Registry::$i18n_js_strings['Nov'] = esc_html__('Nov', 'event_espresso');
2015
+		EE_Registry::$i18n_js_strings['Dec'] = esc_html__('Dec', 'event_espresso');
2016
+		EE_Registry::$i18n_js_strings['Sunday'] = esc_html__('Sunday', 'event_espresso');
2017
+		EE_Registry::$i18n_js_strings['Monday'] = esc_html__('Monday', 'event_espresso');
2018
+		EE_Registry::$i18n_js_strings['Tuesday'] = esc_html__('Tuesday', 'event_espresso');
2019
+		EE_Registry::$i18n_js_strings['Wednesday'] = esc_html__('Wednesday', 'event_espresso');
2020
+		EE_Registry::$i18n_js_strings['Thursday'] = esc_html__('Thursday', 'event_espresso');
2021
+		EE_Registry::$i18n_js_strings['Friday'] = esc_html__('Friday', 'event_espresso');
2022
+		EE_Registry::$i18n_js_strings['Saturday'] = esc_html__('Saturday', 'event_espresso');
2023
+		EE_Registry::$i18n_js_strings['Sun'] = esc_html__('Sun', 'event_espresso');
2024
+		EE_Registry::$i18n_js_strings['Mon'] = esc_html__('Mon', 'event_espresso');
2025
+		EE_Registry::$i18n_js_strings['Tue'] = esc_html__('Tue', 'event_espresso');
2026
+		EE_Registry::$i18n_js_strings['Wed'] = esc_html__('Wed', 'event_espresso');
2027
+		EE_Registry::$i18n_js_strings['Thu'] = esc_html__('Thu', 'event_espresso');
2028
+		EE_Registry::$i18n_js_strings['Fri'] = esc_html__('Fri', 'event_espresso');
2029
+		EE_Registry::$i18n_js_strings['Sat'] = esc_html__('Sat', 'event_espresso');
2030
+	}
2031
+
2032
+
2033
+	/**
2034
+	 *        load enhanced xdebug styles for ppl with failing eyesight
2035
+	 *
2036
+	 * @return        void
2037
+	 */
2038
+	public function add_xdebug_style()
2039
+	{
2040
+		echo '<style>.xdebug-error { font-size:1.5em; }</style>';
2041
+	}
2042
+
2043
+
2044
+	/************************/
2045
+	/** LIST TABLE METHODS **/
2046
+	/************************/
2047
+	/**
2048
+	 * this sets up the list table if the current view requires it.
2049
+	 *
2050
+	 * @return void
2051
+	 * @throws EE_Error
2052
+	 * @throws InvalidArgumentException
2053
+	 * @throws InvalidDataTypeException
2054
+	 * @throws InvalidInterfaceException
2055
+	 */
2056
+	protected function _set_list_table()
2057
+	{
2058
+		// first is this a list_table view?
2059
+		if (! isset($this->_route_config['list_table'])) {
2060
+			return;
2061
+		} //not a list_table view so get out.
2062
+		// list table functions are per view specific (because some admin pages might have more than one list table!)
2063
+		$list_table_view = '_set_list_table_views_' . $this->_req_action;
2064
+		if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2065
+			// user error msg
2066
+			$error_msg = esc_html__(
2067
+				'An error occurred. The requested list table views could not be found.',
2068
+				'event_espresso'
2069
+			);
2070
+			// developer error msg
2071
+			$error_msg .= '||'
2072
+						  . sprintf(
2073
+							  esc_html__(
2074
+								  '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.',
2075
+								  'event_espresso'
2076
+							  ),
2077
+							  $this->_req_action,
2078
+							  $list_table_view
2079
+						  );
2080
+			throw new EE_Error($error_msg);
2081
+		}
2082
+		// let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2083
+		$this->_views = apply_filters(
2084
+			'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2085
+			$this->_views
2086
+		);
2087
+		$this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2088
+		$this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2089
+		$this->_set_list_table_view();
2090
+		$this->_set_list_table_object();
2091
+	}
2092
+
2093
+
2094
+	/**
2095
+	 * set current view for List Table
2096
+	 *
2097
+	 * @return void
2098
+	 */
2099
+	protected function _set_list_table_view()
2100
+	{
2101
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2102
+		// looking at active items or dumpster diving ?
2103
+		if (! isset($this->_req_data['status']) || ! array_key_exists($this->_req_data['status'], $this->_views)) {
2104
+			$this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2105
+		} else {
2106
+			$this->_view = sanitize_key($this->_req_data['status']);
2107
+		}
2108
+	}
2109
+
2110
+
2111
+	/**
2112
+	 * _set_list_table_object
2113
+	 * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2114
+	 *
2115
+	 * @throws InvalidInterfaceException
2116
+	 * @throws InvalidArgumentException
2117
+	 * @throws InvalidDataTypeException
2118
+	 * @throws EE_Error
2119
+	 * @throws InvalidInterfaceException
2120
+	 */
2121
+	protected function _set_list_table_object()
2122
+	{
2123
+		if (isset($this->_route_config['list_table'])) {
2124
+			if (! class_exists($this->_route_config['list_table'])) {
2125
+				throw new EE_Error(
2126
+					sprintf(
2127
+						esc_html__(
2128
+							'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.',
2129
+							'event_espresso'
2130
+						),
2131
+						$this->_route_config['list_table'],
2132
+						get_class($this)
2133
+					)
2134
+				);
2135
+			}
2136
+			$this->_list_table_object = $this->loader->getShared(
2137
+				$this->_route_config['list_table'],
2138
+				array($this)
2139
+			);
2140
+		}
2141
+	}
2142
+
2143
+
2144
+	/**
2145
+	 * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2146
+	 *
2147
+	 * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2148
+	 *                                                    urls.  The array should be indexed by the view it is being
2149
+	 *                                                    added to.
2150
+	 * @return array
2151
+	 */
2152
+	public function get_list_table_view_RLs($extra_query_args = array())
2153
+	{
2154
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2155
+		if (empty($this->_views)) {
2156
+			$this->_views = array();
2157
+		}
2158
+		// cycle thru views
2159
+		foreach ($this->_views as $key => $view) {
2160
+			$query_args = array();
2161
+			// check for current view
2162
+			$this->_views[ $key ]['class'] = $this->_view === $view['slug'] ? 'current' : '';
2163
+			$query_args['action'] = $this->_req_action;
2164
+			$query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2165
+			$query_args['status'] = $view['slug'];
2166
+			// merge any other arguments sent in.
2167
+			if (isset($extra_query_args[ $view['slug'] ])) {
2168
+				foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2169
+					$query_args[] = $extra_query_arg;
2170
+				}
2171
+			}
2172
+			$this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2173
+		}
2174
+		return $this->_views;
2175
+	}
2176
+
2177
+
2178
+	/**
2179
+	 * _entries_per_page_dropdown
2180
+	 * generates a drop down box for selecting the number of visible rows in an admin page list table
2181
+	 *
2182
+	 * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2183
+	 *         WP does it.
2184
+	 * @param int $max_entries total number of rows in the table
2185
+	 * @return string
2186
+	 */
2187
+	protected function _entries_per_page_dropdown($max_entries = 0)
2188
+	{
2189
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2190
+		$values = array(10, 25, 50, 100);
2191
+		$per_page = (! empty($this->_req_data['per_page'])) ? absint($this->_req_data['per_page']) : 10;
2192
+		if ($max_entries) {
2193
+			$values[] = $max_entries;
2194
+			sort($values);
2195
+		}
2196
+		$entries_per_page_dropdown = '
2197 2197
 			<div id="entries-per-page-dv" class="alignleft actions">
2198 2198
 				<label class="hide-if-no-js">
2199 2199
 					Show
2200 2200
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2201
-        foreach ($values as $value) {
2202
-            if ($value < $max_entries) {
2203
-                $selected = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2204
-                $entries_per_page_dropdown .= '
2201
+		foreach ($values as $value) {
2202
+			if ($value < $max_entries) {
2203
+				$selected = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2204
+				$entries_per_page_dropdown .= '
2205 2205
 						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2206
-            }
2207
-        }
2208
-        $selected = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2209
-        $entries_per_page_dropdown .= '
2206
+			}
2207
+		}
2208
+		$selected = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2209
+		$entries_per_page_dropdown .= '
2210 2210
 						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2211
-        $entries_per_page_dropdown .= '
2211
+		$entries_per_page_dropdown .= '
2212 2212
 					</select>
2213 2213
 					entries
2214 2214
 				</label>
2215 2215
 				<input id="entries-per-page-btn" class="button-secondary" type="submit" value="Go" >
2216 2216
 			</div>
2217 2217
 		';
2218
-        return $entries_per_page_dropdown;
2219
-    }
2220
-
2221
-
2222
-    /**
2223
-     *        _set_search_attributes
2224
-     *
2225
-     * @return        void
2226
-     */
2227
-    public function _set_search_attributes()
2228
-    {
2229
-        $this->_template_args['search']['btn_label'] = sprintf(
2230
-            esc_html__('Search %s', 'event_espresso'),
2231
-            empty($this->_search_btn_label) ? $this->page_label
2232
-                : $this->_search_btn_label
2233
-        );
2234
-        $this->_template_args['search']['callback'] = 'search_' . $this->page_slug;
2235
-    }
2236
-
2237
-
2238
-
2239
-    /*** END LIST TABLE METHODS **/
2240
-
2241
-
2242
-    /**
2243
-     * _add_registered_metaboxes
2244
-     *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2245
-     *
2246
-     * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2247
-     * @return void
2248
-     * @throws EE_Error
2249
-     */
2250
-    private function _add_registered_meta_boxes()
2251
-    {
2252
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2253
-        // we only add meta boxes if the page_route calls for it
2254
-        if (
2255
-            is_array($this->_route_config) && isset($this->_route_config['metaboxes'])
2256
-            && is_array(
2257
-                $this->_route_config['metaboxes']
2258
-            )
2259
-        ) {
2260
-            // this simply loops through the callbacks provided
2261
-            // and checks if there is a corresponding callback registered by the child
2262
-            // if there is then we go ahead and process the metabox loader.
2263
-            foreach ($this->_route_config['metaboxes'] as $metabox_callback) {
2264
-                // first check for Closures
2265
-                if ($metabox_callback instanceof Closure) {
2266
-                    $result = $metabox_callback();
2267
-                } elseif (is_array($metabox_callback) && isset($metabox_callback[0], $metabox_callback[1])) {
2268
-                    $result = call_user_func(array($metabox_callback[0], $metabox_callback[1]));
2269
-                } else {
2270
-                    $result = $this->{$metabox_callback}();
2271
-                }
2272
-                if ($result === false) {
2273
-                    // user error msg
2274
-                    $error_msg = esc_html__(
2275
-                        'An error occurred. The  requested metabox could not be found.',
2276
-                        'event_espresso'
2277
-                    );
2278
-                    // developer error msg
2279
-                    $error_msg .= '||'
2280
-                                  . sprintf(
2281
-                                      esc_html__(
2282
-                                          '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.',
2283
-                                          'event_espresso'
2284
-                                      ),
2285
-                                      $metabox_callback
2286
-                                  );
2287
-                    throw new EE_Error($error_msg);
2288
-                }
2289
-            }
2290
-        }
2291
-    }
2292
-
2293
-
2294
-    /**
2295
-     * _add_screen_columns
2296
-     * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2297
-     * the dynamic column template and we'll setup the column options for the page.
2298
-     *
2299
-     * @return void
2300
-     */
2301
-    private function _add_screen_columns()
2302
-    {
2303
-        if (
2304
-            is_array($this->_route_config)
2305
-            && isset($this->_route_config['columns'])
2306
-            && is_array($this->_route_config['columns'])
2307
-            && count($this->_route_config['columns']) === 2
2308
-        ) {
2309
-            add_screen_option(
2310
-                'layout_columns',
2311
-                array(
2312
-                    'max'     => (int) $this->_route_config['columns'][0],
2313
-                    'default' => (int) $this->_route_config['columns'][1],
2314
-                )
2315
-            );
2316
-            $this->_template_args['num_columns'] = $this->_route_config['columns'][0];
2317
-            $screen_id = $this->_current_screen->id;
2318
-            $screen_columns = (int) get_user_option("screen_layout_{$screen_id}");
2319
-            $total_columns = ! empty($screen_columns)
2320
-                ? $screen_columns
2321
-                : $this->_route_config['columns'][1];
2322
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2323
-            $this->_template_args['current_page'] = $this->_wp_page_slug;
2324
-            $this->_template_args['screen'] = $this->_current_screen;
2325
-            $this->_column_template_path = EE_ADMIN_TEMPLATE
2326
-                                           . 'admin_details_metabox_column_wrapper.template.php';
2327
-            // finally if we don't have has_metaboxes set in the route config
2328
-            // let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2329
-            $this->_route_config['has_metaboxes'] = true;
2330
-        }
2331
-    }
2332
-
2333
-
2334
-
2335
-    /** GLOBALLY AVAILABLE METABOXES **/
2336
-
2337
-
2338
-    /**
2339
-     * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2340
-     * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2341
-     * these get loaded on.
2342
-     */
2343
-    private function _espresso_news_post_box()
2344
-    {
2345
-        $news_box_title = apply_filters(
2346
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2347
-            esc_html__('New @ Event Espresso', 'event_espresso')
2348
-        );
2349
-        add_meta_box(
2350
-            'espresso_news_post_box',
2351
-            $news_box_title,
2352
-            array(
2353
-                $this,
2354
-                'espresso_news_post_box',
2355
-            ),
2356
-            $this->_wp_page_slug,
2357
-            'side'
2358
-        );
2359
-    }
2360
-
2361
-
2362
-    /**
2363
-     * Code for setting up espresso ratings request metabox.
2364
-     */
2365
-    protected function _espresso_ratings_request()
2366
-    {
2367
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2368
-            return;
2369
-        }
2370
-        $ratings_box_title = apply_filters(
2371
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2372
-            esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2373
-        );
2374
-        add_meta_box(
2375
-            'espresso_ratings_request',
2376
-            $ratings_box_title,
2377
-            array(
2378
-                $this,
2379
-                'espresso_ratings_request',
2380
-            ),
2381
-            $this->_wp_page_slug,
2382
-            'side'
2383
-        );
2384
-    }
2385
-
2386
-
2387
-    /**
2388
-     * Code for setting up espresso ratings request metabox content.
2389
-     *
2390
-     * @throws DomainException
2391
-     */
2392
-    public function espresso_ratings_request()
2393
-    {
2394
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2395
-    }
2396
-
2397
-
2398
-    public static function cached_rss_display($rss_id, $url)
2399
-    {
2400
-        $loading = '<p class="widget-loading hide-if-no-js">'
2401
-                   . __('Loading&#8230;', 'event_espresso')
2402
-                   . '</p><p class="hide-if-js">'
2403
-                   . esc_html__('This widget requires JavaScript.', 'event_espresso')
2404
-                   . '</p>';
2405
-        $pre = '<div class="espresso-rss-display">' . "\n\t";
2406
-        $pre .= '<span id="' . $rss_id . '_url" class="hidden">' . $url . '</span>';
2407
-        $post = '</div>' . "\n";
2408
-        $cache_key = 'ee_rss_' . md5($rss_id);
2409
-        $output = get_transient($cache_key);
2410
-        if ($output !== false) {
2411
-            echo $pre . $output . $post;
2412
-            return true;
2413
-        }
2414
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2415
-            echo $pre . $loading . $post;
2416
-            return false;
2417
-        }
2418
-        ob_start();
2419
-        wp_widget_rss_output($url, array('show_date' => 0, 'items' => 5));
2420
-        set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2421
-        return true;
2422
-    }
2423
-
2424
-
2425
-    public function espresso_news_post_box()
2426
-    {
2427
-        ?>
2218
+		return $entries_per_page_dropdown;
2219
+	}
2220
+
2221
+
2222
+	/**
2223
+	 *        _set_search_attributes
2224
+	 *
2225
+	 * @return        void
2226
+	 */
2227
+	public function _set_search_attributes()
2228
+	{
2229
+		$this->_template_args['search']['btn_label'] = sprintf(
2230
+			esc_html__('Search %s', 'event_espresso'),
2231
+			empty($this->_search_btn_label) ? $this->page_label
2232
+				: $this->_search_btn_label
2233
+		);
2234
+		$this->_template_args['search']['callback'] = 'search_' . $this->page_slug;
2235
+	}
2236
+
2237
+
2238
+
2239
+	/*** END LIST TABLE METHODS **/
2240
+
2241
+
2242
+	/**
2243
+	 * _add_registered_metaboxes
2244
+	 *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2245
+	 *
2246
+	 * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2247
+	 * @return void
2248
+	 * @throws EE_Error
2249
+	 */
2250
+	private function _add_registered_meta_boxes()
2251
+	{
2252
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2253
+		// we only add meta boxes if the page_route calls for it
2254
+		if (
2255
+			is_array($this->_route_config) && isset($this->_route_config['metaboxes'])
2256
+			&& is_array(
2257
+				$this->_route_config['metaboxes']
2258
+			)
2259
+		) {
2260
+			// this simply loops through the callbacks provided
2261
+			// and checks if there is a corresponding callback registered by the child
2262
+			// if there is then we go ahead and process the metabox loader.
2263
+			foreach ($this->_route_config['metaboxes'] as $metabox_callback) {
2264
+				// first check for Closures
2265
+				if ($metabox_callback instanceof Closure) {
2266
+					$result = $metabox_callback();
2267
+				} elseif (is_array($metabox_callback) && isset($metabox_callback[0], $metabox_callback[1])) {
2268
+					$result = call_user_func(array($metabox_callback[0], $metabox_callback[1]));
2269
+				} else {
2270
+					$result = $this->{$metabox_callback}();
2271
+				}
2272
+				if ($result === false) {
2273
+					// user error msg
2274
+					$error_msg = esc_html__(
2275
+						'An error occurred. The  requested metabox could not be found.',
2276
+						'event_espresso'
2277
+					);
2278
+					// developer error msg
2279
+					$error_msg .= '||'
2280
+								  . sprintf(
2281
+									  esc_html__(
2282
+										  '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.',
2283
+										  'event_espresso'
2284
+									  ),
2285
+									  $metabox_callback
2286
+								  );
2287
+					throw new EE_Error($error_msg);
2288
+				}
2289
+			}
2290
+		}
2291
+	}
2292
+
2293
+
2294
+	/**
2295
+	 * _add_screen_columns
2296
+	 * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2297
+	 * the dynamic column template and we'll setup the column options for the page.
2298
+	 *
2299
+	 * @return void
2300
+	 */
2301
+	private function _add_screen_columns()
2302
+	{
2303
+		if (
2304
+			is_array($this->_route_config)
2305
+			&& isset($this->_route_config['columns'])
2306
+			&& is_array($this->_route_config['columns'])
2307
+			&& count($this->_route_config['columns']) === 2
2308
+		) {
2309
+			add_screen_option(
2310
+				'layout_columns',
2311
+				array(
2312
+					'max'     => (int) $this->_route_config['columns'][0],
2313
+					'default' => (int) $this->_route_config['columns'][1],
2314
+				)
2315
+			);
2316
+			$this->_template_args['num_columns'] = $this->_route_config['columns'][0];
2317
+			$screen_id = $this->_current_screen->id;
2318
+			$screen_columns = (int) get_user_option("screen_layout_{$screen_id}");
2319
+			$total_columns = ! empty($screen_columns)
2320
+				? $screen_columns
2321
+				: $this->_route_config['columns'][1];
2322
+			$this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2323
+			$this->_template_args['current_page'] = $this->_wp_page_slug;
2324
+			$this->_template_args['screen'] = $this->_current_screen;
2325
+			$this->_column_template_path = EE_ADMIN_TEMPLATE
2326
+										   . 'admin_details_metabox_column_wrapper.template.php';
2327
+			// finally if we don't have has_metaboxes set in the route config
2328
+			// let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2329
+			$this->_route_config['has_metaboxes'] = true;
2330
+		}
2331
+	}
2332
+
2333
+
2334
+
2335
+	/** GLOBALLY AVAILABLE METABOXES **/
2336
+
2337
+
2338
+	/**
2339
+	 * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2340
+	 * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2341
+	 * these get loaded on.
2342
+	 */
2343
+	private function _espresso_news_post_box()
2344
+	{
2345
+		$news_box_title = apply_filters(
2346
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2347
+			esc_html__('New @ Event Espresso', 'event_espresso')
2348
+		);
2349
+		add_meta_box(
2350
+			'espresso_news_post_box',
2351
+			$news_box_title,
2352
+			array(
2353
+				$this,
2354
+				'espresso_news_post_box',
2355
+			),
2356
+			$this->_wp_page_slug,
2357
+			'side'
2358
+		);
2359
+	}
2360
+
2361
+
2362
+	/**
2363
+	 * Code for setting up espresso ratings request metabox.
2364
+	 */
2365
+	protected function _espresso_ratings_request()
2366
+	{
2367
+		if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2368
+			return;
2369
+		}
2370
+		$ratings_box_title = apply_filters(
2371
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2372
+			esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2373
+		);
2374
+		add_meta_box(
2375
+			'espresso_ratings_request',
2376
+			$ratings_box_title,
2377
+			array(
2378
+				$this,
2379
+				'espresso_ratings_request',
2380
+			),
2381
+			$this->_wp_page_slug,
2382
+			'side'
2383
+		);
2384
+	}
2385
+
2386
+
2387
+	/**
2388
+	 * Code for setting up espresso ratings request metabox content.
2389
+	 *
2390
+	 * @throws DomainException
2391
+	 */
2392
+	public function espresso_ratings_request()
2393
+	{
2394
+		EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2395
+	}
2396
+
2397
+
2398
+	public static function cached_rss_display($rss_id, $url)
2399
+	{
2400
+		$loading = '<p class="widget-loading hide-if-no-js">'
2401
+				   . __('Loading&#8230;', 'event_espresso')
2402
+				   . '</p><p class="hide-if-js">'
2403
+				   . esc_html__('This widget requires JavaScript.', 'event_espresso')
2404
+				   . '</p>';
2405
+		$pre = '<div class="espresso-rss-display">' . "\n\t";
2406
+		$pre .= '<span id="' . $rss_id . '_url" class="hidden">' . $url . '</span>';
2407
+		$post = '</div>' . "\n";
2408
+		$cache_key = 'ee_rss_' . md5($rss_id);
2409
+		$output = get_transient($cache_key);
2410
+		if ($output !== false) {
2411
+			echo $pre . $output . $post;
2412
+			return true;
2413
+		}
2414
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2415
+			echo $pre . $loading . $post;
2416
+			return false;
2417
+		}
2418
+		ob_start();
2419
+		wp_widget_rss_output($url, array('show_date' => 0, 'items' => 5));
2420
+		set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2421
+		return true;
2422
+	}
2423
+
2424
+
2425
+	public function espresso_news_post_box()
2426
+	{
2427
+		?>
2428 2428
         <div class="padding">
2429 2429
             <div id="espresso_news_post_box_content" class="infolinks">
2430 2430
                 <?php
2431
-                // Get RSS Feed(s)
2432
-                EE_Admin_Page::cached_rss_display(
2433
-                    'espresso_news_post_box_content',
2434
-                    urlencode(
2435
-                        apply_filters(
2436
-                            'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2437
-                            'http://eventespresso.com/feed/'
2438
-                        )
2439
-                    )
2440
-                );
2441
-                ?>
2431
+				// Get RSS Feed(s)
2432
+				EE_Admin_Page::cached_rss_display(
2433
+					'espresso_news_post_box_content',
2434
+					urlencode(
2435
+						apply_filters(
2436
+							'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2437
+							'http://eventespresso.com/feed/'
2438
+						)
2439
+					)
2440
+				);
2441
+				?>
2442 2442
             </div>
2443 2443
             <?php do_action('AHEE__EE_Admin_Page__espresso_news_post_box__after_content'); ?>
2444 2444
         </div>
2445 2445
         <?php
2446
-    }
2447
-
2448
-
2449
-    private function _espresso_links_post_box()
2450
-    {
2451
-        // Hiding until we actually have content to put in here...
2452
-        // add_meta_box('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2453
-    }
2454
-
2455
-
2456
-    public function espresso_links_post_box()
2457
-    {
2458
-        // Hiding until we actually have content to put in here...
2459
-        // EEH_Template::display_template(
2460
-        //     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2461
-        // );
2462
-    }
2463
-
2464
-
2465
-    protected function _espresso_sponsors_post_box()
2466
-    {
2467
-        if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2468
-            add_meta_box(
2469
-                'espresso_sponsors_post_box',
2470
-                esc_html__('Event Espresso Highlights', 'event_espresso'),
2471
-                array($this, 'espresso_sponsors_post_box'),
2472
-                $this->_wp_page_slug,
2473
-                'side'
2474
-            );
2475
-        }
2476
-    }
2477
-
2478
-
2479
-    public function espresso_sponsors_post_box()
2480
-    {
2481
-        EEH_Template::display_template(
2482
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2483
-        );
2484
-    }
2485
-
2486
-
2487
-    private function _publish_post_box()
2488
-    {
2489
-        $meta_box_ref = 'espresso_' . $this->page_slug . '_editor_overview';
2490
-        // if there is a array('label' => array('publishbox' => 'some title') ) present in the _page_config array
2491
-        // then we'll use that for the metabox label.
2492
-        // Otherwise we'll just use publish (publishbox itself could be an array of labels indexed by routes)
2493
-        if (! empty($this->_labels['publishbox'])) {
2494
-            $box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][ $this->_req_action ]
2495
-                : $this->_labels['publishbox'];
2496
-        } else {
2497
-            $box_label = esc_html__('Publish', 'event_espresso');
2498
-        }
2499
-        $box_label = apply_filters(
2500
-            'FHEE__EE_Admin_Page___publish_post_box__box_label',
2501
-            $box_label,
2502
-            $this->_req_action,
2503
-            $this
2504
-        );
2505
-        add_meta_box(
2506
-            $meta_box_ref,
2507
-            $box_label,
2508
-            array($this, 'editor_overview'),
2509
-            $this->_current_screen->id,
2510
-            'side',
2511
-            'high'
2512
-        );
2513
-    }
2514
-
2515
-
2516
-    public function editor_overview()
2517
-    {
2518
-        // if we have extra content set let's add it in if not make sure its empty
2519
-        $this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2520
-            ? $this->_template_args['publish_box_extra_content']
2521
-            : '';
2522
-        echo EEH_Template::display_template(
2523
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2524
-            $this->_template_args,
2525
-            true
2526
-        );
2527
-    }
2528
-
2529
-
2530
-    /** end of globally available metaboxes section **/
2531
-
2532
-
2533
-    /**
2534
-     * Public wrapper for the protected method.  Allows plugins/addons to externally call the
2535
-     * protected method.
2536
-     *
2537
-     * @see   $this->_set_publish_post_box_vars for param details
2538
-     * @since 4.6.0
2539
-     * @param string $name
2540
-     * @param int    $id
2541
-     * @param bool   $delete
2542
-     * @param string $save_close_redirect_URL
2543
-     * @param bool   $both_btns
2544
-     * @throws EE_Error
2545
-     * @throws InvalidArgumentException
2546
-     * @throws InvalidDataTypeException
2547
-     * @throws InvalidInterfaceException
2548
-     */
2549
-    public function set_publish_post_box_vars(
2550
-        $name = '',
2551
-        $id = 0,
2552
-        $delete = false,
2553
-        $save_close_redirect_URL = '',
2554
-        $both_btns = true
2555
-    ) {
2556
-        $this->_set_publish_post_box_vars(
2557
-            $name,
2558
-            $id,
2559
-            $delete,
2560
-            $save_close_redirect_URL,
2561
-            $both_btns
2562
-        );
2563
-    }
2564
-
2565
-
2566
-    /**
2567
-     * Sets the _template_args arguments used by the _publish_post_box shortcut
2568
-     * Note: currently there is no validation for this.  However if you want the delete button, the
2569
-     * save, and save and close buttons to work properly, then you will want to include a
2570
-     * values for the name and id arguments.
2571
-     *
2572
-     * @todo  Add in validation for name/id arguments.
2573
-     * @param    string  $name                    key used for the action ID (i.e. event_id)
2574
-     * @param    int     $id                      id attached to the item published
2575
-     * @param    string  $delete                  page route callback for the delete action
2576
-     * @param    string  $save_close_redirect_URL custom URL to redirect to after Save & Close has been completed
2577
-     * @param    boolean $both_btns               whether to display BOTH the "Save & Close" and "Save" buttons or just
2578
-     *                                            the Save button
2579
-     * @throws EE_Error
2580
-     * @throws InvalidArgumentException
2581
-     * @throws InvalidDataTypeException
2582
-     * @throws InvalidInterfaceException
2583
-     */
2584
-    protected function _set_publish_post_box_vars(
2585
-        $name = '',
2586
-        $id = 0,
2587
-        $delete = '',
2588
-        $save_close_redirect_URL = '',
2589
-        $both_btns = true
2590
-    ) {
2591
-        // if Save & Close, use a custom redirect URL or default to the main page?
2592
-        $save_close_redirect_URL = ! empty($save_close_redirect_URL)
2593
-            ? $save_close_redirect_URL
2594
-            : $this->_admin_base_url;
2595
-        // create the Save & Close and Save buttons
2596
-        $this->_set_save_buttons($both_btns, array(), array(), $save_close_redirect_URL);
2597
-        // if we have extra content set let's add it in if not make sure its empty
2598
-        $this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2599
-            ? $this->_template_args['publish_box_extra_content']
2600
-            : '';
2601
-        if ($delete && ! empty($id)) {
2602
-            // make sure we have a default if just true is sent.
2603
-            $delete = ! empty($delete) ? $delete : 'delete';
2604
-            $delete_link_args = array($name => $id);
2605
-            $delete = $this->get_action_link_or_button(
2606
-                $delete,
2607
-                $delete,
2608
-                $delete_link_args,
2609
-                'submitdelete deletion'
2610
-            );
2611
-        }
2612
-        $this->_template_args['publish_delete_link'] = ! empty($id) ? $delete : '';
2613
-        if (! empty($name) && ! empty($id)) {
2614
-            $hidden_field_arr[ $name ] = array(
2615
-                'type'  => 'hidden',
2616
-                'value' => $id,
2617
-            );
2618
-            $hf = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2619
-        } else {
2620
-            $hf = '';
2621
-        }
2622
-        // add hidden field
2623
-        $this->_template_args['publish_hidden_fields'] = is_array($hf) && ! empty($name)
2624
-            ? $hf[ $name ]['field']
2625
-            : $hf;
2626
-    }
2627
-
2628
-
2629
-    /**
2630
-     * displays an error message to ppl who have javascript disabled
2631
-     *
2632
-     * @return void
2633
-     */
2634
-    private function _display_no_javascript_warning()
2635
-    {
2636
-        ?>
2446
+	}
2447
+
2448
+
2449
+	private function _espresso_links_post_box()
2450
+	{
2451
+		// Hiding until we actually have content to put in here...
2452
+		// add_meta_box('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2453
+	}
2454
+
2455
+
2456
+	public function espresso_links_post_box()
2457
+	{
2458
+		// Hiding until we actually have content to put in here...
2459
+		// EEH_Template::display_template(
2460
+		//     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2461
+		// );
2462
+	}
2463
+
2464
+
2465
+	protected function _espresso_sponsors_post_box()
2466
+	{
2467
+		if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2468
+			add_meta_box(
2469
+				'espresso_sponsors_post_box',
2470
+				esc_html__('Event Espresso Highlights', 'event_espresso'),
2471
+				array($this, 'espresso_sponsors_post_box'),
2472
+				$this->_wp_page_slug,
2473
+				'side'
2474
+			);
2475
+		}
2476
+	}
2477
+
2478
+
2479
+	public function espresso_sponsors_post_box()
2480
+	{
2481
+		EEH_Template::display_template(
2482
+			EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2483
+		);
2484
+	}
2485
+
2486
+
2487
+	private function _publish_post_box()
2488
+	{
2489
+		$meta_box_ref = 'espresso_' . $this->page_slug . '_editor_overview';
2490
+		// if there is a array('label' => array('publishbox' => 'some title') ) present in the _page_config array
2491
+		// then we'll use that for the metabox label.
2492
+		// Otherwise we'll just use publish (publishbox itself could be an array of labels indexed by routes)
2493
+		if (! empty($this->_labels['publishbox'])) {
2494
+			$box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][ $this->_req_action ]
2495
+				: $this->_labels['publishbox'];
2496
+		} else {
2497
+			$box_label = esc_html__('Publish', 'event_espresso');
2498
+		}
2499
+		$box_label = apply_filters(
2500
+			'FHEE__EE_Admin_Page___publish_post_box__box_label',
2501
+			$box_label,
2502
+			$this->_req_action,
2503
+			$this
2504
+		);
2505
+		add_meta_box(
2506
+			$meta_box_ref,
2507
+			$box_label,
2508
+			array($this, 'editor_overview'),
2509
+			$this->_current_screen->id,
2510
+			'side',
2511
+			'high'
2512
+		);
2513
+	}
2514
+
2515
+
2516
+	public function editor_overview()
2517
+	{
2518
+		// if we have extra content set let's add it in if not make sure its empty
2519
+		$this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2520
+			? $this->_template_args['publish_box_extra_content']
2521
+			: '';
2522
+		echo EEH_Template::display_template(
2523
+			EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2524
+			$this->_template_args,
2525
+			true
2526
+		);
2527
+	}
2528
+
2529
+
2530
+	/** end of globally available metaboxes section **/
2531
+
2532
+
2533
+	/**
2534
+	 * Public wrapper for the protected method.  Allows plugins/addons to externally call the
2535
+	 * protected method.
2536
+	 *
2537
+	 * @see   $this->_set_publish_post_box_vars for param details
2538
+	 * @since 4.6.0
2539
+	 * @param string $name
2540
+	 * @param int    $id
2541
+	 * @param bool   $delete
2542
+	 * @param string $save_close_redirect_URL
2543
+	 * @param bool   $both_btns
2544
+	 * @throws EE_Error
2545
+	 * @throws InvalidArgumentException
2546
+	 * @throws InvalidDataTypeException
2547
+	 * @throws InvalidInterfaceException
2548
+	 */
2549
+	public function set_publish_post_box_vars(
2550
+		$name = '',
2551
+		$id = 0,
2552
+		$delete = false,
2553
+		$save_close_redirect_URL = '',
2554
+		$both_btns = true
2555
+	) {
2556
+		$this->_set_publish_post_box_vars(
2557
+			$name,
2558
+			$id,
2559
+			$delete,
2560
+			$save_close_redirect_URL,
2561
+			$both_btns
2562
+		);
2563
+	}
2564
+
2565
+
2566
+	/**
2567
+	 * Sets the _template_args arguments used by the _publish_post_box shortcut
2568
+	 * Note: currently there is no validation for this.  However if you want the delete button, the
2569
+	 * save, and save and close buttons to work properly, then you will want to include a
2570
+	 * values for the name and id arguments.
2571
+	 *
2572
+	 * @todo  Add in validation for name/id arguments.
2573
+	 * @param    string  $name                    key used for the action ID (i.e. event_id)
2574
+	 * @param    int     $id                      id attached to the item published
2575
+	 * @param    string  $delete                  page route callback for the delete action
2576
+	 * @param    string  $save_close_redirect_URL custom URL to redirect to after Save & Close has been completed
2577
+	 * @param    boolean $both_btns               whether to display BOTH the "Save & Close" and "Save" buttons or just
2578
+	 *                                            the Save button
2579
+	 * @throws EE_Error
2580
+	 * @throws InvalidArgumentException
2581
+	 * @throws InvalidDataTypeException
2582
+	 * @throws InvalidInterfaceException
2583
+	 */
2584
+	protected function _set_publish_post_box_vars(
2585
+		$name = '',
2586
+		$id = 0,
2587
+		$delete = '',
2588
+		$save_close_redirect_URL = '',
2589
+		$both_btns = true
2590
+	) {
2591
+		// if Save & Close, use a custom redirect URL or default to the main page?
2592
+		$save_close_redirect_URL = ! empty($save_close_redirect_URL)
2593
+			? $save_close_redirect_URL
2594
+			: $this->_admin_base_url;
2595
+		// create the Save & Close and Save buttons
2596
+		$this->_set_save_buttons($both_btns, array(), array(), $save_close_redirect_URL);
2597
+		// if we have extra content set let's add it in if not make sure its empty
2598
+		$this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2599
+			? $this->_template_args['publish_box_extra_content']
2600
+			: '';
2601
+		if ($delete && ! empty($id)) {
2602
+			// make sure we have a default if just true is sent.
2603
+			$delete = ! empty($delete) ? $delete : 'delete';
2604
+			$delete_link_args = array($name => $id);
2605
+			$delete = $this->get_action_link_or_button(
2606
+				$delete,
2607
+				$delete,
2608
+				$delete_link_args,
2609
+				'submitdelete deletion'
2610
+			);
2611
+		}
2612
+		$this->_template_args['publish_delete_link'] = ! empty($id) ? $delete : '';
2613
+		if (! empty($name) && ! empty($id)) {
2614
+			$hidden_field_arr[ $name ] = array(
2615
+				'type'  => 'hidden',
2616
+				'value' => $id,
2617
+			);
2618
+			$hf = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2619
+		} else {
2620
+			$hf = '';
2621
+		}
2622
+		// add hidden field
2623
+		$this->_template_args['publish_hidden_fields'] = is_array($hf) && ! empty($name)
2624
+			? $hf[ $name ]['field']
2625
+			: $hf;
2626
+	}
2627
+
2628
+
2629
+	/**
2630
+	 * displays an error message to ppl who have javascript disabled
2631
+	 *
2632
+	 * @return void
2633
+	 */
2634
+	private function _display_no_javascript_warning()
2635
+	{
2636
+		?>
2637 2637
         <noscript>
2638 2638
             <div id="no-js-message" class="error">
2639 2639
                 <p style="font-size:1.3em;">
2640 2640
                     <span style="color:red;"><?php esc_html_e('Warning!', 'event_espresso'); ?></span>
2641 2641
                     <?php esc_html_e(
2642
-                        '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.',
2643
-                        'event_espresso'
2644
-                    ); ?>
2642
+						'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.',
2643
+						'event_espresso'
2644
+					); ?>
2645 2645
                 </p>
2646 2646
             </div>
2647 2647
         </noscript>
2648 2648
         <?php
2649
-    }
2650
-
2651
-
2652
-    /**
2653
-     * displays espresso success and/or error notices
2654
-     *
2655
-     * @return void
2656
-     */
2657
-    private function _display_espresso_notices()
2658
-    {
2659
-        $notices = $this->_get_transient(true);
2660
-        echo stripslashes($notices);
2661
-    }
2662
-
2663
-
2664
-    /**
2665
-     * spinny things pacify the masses
2666
-     *
2667
-     * @return void
2668
-     */
2669
-    protected function _add_admin_page_ajax_loading_img()
2670
-    {
2671
-        ?>
2649
+	}
2650
+
2651
+
2652
+	/**
2653
+	 * displays espresso success and/or error notices
2654
+	 *
2655
+	 * @return void
2656
+	 */
2657
+	private function _display_espresso_notices()
2658
+	{
2659
+		$notices = $this->_get_transient(true);
2660
+		echo stripslashes($notices);
2661
+	}
2662
+
2663
+
2664
+	/**
2665
+	 * spinny things pacify the masses
2666
+	 *
2667
+	 * @return void
2668
+	 */
2669
+	protected function _add_admin_page_ajax_loading_img()
2670
+	{
2671
+		?>
2672 2672
         <div id="espresso-ajax-loading" class="ajax-loading-grey">
2673 2673
             <span class="ee-spinner ee-spin"></span><span class="hidden"><?php
2674
-                esc_html_e('loading...', 'event_espresso'); ?></span>
2674
+				esc_html_e('loading...', 'event_espresso'); ?></span>
2675 2675
         </div>
2676 2676
         <?php
2677
-    }
2677
+	}
2678 2678
 
2679 2679
 
2680
-    /**
2681
-     * add admin page overlay for modal boxes
2682
-     *
2683
-     * @return void
2684
-     */
2685
-    protected function _add_admin_page_overlay()
2686
-    {
2687
-        ?>
2680
+	/**
2681
+	 * add admin page overlay for modal boxes
2682
+	 *
2683
+	 * @return void
2684
+	 */
2685
+	protected function _add_admin_page_overlay()
2686
+	{
2687
+		?>
2688 2688
         <div id="espresso-admin-page-overlay-dv" class=""></div>
2689 2689
         <?php
2690
-    }
2691
-
2692
-
2693
-    /**
2694
-     * facade for add_meta_box
2695
-     *
2696
-     * @param string  $action        where the metabox get's displayed
2697
-     * @param string  $title         Title of Metabox (output in metabox header)
2698
-     * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2699
-     *                               instead of the one created in here.
2700
-     * @param array   $callback_args an array of args supplied for the metabox
2701
-     * @param string  $column        what metabox column
2702
-     * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2703
-     * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2704
-     *                               created but just set our own callback for wp's add_meta_box.
2705
-     * @throws DomainException
2706
-     */
2707
-    public function _add_admin_page_meta_box(
2708
-        $action,
2709
-        $title,
2710
-        $callback,
2711
-        $callback_args,
2712
-        $column = 'normal',
2713
-        $priority = 'high',
2714
-        $create_func = true
2715
-    ) {
2716
-        do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2717
-        // 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.
2718
-        if (empty($callback_args) && $create_func) {
2719
-            $callback_args = array(
2720
-                'template_path' => $this->_template_path,
2721
-                'template_args' => $this->_template_args,
2722
-            );
2723
-        }
2724
-        // 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)
2725
-        $call_back_func = $create_func
2726
-            ? static function ($post, $metabox) {
2727
-                do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2728
-                echo EEH_Template::display_template(
2729
-                    $metabox['args']['template_path'],
2730
-                    $metabox['args']['template_args'],
2731
-                    true
2732
-                );
2733
-            }
2734
-            : $callback;
2735
-        add_meta_box(
2736
-            str_replace('_', '-', $action) . '-mbox',
2737
-            $title,
2738
-            $call_back_func,
2739
-            $this->_wp_page_slug,
2740
-            $column,
2741
-            $priority,
2742
-            $callback_args
2743
-        );
2744
-    }
2745
-
2746
-
2747
-    /**
2748
-     * generates HTML wrapper for and admin details page that contains metaboxes in columns
2749
-     *
2750
-     * @throws DomainException
2751
-     * @throws EE_Error
2752
-     * @throws InvalidArgumentException
2753
-     * @throws InvalidDataTypeException
2754
-     * @throws InvalidInterfaceException
2755
-     */
2756
-    public function display_admin_page_with_metabox_columns()
2757
-    {
2758
-        $this->_template_args['post_body_content'] = $this->_template_args['admin_page_content'];
2759
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2760
-            $this->_column_template_path,
2761
-            $this->_template_args,
2762
-            true
2763
-        );
2764
-        // the final wrapper
2765
-        $this->admin_page_wrapper();
2766
-    }
2767
-
2768
-
2769
-    /**
2770
-     * generates  HTML wrapper for an admin details page
2771
-     *
2772
-     * @return void
2773
-     * @throws DomainException
2774
-     * @throws EE_Error
2775
-     * @throws InvalidArgumentException
2776
-     * @throws InvalidDataTypeException
2777
-     * @throws InvalidInterfaceException
2778
-     */
2779
-    public function display_admin_page_with_sidebar()
2780
-    {
2781
-        $this->_display_admin_page(true);
2782
-    }
2783
-
2784
-
2785
-    /**
2786
-     * generates  HTML wrapper for an admin details page (except no sidebar)
2787
-     *
2788
-     * @return void
2789
-     * @throws DomainException
2790
-     * @throws EE_Error
2791
-     * @throws InvalidArgumentException
2792
-     * @throws InvalidDataTypeException
2793
-     * @throws InvalidInterfaceException
2794
-     */
2795
-    public function display_admin_page_with_no_sidebar()
2796
-    {
2797
-        $this->_display_admin_page();
2798
-    }
2799
-
2800
-
2801
-    /**
2802
-     * generates HTML wrapper for an EE about admin page (no sidebar)
2803
-     *
2804
-     * @return void
2805
-     * @throws DomainException
2806
-     * @throws EE_Error
2807
-     * @throws InvalidArgumentException
2808
-     * @throws InvalidDataTypeException
2809
-     * @throws InvalidInterfaceException
2810
-     */
2811
-    public function display_about_admin_page()
2812
-    {
2813
-        $this->_display_admin_page(false, true);
2814
-    }
2815
-
2816
-
2817
-    /**
2818
-     * display_admin_page
2819
-     * contains the code for actually displaying an admin page
2820
-     *
2821
-     * @param boolean $sidebar true with sidebar, false without
2822
-     * @param boolean $about   use the about admin wrapper instead of the default.
2823
-     * @return void
2824
-     * @throws DomainException
2825
-     * @throws EE_Error
2826
-     * @throws InvalidArgumentException
2827
-     * @throws InvalidDataTypeException
2828
-     * @throws InvalidInterfaceException
2829
-     */
2830
-    private function _display_admin_page($sidebar = false, $about = false)
2831
-    {
2832
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2833
-        // custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2834
-        do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2835
-        // set current wp page slug - looks like: event-espresso_page_event_categories
2836
-        // keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2837
-        $this->_template_args['current_page'] = $this->_wp_page_slug;
2838
-        $this->_template_args['admin_page_wrapper_div_id'] = $this->_cpt_route
2839
-            ? 'poststuff'
2840
-            : 'espresso-default-admin';
2841
-        $template_path = $sidebar
2842
-            ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2843
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2844
-        if (defined('DOING_AJAX') && DOING_AJAX) {
2845
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2846
-        }
2847
-        $template_path = ! empty($this->_column_template_path)
2848
-            ? $this->_column_template_path : $template_path;
2849
-        $this->_template_args['post_body_content'] = isset($this->_template_args['admin_page_content'])
2850
-            ? $this->_template_args['admin_page_content']
2851
-            : '';
2852
-        $this->_template_args['before_admin_page_content'] = isset($this->_template_args['before_admin_page_content'])
2853
-            ? $this->_template_args['before_admin_page_content']
2854
-            : '';
2855
-        $this->_template_args['after_admin_page_content'] = isset($this->_template_args['after_admin_page_content'])
2856
-            ? $this->_template_args['after_admin_page_content']
2857
-            : '';
2858
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2859
-            $template_path,
2860
-            $this->_template_args,
2861
-            true
2862
-        );
2863
-        // the final template wrapper
2864
-        $this->admin_page_wrapper($about);
2865
-    }
2866
-
2867
-
2868
-    /**
2869
-     * This is used to display caf preview pages.
2870
-     *
2871
-     * @since 4.3.2
2872
-     * @param string $utm_campaign_source what is the key used for google analytics link
2873
-     * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2874
-     *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2875
-     * @return void
2876
-     * @throws DomainException
2877
-     * @throws EE_Error
2878
-     * @throws InvalidArgumentException
2879
-     * @throws InvalidDataTypeException
2880
-     * @throws InvalidInterfaceException
2881
-     */
2882
-    public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2883
-    {
2884
-        // let's generate a default preview action button if there isn't one already present.
2885
-        $this->_labels['buttons']['buy_now'] = esc_html__(
2886
-            'Upgrade to Event Espresso 4 Right Now',
2887
-            'event_espresso'
2888
-        );
2889
-        $buy_now_url = add_query_arg(
2890
-            array(
2891
-                'ee_ver'       => 'ee4',
2892
-                'utm_source'   => 'ee4_plugin_admin',
2893
-                'utm_medium'   => 'link',
2894
-                'utm_campaign' => $utm_campaign_source,
2895
-                'utm_content'  => 'buy_now_button',
2896
-            ),
2897
-            'http://eventespresso.com/pricing/'
2898
-        );
2899
-        $this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2900
-            ? $this->get_action_link_or_button(
2901
-                '',
2902
-                'buy_now',
2903
-                array(),
2904
-                'button-primary button-large',
2905
-                $buy_now_url,
2906
-                true
2907
-            )
2908
-            : $this->_template_args['preview_action_button'];
2909
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2910
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2911
-            $this->_template_args,
2912
-            true
2913
-        );
2914
-        $this->_display_admin_page($display_sidebar);
2915
-    }
2916
-
2917
-
2918
-    /**
2919
-     * display_admin_list_table_page_with_sidebar
2920
-     * generates HTML wrapper for an admin_page with list_table
2921
-     *
2922
-     * @return void
2923
-     * @throws DomainException
2924
-     * @throws EE_Error
2925
-     * @throws InvalidArgumentException
2926
-     * @throws InvalidDataTypeException
2927
-     * @throws InvalidInterfaceException
2928
-     */
2929
-    public function display_admin_list_table_page_with_sidebar()
2930
-    {
2931
-        $this->_display_admin_list_table_page(true);
2932
-    }
2933
-
2934
-
2935
-    /**
2936
-     * display_admin_list_table_page_with_no_sidebar
2937
-     * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2938
-     *
2939
-     * @return void
2940
-     * @throws DomainException
2941
-     * @throws EE_Error
2942
-     * @throws InvalidArgumentException
2943
-     * @throws InvalidDataTypeException
2944
-     * @throws InvalidInterfaceException
2945
-     */
2946
-    public function display_admin_list_table_page_with_no_sidebar()
2947
-    {
2948
-        $this->_display_admin_list_table_page();
2949
-    }
2950
-
2951
-
2952
-    /**
2953
-     * generates html wrapper for an admin_list_table page
2954
-     *
2955
-     * @param boolean $sidebar whether to display with sidebar or not.
2956
-     * @return void
2957
-     * @throws DomainException
2958
-     * @throws EE_Error
2959
-     * @throws InvalidArgumentException
2960
-     * @throws InvalidDataTypeException
2961
-     * @throws InvalidInterfaceException
2962
-     */
2963
-    private function _display_admin_list_table_page($sidebar = false)
2964
-    {
2965
-        // setup search attributes
2966
-        $this->_set_search_attributes();
2967
-        $this->_template_args['current_page'] = $this->_wp_page_slug;
2968
-        $template_path = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2969
-        $this->_template_args['table_url'] = defined('DOING_AJAX')
2970
-            ? add_query_arg(array('noheader' => 'true', 'route' => $this->_req_action), $this->_admin_base_url)
2971
-            : add_query_arg(array('route' => $this->_req_action), $this->_admin_base_url);
2972
-        $this->_template_args['list_table'] = $this->_list_table_object;
2973
-        $this->_template_args['current_route'] = $this->_req_action;
2974
-        $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2975
-        $ajax_sorting_callback = $this->_list_table_object->get_ajax_sorting_callback();
2976
-        if (! empty($ajax_sorting_callback)) {
2977
-            $sortable_list_table_form_fields = wp_nonce_field(
2978
-                $ajax_sorting_callback . '_nonce',
2979
-                $ajax_sorting_callback . '_nonce',
2980
-                false,
2981
-                false
2982
-            );
2983
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2984
-                                                . $this->page_slug
2985
-                                                . '" />';
2986
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2987
-                                                . $ajax_sorting_callback
2988
-                                                . '" />';
2989
-        } else {
2990
-            $sortable_list_table_form_fields = '';
2991
-        }
2992
-        $this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2993
-        $hidden_form_fields = isset($this->_template_args['list_table_hidden_fields'])
2994
-            ? $this->_template_args['list_table_hidden_fields']
2995
-            : '';
2996
-        $nonce_ref = $this->_req_action . '_nonce';
2997
-        $hidden_form_fields .= '<input type="hidden" name="'
2998
-                               . $nonce_ref
2999
-                               . '" value="'
3000
-                               . wp_create_nonce($nonce_ref)
3001
-                               . '">';
3002
-        $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
3003
-        // display message about search results?
3004
-        $this->_template_args['before_list_table'] .= ! empty($this->_req_data['s'])
3005
-            ? '<p class="ee-search-results">' . sprintf(
3006
-                esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
3007
-                trim($this->_req_data['s'], '%')
3008
-            ) . '</p>'
3009
-            : '';
3010
-        // filter before_list_table template arg
3011
-        $this->_template_args['before_list_table'] = apply_filters(
3012
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
3013
-            $this->_template_args['before_list_table'],
3014
-            $this->page_slug,
3015
-            $this->_req_data,
3016
-            $this->_req_action
3017
-        );
3018
-        // convert to array and filter again
3019
-        // arrays are easier to inject new items in a specific location,
3020
-        // but would not be backwards compatible, so we have to add a new filter
3021
-        $this->_template_args['before_list_table'] = implode(
3022
-            " \n",
3023
-            (array) apply_filters(
3024
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
3025
-                (array) $this->_template_args['before_list_table'],
3026
-                $this->page_slug,
3027
-                $this->_req_data,
3028
-                $this->_req_action
3029
-            )
3030
-        );
3031
-        // filter after_list_table template arg
3032
-        $this->_template_args['after_list_table'] = apply_filters(
3033
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3034
-            $this->_template_args['after_list_table'],
3035
-            $this->page_slug,
3036
-            $this->_req_data,
3037
-            $this->_req_action
3038
-        );
3039
-        // convert to array and filter again
3040
-        // arrays are easier to inject new items in a specific location,
3041
-        // but would not be backwards compatible, so we have to add a new filter
3042
-        $this->_template_args['after_list_table'] = implode(
3043
-            " \n",
3044
-            (array) apply_filters(
3045
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3046
-                (array) $this->_template_args['after_list_table'],
3047
-                $this->page_slug,
3048
-                $this->_req_data,
3049
-                $this->_req_action
3050
-            )
3051
-        );
3052
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3053
-            $template_path,
3054
-            $this->_template_args,
3055
-            true
3056
-        );
3057
-        // the final template wrapper
3058
-        if ($sidebar) {
3059
-            $this->display_admin_page_with_sidebar();
3060
-        } else {
3061
-            $this->display_admin_page_with_no_sidebar();
3062
-        }
3063
-    }
3064
-
3065
-
3066
-    /**
3067
-     * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3068
-     * html string for the legend.
3069
-     * $items are expected in an array in the following format:
3070
-     * $legend_items = array(
3071
-     *        'item_id' => array(
3072
-     *            'icon' => 'http://url_to_icon_being_described.png',
3073
-     *            'desc' => esc_html__('localized description of item');
3074
-     *        )
3075
-     * );
3076
-     *
3077
-     * @param  array $items see above for format of array
3078
-     * @return string html string of legend
3079
-     * @throws DomainException
3080
-     */
3081
-    protected function _display_legend($items)
3082
-    {
3083
-        $this->_template_args['items'] = apply_filters(
3084
-            'FHEE__EE_Admin_Page___display_legend__items',
3085
-            (array) $items,
3086
-            $this
3087
-        );
3088
-        /** @var EventEspresso\core\admin\StatusChangeNotice $status_change_notice */
3089
-        $status_change_notice = $this->loader->getShared('EventEspresso\core\admin\StatusChangeNotice');
3090
-        if (! $status_change_notice->isDismissed()) {
3091
-            $this->_template_args['status_change_notice'] = EEH_Template::display_template(
3092
-                EE_ADMIN_TEMPLATE . 'status_change_notice.template.php',
3093
-                [ 'context' => '__admin-legend', 'page_slug' => $this->page_slug ],
3094
-                true
3095
-            );
3096
-        }
3097
-        return EEH_Template::display_template(
3098
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3099
-            $this->_template_args,
3100
-            true
3101
-        );
3102
-    }
3103
-
3104
-
3105
-    /**
3106
-     * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3107
-     * The returned json object is created from an array in the following format:
3108
-     * array(
3109
-     *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3110
-     *  'success' => FALSE, //(default FALSE) - contains any special success message.
3111
-     *  'notices' => '', // - contains any EE_Error formatted notices
3112
-     *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3113
-     *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3114
-     *  We're also going to include the template args with every package (so js can pick out any specific template args
3115
-     *  that might be included in here)
3116
-     * )
3117
-     * The json object is populated by whatever is set in the $_template_args property.
3118
-     *
3119
-     * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3120
-     *                                 instead of displayed.
3121
-     * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3122
-     * @return void
3123
-     * @throws EE_Error
3124
-     * @throws InvalidArgumentException
3125
-     * @throws InvalidDataTypeException
3126
-     * @throws InvalidInterfaceException
3127
-     */
3128
-    protected function _return_json($sticky_notices = false, $notices_arguments = array())
3129
-    {
3130
-        // make sure any EE_Error notices have been handled.
3131
-        $this->_process_notices($notices_arguments, true, $sticky_notices);
3132
-        $data = isset($this->_template_args['data']) ? $this->_template_args['data'] : array();
3133
-        unset($this->_template_args['data']);
3134
-        $json = array(
3135
-            'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3136
-            'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3137
-            'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3138
-            'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3139
-            'notices'   => EE_Error::get_notices(),
3140
-            'content'   => isset($this->_template_args['admin_page_content'])
3141
-                ? $this->_template_args['admin_page_content'] : '',
3142
-            'data'      => array_merge($data, array('template_args' => $this->_template_args)),
3143
-            'isEEajax'  => true
3144
-            // special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3145
-        );
3146
-        // make sure there are no php errors or headers_sent.  Then we can set correct json header.
3147
-        if (null === error_get_last() || ! headers_sent()) {
3148
-            header('Content-Type: application/json; charset=UTF-8');
3149
-        }
3150
-        echo wp_json_encode($json);
3151
-        exit();
3152
-    }
3153
-
3154
-
3155
-    /**
3156
-     * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3157
-     *
3158
-     * @return void
3159
-     * @throws EE_Error
3160
-     * @throws InvalidArgumentException
3161
-     * @throws InvalidDataTypeException
3162
-     * @throws InvalidInterfaceException
3163
-     */
3164
-    public function return_json()
3165
-    {
3166
-        if (defined('DOING_AJAX') && DOING_AJAX) {
3167
-            $this->_return_json();
3168
-        } else {
3169
-            throw new EE_Error(
3170
-                sprintf(
3171
-                    esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3172
-                    __FUNCTION__
3173
-                )
3174
-            );
3175
-        }
3176
-    }
3177
-
3178
-
3179
-    /**
3180
-     * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3181
-     * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3182
-     *
3183
-     * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3184
-     */
3185
-    public function set_hook_object(EE_Admin_Hooks $hook_obj)
3186
-    {
3187
-        $this->_hook_obj = $hook_obj;
3188
-    }
3189
-
3190
-
3191
-    /**
3192
-     *        generates  HTML wrapper with Tabbed nav for an admin page
3193
-     *
3194
-     * @param boolean $about whether to use the special about page wrapper or default.
3195
-     * @return void
3196
-     * @throws DomainException
3197
-     * @throws EE_Error
3198
-     * @throws InvalidArgumentException
3199
-     * @throws InvalidDataTypeException
3200
-     * @throws InvalidInterfaceException
3201
-     */
3202
-    public function admin_page_wrapper($about = false)
3203
-    {
3204
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3205
-        $this->_nav_tabs = $this->_get_main_nav_tabs();
3206
-        $this->_template_args['nav_tabs'] = $this->_nav_tabs;
3207
-        $this->_template_args['admin_page_title'] = $this->_admin_page_title;
3208
-        $this->_template_args['before_admin_page_content'] = apply_filters(
3209
-            "FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3210
-            isset($this->_template_args['before_admin_page_content'])
3211
-                ? $this->_template_args['before_admin_page_content']
3212
-                : ''
3213
-        );
3214
-        $this->_template_args['after_admin_page_content'] = apply_filters(
3215
-            "FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3216
-            isset($this->_template_args['after_admin_page_content'])
3217
-                ? $this->_template_args['after_admin_page_content']
3218
-                : ''
3219
-        );
3220
-        $this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3221
-        // load settings page wrapper template
3222
-        $template_path = ! defined('DOING_AJAX')
3223
-            ? EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php'
3224
-            : EE_ADMIN_TEMPLATE
3225
-              . 'admin_wrapper_ajax.template.php';
3226
-        // about page?
3227
-        $template_path = $about
3228
-            ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3229
-            : $template_path;
3230
-        if (defined('DOING_AJAX')) {
3231
-            $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3232
-                $template_path,
3233
-                $this->_template_args,
3234
-                true
3235
-            );
3236
-            $this->_return_json();
3237
-        } else {
3238
-            EEH_Template::display_template($template_path, $this->_template_args);
3239
-        }
3240
-    }
3241
-
3242
-
3243
-    /**
3244
-     * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3245
-     *
3246
-     * @return string html
3247
-     * @throws EE_Error
3248
-     */
3249
-    protected function _get_main_nav_tabs()
3250
-    {
3251
-        // let's generate the html using the EEH_Tabbed_Content helper.
3252
-        // We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3253
-        // (rather than setting in the page_routes array)
3254
-        return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs);
3255
-    }
3256
-
3257
-
3258
-    /**
3259
-     *        sort nav tabs
3260
-     *
3261
-     * @param $a
3262
-     * @param $b
3263
-     * @return int
3264
-     */
3265
-    private function _sort_nav_tabs($a, $b)
3266
-    {
3267
-        if ($a['order'] === $b['order']) {
3268
-            return 0;
3269
-        }
3270
-        return ($a['order'] < $b['order']) ? -1 : 1;
3271
-    }
3272
-
3273
-
3274
-    /**
3275
-     *    generates HTML for the forms used on admin pages
3276
-     *
3277
-     * @param    array $input_vars - array of input field details
3278
-     * @param string   $generator  (options are 'string' or 'array', basically use this to indicate which generator to
3279
-     *                             use)
3280
-     * @param bool     $id
3281
-     * @return string
3282
-     * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3283
-     * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3284
-     */
3285
-    protected function _generate_admin_form_fields($input_vars = array(), $generator = 'string', $id = false)
3286
-    {
3287
-        return $generator === 'string'
3288
-            ? EEH_Form_Fields::get_form_fields($input_vars, $id)
3289
-            : EEH_Form_Fields::get_form_fields_array($input_vars);
3290
-    }
3291
-
3292
-
3293
-    /**
3294
-     * generates the "Save" and "Save & Close" buttons for edit forms
3295
-     *
3296
-     * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3297
-     *                                   Close" button.
3298
-     * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3299
-     *                                   'Save', [1] => 'save & close')
3300
-     * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3301
-     *                                   via the "name" value in the button).  We can also use this to just dump
3302
-     *                                   default actions by submitting some other value.
3303
-     * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3304
-     *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3305
-     *                                   close (normal form handling).
3306
-     */
3307
-    protected function _set_save_buttons($both = true, $text = array(), $actions = array(), $referrer = null)
3308
-    {
3309
-        // make sure $text and $actions are in an array
3310
-        $text = (array) $text;
3311
-        $actions = (array) $actions;
3312
-        $referrer_url = empty($referrer)
3313
-            ? '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3314
-              . $_SERVER['REQUEST_URI']
3315
-              . '" />'
3316
-            : '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3317
-              . $referrer
3318
-              . '" />';
3319
-        $button_text = ! empty($text)
3320
-            ? $text
3321
-            : array(
3322
-                esc_html__('Save', 'event_espresso'),
3323
-                esc_html__('Save and Close', 'event_espresso'),
3324
-            );
3325
-        $default_names = array('save', 'save_and_close');
3326
-        // add in a hidden index for the current page (so save and close redirects properly)
3327
-        $this->_template_args['save_buttons'] = $referrer_url;
3328
-        foreach ($button_text as $key => $button) {
3329
-            $ref = $default_names[ $key ];
3330
-            $this->_template_args['save_buttons'] .= '<input type="submit" class="button-primary '
3331
-                                                     . $ref
3332
-                                                     . '" value="'
3333
-                                                     . $button
3334
-                                                     . '" name="'
3335
-                                                     . (! empty($actions) ? $actions[ $key ] : $ref)
3336
-                                                     . '" id="'
3337
-                                                     . $this->_current_view . '_' . $ref
3338
-                                                     . '" />';
3339
-            if (! $both) {
3340
-                break;
3341
-            }
3342
-        }
3343
-    }
3344
-
3345
-
3346
-    /**
3347
-     * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3348
-     *
3349
-     * @see   $this->_set_add_edit_form_tags() for details on params
3350
-     * @since 4.6.0
3351
-     * @param string $route
3352
-     * @param array  $additional_hidden_fields
3353
-     */
3354
-    public function set_add_edit_form_tags($route = '', $additional_hidden_fields = array())
3355
-    {
3356
-        $this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3357
-    }
3358
-
3359
-
3360
-    /**
3361
-     * set form open and close tags on add/edit pages.
3362
-     *
3363
-     * @param string $route                    the route you want the form to direct to
3364
-     * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3365
-     * @return void
3366
-     */
3367
-    protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = array())
3368
-    {
3369
-        if (empty($route)) {
3370
-            $user_msg = esc_html__(
3371
-                'An error occurred. No action was set for this page\'s form.',
3372
-                'event_espresso'
3373
-            );
3374
-            $dev_msg = $user_msg . "\n"
3375
-                       . sprintf(
3376
-                           esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3377
-                           __FUNCTION__,
3378
-                           EE_Admin_Page::class
3379
-                       );
3380
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3381
-        }
3382
-        // open form
3383
-        $this->_template_args['before_admin_page_content'] = '<form name="form" method="post" action="'
3384
-                                                             . $this->_admin_base_url
3385
-                                                             . '" id="'
3386
-                                                             . $route
3387
-                                                             . '_event_form" >';
3388
-        // add nonce
3389
-        $nonce = wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3390
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3391
-        // add REQUIRED form action
3392
-        $hidden_fields = array(
3393
-            'action' => array('type' => 'hidden', 'value' => $route),
3394
-        );
3395
-        // merge arrays
3396
-        $hidden_fields = is_array($additional_hidden_fields)
3397
-            ? array_merge($hidden_fields, $additional_hidden_fields)
3398
-            : $hidden_fields;
3399
-        // generate form fields
3400
-        $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3401
-        // add fields to form
3402
-        foreach ((array) $form_fields as $field_name => $form_field) {
3403
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3404
-        }
3405
-        // close form
3406
-        $this->_template_args['after_admin_page_content'] = '</form>';
3407
-    }
3408
-
3409
-
3410
-    /**
3411
-     * Public Wrapper for _redirect_after_action() method since its
3412
-     * discovered it would be useful for external code to have access.
3413
-     *
3414
-     * @param bool   $success
3415
-     * @param string $what
3416
-     * @param string $action_desc
3417
-     * @param array  $query_args
3418
-     * @param bool   $override_overwrite
3419
-     * @throws EE_Error
3420
-     * @throws InvalidArgumentException
3421
-     * @throws InvalidDataTypeException
3422
-     * @throws InvalidInterfaceException
3423
-     * @see   EE_Admin_Page::_redirect_after_action() for params.
3424
-     * @since 4.5.0
3425
-     */
3426
-    public function redirect_after_action(
3427
-        $success = false,
3428
-        $what = 'item',
3429
-        $action_desc = 'processed',
3430
-        $query_args = array(),
3431
-        $override_overwrite = false
3432
-    ) {
3433
-        $this->_redirect_after_action(
3434
-            $success,
3435
-            $what,
3436
-            $action_desc,
3437
-            $query_args,
3438
-            $override_overwrite
3439
-        );
3440
-    }
3441
-
3442
-
3443
-    /**
3444
-     * Helper method for merging existing request data with the returned redirect url.
3445
-     *
3446
-     * This is typically used for redirects after an action so that if the original view was a filtered view those
3447
-     * filters are still applied.
3448
-     *
3449
-     * @param array $new_route_data
3450
-     * @return array
3451
-     */
3452
-    protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3453
-    {
3454
-        foreach ($this->_req_data as $ref => $value) {
3455
-            // unset nonces
3456
-            if (strpos($ref, 'nonce') !== false) {
3457
-                unset($this->_req_data[ $ref ]);
3458
-                continue;
3459
-            }
3460
-            // urlencode values.
3461
-            $value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3462
-            $this->_req_data[ $ref ] = $value;
3463
-        }
3464
-        return array_merge($this->_req_data, $new_route_data);
3465
-    }
3466
-
3467
-
3468
-    /**
3469
-     *    _redirect_after_action
3470
-     *
3471
-     * @param int    $success            - whether success was for two or more records, or just one, or none
3472
-     * @param string $what               - what the action was performed on
3473
-     * @param string $action_desc        - what was done ie: updated, deleted, etc
3474
-     * @param array  $query_args         - an array of query_args to be added to the URL to redirect to after the admin
3475
-     *                                   action is completed
3476
-     * @param BOOL   $override_overwrite by default all EE_Error::success messages are overwritten, this allows you to
3477
-     *                                   override this so that they show.
3478
-     * @return void
3479
-     * @throws EE_Error
3480
-     * @throws InvalidArgumentException
3481
-     * @throws InvalidDataTypeException
3482
-     * @throws InvalidInterfaceException
3483
-     */
3484
-    protected function _redirect_after_action(
3485
-        $success = 0,
3486
-        $what = 'item',
3487
-        $action_desc = 'processed',
3488
-        $query_args = array(),
3489
-        $override_overwrite = false
3490
-    ) {
3491
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3492
-        // class name for actions/filters.
3493
-        $classname = get_class($this);
3494
-        // set redirect url.
3495
-        // Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3496
-        // otherwise we go with whatever is set as the _admin_base_url
3497
-        $redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3498
-        $notices = EE_Error::get_notices(false);
3499
-        // overwrite default success messages //BUT ONLY if overwrite not overridden
3500
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3501
-            EE_Error::overwrite_success();
3502
-        }
3503
-        if (! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3504
-            // how many records affected ? more than one record ? or just one ?
3505
-            if ($success > 1) {
3506
-                // set plural msg
3507
-                EE_Error::add_success(
3508
-                    sprintf(
3509
-                        esc_html__('The "%s" have been successfully %s.', 'event_espresso'),
3510
-                        $what,
3511
-                        $action_desc
3512
-                    ),
3513
-                    __FILE__,
3514
-                    __FUNCTION__,
3515
-                    __LINE__
3516
-                );
3517
-            } elseif ($success === 1) {
3518
-                // set singular msg
3519
-                EE_Error::add_success(
3520
-                    sprintf(
3521
-                        esc_html__('The "%s" has been successfully %s.', 'event_espresso'),
3522
-                        $what,
3523
-                        $action_desc
3524
-                    ),
3525
-                    __FILE__,
3526
-                    __FUNCTION__,
3527
-                    __LINE__
3528
-                );
3529
-            }
3530
-        }
3531
-        // check that $query_args isn't something crazy
3532
-        if (! is_array($query_args)) {
3533
-            $query_args = array();
3534
-        }
3535
-        /**
3536
-         * Allow injecting actions before the query_args are modified for possible different
3537
-         * redirections on save and close actions
3538
-         *
3539
-         * @since 4.2.0
3540
-         * @param array $query_args       The original query_args array coming into the
3541
-         *                                method.
3542
-         */
3543
-        do_action(
3544
-            "AHEE__{$classname}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3545
-            $query_args
3546
-        );
3547
-        // calculate where we're going (if we have a "save and close" button pushed)
3548
-        if (isset($this->_req_data['save_and_close'], $this->_req_data['save_and_close_referrer'])) {
3549
-            // even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3550
-            $parsed_url = parse_url($this->_req_data['save_and_close_referrer']);
3551
-            // regenerate query args array from referrer URL
3552
-            parse_str($parsed_url['query'], $query_args);
3553
-            // correct page and action will be in the query args now
3554
-            $redirect_url = admin_url('admin.php');
3555
-        }
3556
-        // merge any default query_args set in _default_route_query_args property
3557
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3558
-            $args_to_merge = array();
3559
-            foreach ($this->_default_route_query_args as $query_param => $query_value) {
3560
-                // is there a wp_referer array in our _default_route_query_args property?
3561
-                if ($query_param === 'wp_referer') {
3562
-                    $query_value = (array) $query_value;
3563
-                    foreach ($query_value as $reference => $value) {
3564
-                        if (strpos($reference, 'nonce') !== false) {
3565
-                            continue;
3566
-                        }
3567
-                        // finally we will override any arguments in the referer with
3568
-                        // what might be set on the _default_route_query_args array.
3569
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3570
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3571
-                        } else {
3572
-                            $args_to_merge[ $reference ] = urlencode($value);
3573
-                        }
3574
-                    }
3575
-                    continue;
3576
-                }
3577
-                $args_to_merge[ $query_param ] = $query_value;
3578
-            }
3579
-            // now let's merge these arguments but override with what was specifically sent in to the
3580
-            // redirect.
3581
-            $query_args = array_merge($args_to_merge, $query_args);
3582
-        }
3583
-        $this->_process_notices($query_args);
3584
-        // generate redirect url
3585
-        // if redirecting to anything other than the main page, add a nonce
3586
-        if (isset($query_args['action'])) {
3587
-            // manually generate wp_nonce and merge that with the query vars
3588
-            // becuz the wp_nonce_url function wrecks havoc on some vars
3589
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3590
-        }
3591
-        // we're adding some hooks and filters in here for processing any things just before redirects
3592
-        // (example: an admin page has done an insert or update and we want to run something after that).
3593
-        do_action('AHEE_redirect_' . $classname . $this->_req_action, $query_args);
3594
-        $redirect_url = apply_filters(
3595
-            'FHEE_redirect_' . $classname . $this->_req_action,
3596
-            EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3597
-            $query_args
3598
-        );
3599
-        // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3600
-        if (defined('DOING_AJAX')) {
3601
-            $default_data = array(
3602
-                'close'        => true,
3603
-                'redirect_url' => $redirect_url,
3604
-                'where'        => 'main',
3605
-                'what'         => 'append',
3606
-            );
3607
-            $this->_template_args['success'] = $success;
3608
-            $this->_template_args['data'] = ! empty($this->_template_args['data']) ? array_merge(
3609
-                $default_data,
3610
-                $this->_template_args['data']
3611
-            ) : $default_data;
3612
-            $this->_return_json();
3613
-        }
3614
-        wp_safe_redirect($redirect_url);
3615
-        exit();
3616
-    }
3617
-
3618
-
3619
-    /**
3620
-     * process any notices before redirecting (or returning ajax request)
3621
-     * This method sets the $this->_template_args['notices'] attribute;
3622
-     *
3623
-     * @param array $query_args         any query args that need to be used for notice transient ('action')
3624
-     * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3625
-     *                                  page_routes haven't been defined yet.
3626
-     * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3627
-     *                                  still save a transient for the notice.
3628
-     * @return void
3629
-     * @throws EE_Error
3630
-     * @throws InvalidArgumentException
3631
-     * @throws InvalidDataTypeException
3632
-     * @throws InvalidInterfaceException
3633
-     */
3634
-    protected function _process_notices($query_args = array(), $skip_route_verify = false, $sticky_notices = true)
3635
-    {
3636
-        // first let's set individual error properties if doing_ajax and the properties aren't already set.
3637
-        if (defined('DOING_AJAX') && DOING_AJAX) {
3638
-            $notices = EE_Error::get_notices(false);
3639
-            if (empty($this->_template_args['success'])) {
3640
-                $this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3641
-            }
3642
-            if (empty($this->_template_args['errors'])) {
3643
-                $this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3644
-            }
3645
-            if (empty($this->_template_args['attention'])) {
3646
-                $this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3647
-            }
3648
-        }
3649
-        $this->_template_args['notices'] = EE_Error::get_notices();
3650
-        // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3651
-        if (! defined('DOING_AJAX') || $sticky_notices) {
3652
-            $route = isset($query_args['action']) ? $query_args['action'] : 'default';
3653
-            $this->_add_transient(
3654
-                $route,
3655
-                $this->_template_args['notices'],
3656
-                true,
3657
-                $skip_route_verify
3658
-            );
3659
-        }
3660
-    }
3661
-
3662
-
3663
-    /**
3664
-     * get_action_link_or_button
3665
-     * returns the button html for adding, editing, or deleting an item (depending on given type)
3666
-     *
3667
-     * @param string $action        use this to indicate which action the url is generated with.
3668
-     * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3669
-     *                              property.
3670
-     * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3671
-     * @param string $class         Use this to give the class for the button. Defaults to 'button-primary'
3672
-     * @param string $base_url      If this is not provided
3673
-     *                              the _admin_base_url will be used as the default for the button base_url.
3674
-     *                              Otherwise this value will be used.
3675
-     * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3676
-     * @return string
3677
-     * @throws InvalidArgumentException
3678
-     * @throws InvalidInterfaceException
3679
-     * @throws InvalidDataTypeException
3680
-     * @throws EE_Error
3681
-     */
3682
-    public function get_action_link_or_button(
3683
-        $action,
3684
-        $type = 'add',
3685
-        $extra_request = array(),
3686
-        $class = 'button-primary',
3687
-        $base_url = '',
3688
-        $exclude_nonce = false
3689
-    ) {
3690
-        // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3691
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3692
-            throw new EE_Error(
3693
-                sprintf(
3694
-                    esc_html__(
3695
-                        'There is no page route for given action for the button.  This action was given: %s',
3696
-                        'event_espresso'
3697
-                    ),
3698
-                    $action
3699
-                )
3700
-            );
3701
-        }
3702
-        if (! isset($this->_labels['buttons'][ $type ])) {
3703
-            throw new EE_Error(
3704
-                sprintf(
3705
-                    __(
3706
-                        'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3707
-                        'event_espresso'
3708
-                    ),
3709
-                    $type
3710
-                )
3711
-            );
3712
-        }
3713
-        // finally check user access for this button.
3714
-        $has_access = $this->check_user_access($action, true);
3715
-        if (! $has_access) {
3716
-            return '';
3717
-        }
3718
-        $_base_url = ! $base_url ? $this->_admin_base_url : $base_url;
3719
-        $query_args = array(
3720
-            'action' => $action,
3721
-        );
3722
-        // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3723
-        if (! empty($extra_request)) {
3724
-            $query_args = array_merge($extra_request, $query_args);
3725
-        }
3726
-        $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3727
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3728
-    }
3729
-
3730
-
3731
-    /**
3732
-     * _per_page_screen_option
3733
-     * Utility function for adding in a per_page_option in the screen_options_dropdown.
3734
-     *
3735
-     * @return void
3736
-     * @throws InvalidArgumentException
3737
-     * @throws InvalidInterfaceException
3738
-     * @throws InvalidDataTypeException
3739
-     */
3740
-    protected function _per_page_screen_option()
3741
-    {
3742
-        $option = 'per_page';
3743
-        $args = array(
3744
-            'label'   => apply_filters(
3745
-                'FHEE__EE_Admin_Page___per_page_screen_options___label',
3746
-                $this->_admin_page_title,
3747
-                $this
3748
-            ),
3749
-            'default' => (int) apply_filters(
3750
-                'FHEE__EE_Admin_Page___per_page_screen_options__default',
3751
-                20
3752
-            ),
3753
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3754
-        );
3755
-        // ONLY add the screen option if the user has access to it.
3756
-        if ($this->check_user_access($this->_current_view, true)) {
3757
-            add_screen_option($option, $args);
3758
-        }
3759
-    }
3760
-
3761
-
3762
-    /**
3763
-     * set_per_page_screen_option
3764
-     * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3765
-     * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3766
-     * admin_menu.
3767
-     *
3768
-     * @return void
3769
-     */
3770
-    private function _set_per_page_screen_options()
3771
-    {
3772
-        if (isset($_POST['wp_screen_options']) && is_array($_POST['wp_screen_options'])) {
3773
-            check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3774
-            if (! $user = wp_get_current_user()) {
3775
-                return;
3776
-            }
3777
-            $option = $_POST['wp_screen_options']['option'];
3778
-            $value = $_POST['wp_screen_options']['value'];
3779
-            if ($option !== sanitize_key($option)) {
3780
-                return;
3781
-            }
3782
-            $map_option = $option;
3783
-            $option = str_replace('-', '_', $option);
3784
-            switch ($map_option) {
3785
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3786
-                    $value = (int) $value;
3787
-                    $max_value = apply_filters(
3788
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3789
-                        999,
3790
-                        $this->_current_page,
3791
-                        $this->_current_view
3792
-                    );
3793
-                    if ($value < 1) {
3794
-                        return;
3795
-                    }
3796
-                    $value = min($value, $max_value);
3797
-                    break;
3798
-                default:
3799
-                    $value = apply_filters(
3800
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3801
-                        false,
3802
-                        $option,
3803
-                        $value
3804
-                    );
3805
-                    if (false === $value) {
3806
-                        return;
3807
-                    }
3808
-                    break;
3809
-            }
3810
-            update_user_meta($user->ID, $option, $value);
3811
-            wp_safe_redirect(remove_query_arg(array('pagenum', 'apage', 'paged'), wp_get_referer()));
3812
-            exit;
3813
-        }
3814
-    }
3815
-
3816
-
3817
-    /**
3818
-     * This just allows for setting the $_template_args property if it needs to be set outside the object
3819
-     *
3820
-     * @param array $data array that will be assigned to template args.
3821
-     */
3822
-    public function set_template_args($data)
3823
-    {
3824
-        $this->_template_args = array_merge($this->_template_args, (array) $data);
3825
-    }
3826
-
3827
-
3828
-    /**
3829
-     * This makes available the WP transient system for temporarily moving data between routes
3830
-     *
3831
-     * @param string $route             the route that should receive the transient
3832
-     * @param array  $data              the data that gets sent
3833
-     * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3834
-     *                                  normal route transient.
3835
-     * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3836
-     *                                  when we are adding a transient before page_routes have been defined.
3837
-     * @return void
3838
-     * @throws EE_Error
3839
-     */
3840
-    protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3841
-    {
3842
-        $user_id = get_current_user_id();
3843
-        if (! $skip_route_verify) {
3844
-            $this->_verify_route($route);
3845
-        }
3846
-        // now let's set the string for what kind of transient we're setting
3847
-        $transient = $notices
3848
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3849
-            : 'rte_tx_' . $route . '_' . $user_id;
3850
-        $data = $notices ? array('notices' => $data) : $data;
3851
-        // is there already a transient for this route?  If there is then let's ADD to that transient
3852
-        $existing = is_multisite() && is_network_admin()
3853
-            ? get_site_transient($transient)
3854
-            : get_transient($transient);
3855
-        if ($existing) {
3856
-            $data = array_merge((array) $data, (array) $existing);
3857
-        }
3858
-        if (is_multisite() && is_network_admin()) {
3859
-            set_site_transient($transient, $data, 8);
3860
-        } else {
3861
-            set_transient($transient, $data, 8);
3862
-        }
3863
-    }
3864
-
3865
-
3866
-    /**
3867
-     * this retrieves the temporary transient that has been set for moving data between routes.
3868
-     *
3869
-     * @param bool   $notices true we get notices transient. False we just return normal route transient
3870
-     * @param string $route
3871
-     * @return mixed data
3872
-     */
3873
-    protected function _get_transient($notices = false, $route = '')
3874
-    {
3875
-        $user_id = get_current_user_id();
3876
-        $route = ! $route ? $this->_req_action : $route;
3877
-        $transient = $notices
3878
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3879
-            : 'rte_tx_' . $route . '_' . $user_id;
3880
-        $data = is_multisite() && is_network_admin()
3881
-            ? get_site_transient($transient)
3882
-            : get_transient($transient);
3883
-        // delete transient after retrieval (just in case it hasn't expired);
3884
-        if (is_multisite() && is_network_admin()) {
3885
-            delete_site_transient($transient);
3886
-        } else {
3887
-            delete_transient($transient);
3888
-        }
3889
-        return $notices && isset($data['notices']) ? $data['notices'] : $data;
3890
-    }
3891
-
3892
-
3893
-    /**
3894
-     * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3895
-     * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3896
-     * default route callback on the EE_Admin page you want it run.)
3897
-     *
3898
-     * @return void
3899
-     */
3900
-    protected function _transient_garbage_collection()
3901
-    {
3902
-        global $wpdb;
3903
-        // retrieve all existing transients
3904
-        $query = "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3905
-        if ($results = $wpdb->get_results($query)) {
3906
-            foreach ($results as $result) {
3907
-                $transient = str_replace('_transient_', '', $result->option_name);
3908
-                get_transient($transient);
3909
-                if (is_multisite() && is_network_admin()) {
3910
-                    get_site_transient($transient);
3911
-                }
3912
-            }
3913
-        }
3914
-    }
3915
-
3916
-
3917
-    /**
3918
-     * get_view
3919
-     *
3920
-     * @return string content of _view property
3921
-     */
3922
-    public function get_view()
3923
-    {
3924
-        return $this->_view;
3925
-    }
3926
-
3927
-
3928
-    /**
3929
-     * getter for the protected $_views property
3930
-     *
3931
-     * @return array
3932
-     */
3933
-    public function get_views()
3934
-    {
3935
-        return $this->_views;
3936
-    }
3937
-
3938
-
3939
-    /**
3940
-     * get_current_page
3941
-     *
3942
-     * @return string _current_page property value
3943
-     */
3944
-    public function get_current_page()
3945
-    {
3946
-        return $this->_current_page;
3947
-    }
3948
-
3949
-
3950
-    /**
3951
-     * get_current_view
3952
-     *
3953
-     * @return string _current_view property value
3954
-     */
3955
-    public function get_current_view()
3956
-    {
3957
-        return $this->_current_view;
3958
-    }
3959
-
3960
-
3961
-    /**
3962
-     * get_current_screen
3963
-     *
3964
-     * @return object The current WP_Screen object
3965
-     */
3966
-    public function get_current_screen()
3967
-    {
3968
-        return $this->_current_screen;
3969
-    }
3970
-
3971
-
3972
-    /**
3973
-     * get_current_page_view_url
3974
-     *
3975
-     * @return string This returns the url for the current_page_view.
3976
-     */
3977
-    public function get_current_page_view_url()
3978
-    {
3979
-        return $this->_current_page_view_url;
3980
-    }
3981
-
3982
-
3983
-    /**
3984
-     * just returns the _req_data property
3985
-     *
3986
-     * @return array
3987
-     */
3988
-    public function get_request_data()
3989
-    {
3990
-        return $this->_req_data;
3991
-    }
3992
-
3993
-
3994
-    /**
3995
-     * returns the _req_data protected property
3996
-     *
3997
-     * @return string
3998
-     */
3999
-    public function get_req_action()
4000
-    {
4001
-        return $this->_req_action;
4002
-    }
4003
-
4004
-
4005
-    /**
4006
-     * @return bool  value of $_is_caf property
4007
-     */
4008
-    public function is_caf()
4009
-    {
4010
-        return $this->_is_caf;
4011
-    }
4012
-
4013
-
4014
-    /**
4015
-     * @return mixed
4016
-     */
4017
-    public function default_espresso_metaboxes()
4018
-    {
4019
-        return $this->_default_espresso_metaboxes;
4020
-    }
4021
-
4022
-
4023
-    /**
4024
-     * @return mixed
4025
-     */
4026
-    public function admin_base_url()
4027
-    {
4028
-        return $this->_admin_base_url;
4029
-    }
4030
-
4031
-
4032
-    /**
4033
-     * @return mixed
4034
-     */
4035
-    public function wp_page_slug()
4036
-    {
4037
-        return $this->_wp_page_slug;
4038
-    }
4039
-
4040
-
4041
-    /**
4042
-     * updates  espresso configuration settings
4043
-     *
4044
-     * @param string                   $tab
4045
-     * @param EE_Config_Base|EE_Config $config
4046
-     * @param string                   $file file where error occurred
4047
-     * @param string                   $func function  where error occurred
4048
-     * @param string                   $line line no where error occurred
4049
-     * @return boolean
4050
-     */
4051
-    protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
4052
-    {
4053
-        // remove any options that are NOT going to be saved with the config settings.
4054
-        if (isset($config->core->ee_ueip_optin)) {
4055
-            // TODO: remove the following two lines and make sure values are migrated from 3.1
4056
-            update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4057
-            update_option('ee_ueip_has_notified', true);
4058
-        }
4059
-        // and save it (note we're also doing the network save here)
4060
-        $net_saved = is_main_site() ? EE_Network_Config::instance()->update_config(false, false) : true;
4061
-        $config_saved = EE_Config::instance()->update_espresso_config(false, false);
4062
-        if ($config_saved && $net_saved) {
4063
-            EE_Error::add_success(sprintf(__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4064
-            return true;
4065
-        }
4066
-        EE_Error::add_error(sprintf(__('The "%s" were not updated.', 'event_espresso'), $tab), $file, $func, $line);
4067
-        return false;
4068
-    }
4069
-
4070
-
4071
-    /**
4072
-     * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4073
-     *
4074
-     * @return array
4075
-     */
4076
-    public function get_yes_no_values()
4077
-    {
4078
-        return $this->_yes_no_values;
4079
-    }
4080
-
4081
-
4082
-    /**
4083
-     * @return string
4084
-     * @throws ReflectionException
4085
-     * @since $VID:$
4086
-     */
4087
-    protected function _get_dir()
4088
-    {
4089
-        $reflector = new ReflectionClass(get_class($this));
4090
-        return dirname($reflector->getFileName());
4091
-    }
4092
-
4093
-
4094
-    /**
4095
-     * A helper for getting a "next link".
4096
-     *
4097
-     * @param string $url   The url to link to
4098
-     * @param string $class The class to use.
4099
-     * @return string
4100
-     */
4101
-    protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4102
-    {
4103
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4104
-    }
4105
-
4106
-
4107
-    /**
4108
-     * A helper for getting a "previous link".
4109
-     *
4110
-     * @param string $url   The url to link to
4111
-     * @param string $class The class to use.
4112
-     * @return string
4113
-     */
4114
-    protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4115
-    {
4116
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4117
-    }
4118
-
4119
-
4120
-
4121
-
4122
-
4123
-
4124
-
4125
-    // below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4126
-
4127
-
4128
-    /**
4129
-     * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4130
-     * 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
4131
-     * _req_data array.
4132
-     *
4133
-     * @return bool success/fail
4134
-     * @throws EE_Error
4135
-     * @throws InvalidArgumentException
4136
-     * @throws ReflectionException
4137
-     * @throws InvalidDataTypeException
4138
-     * @throws InvalidInterfaceException
4139
-     */
4140
-    protected function _process_resend_registration()
4141
-    {
4142
-        $this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4143
-        do_action(
4144
-            'AHEE__EE_Admin_Page___process_resend_registration',
4145
-            $this->_template_args['success'],
4146
-            $this->_req_data
4147
-        );
4148
-        return $this->_template_args['success'];
4149
-    }
4150
-
4151
-
4152
-    /**
4153
-     * This automatically processes any payment message notifications when manual payment has been applied.
4154
-     *
4155
-     * @param EE_Payment $payment
4156
-     * @return bool success/fail
4157
-     */
4158
-    protected function _process_payment_notification(EE_Payment $payment)
4159
-    {
4160
-        add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4161
-        do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4162
-        $this->_template_args['success'] = apply_filters(
4163
-            'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4164
-            false,
4165
-            $payment
4166
-        );
4167
-        return $this->_template_args['success'];
4168
-    }
2690
+	}
2691
+
2692
+
2693
+	/**
2694
+	 * facade for add_meta_box
2695
+	 *
2696
+	 * @param string  $action        where the metabox get's displayed
2697
+	 * @param string  $title         Title of Metabox (output in metabox header)
2698
+	 * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2699
+	 *                               instead of the one created in here.
2700
+	 * @param array   $callback_args an array of args supplied for the metabox
2701
+	 * @param string  $column        what metabox column
2702
+	 * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2703
+	 * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2704
+	 *                               created but just set our own callback for wp's add_meta_box.
2705
+	 * @throws DomainException
2706
+	 */
2707
+	public function _add_admin_page_meta_box(
2708
+		$action,
2709
+		$title,
2710
+		$callback,
2711
+		$callback_args,
2712
+		$column = 'normal',
2713
+		$priority = 'high',
2714
+		$create_func = true
2715
+	) {
2716
+		do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2717
+		// 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.
2718
+		if (empty($callback_args) && $create_func) {
2719
+			$callback_args = array(
2720
+				'template_path' => $this->_template_path,
2721
+				'template_args' => $this->_template_args,
2722
+			);
2723
+		}
2724
+		// 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)
2725
+		$call_back_func = $create_func
2726
+			? static function ($post, $metabox) {
2727
+				do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2728
+				echo EEH_Template::display_template(
2729
+					$metabox['args']['template_path'],
2730
+					$metabox['args']['template_args'],
2731
+					true
2732
+				);
2733
+			}
2734
+			: $callback;
2735
+		add_meta_box(
2736
+			str_replace('_', '-', $action) . '-mbox',
2737
+			$title,
2738
+			$call_back_func,
2739
+			$this->_wp_page_slug,
2740
+			$column,
2741
+			$priority,
2742
+			$callback_args
2743
+		);
2744
+	}
2745
+
2746
+
2747
+	/**
2748
+	 * generates HTML wrapper for and admin details page that contains metaboxes in columns
2749
+	 *
2750
+	 * @throws DomainException
2751
+	 * @throws EE_Error
2752
+	 * @throws InvalidArgumentException
2753
+	 * @throws InvalidDataTypeException
2754
+	 * @throws InvalidInterfaceException
2755
+	 */
2756
+	public function display_admin_page_with_metabox_columns()
2757
+	{
2758
+		$this->_template_args['post_body_content'] = $this->_template_args['admin_page_content'];
2759
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2760
+			$this->_column_template_path,
2761
+			$this->_template_args,
2762
+			true
2763
+		);
2764
+		// the final wrapper
2765
+		$this->admin_page_wrapper();
2766
+	}
2767
+
2768
+
2769
+	/**
2770
+	 * generates  HTML wrapper for an admin details page
2771
+	 *
2772
+	 * @return void
2773
+	 * @throws DomainException
2774
+	 * @throws EE_Error
2775
+	 * @throws InvalidArgumentException
2776
+	 * @throws InvalidDataTypeException
2777
+	 * @throws InvalidInterfaceException
2778
+	 */
2779
+	public function display_admin_page_with_sidebar()
2780
+	{
2781
+		$this->_display_admin_page(true);
2782
+	}
2783
+
2784
+
2785
+	/**
2786
+	 * generates  HTML wrapper for an admin details page (except no sidebar)
2787
+	 *
2788
+	 * @return void
2789
+	 * @throws DomainException
2790
+	 * @throws EE_Error
2791
+	 * @throws InvalidArgumentException
2792
+	 * @throws InvalidDataTypeException
2793
+	 * @throws InvalidInterfaceException
2794
+	 */
2795
+	public function display_admin_page_with_no_sidebar()
2796
+	{
2797
+		$this->_display_admin_page();
2798
+	}
2799
+
2800
+
2801
+	/**
2802
+	 * generates HTML wrapper for an EE about admin page (no sidebar)
2803
+	 *
2804
+	 * @return void
2805
+	 * @throws DomainException
2806
+	 * @throws EE_Error
2807
+	 * @throws InvalidArgumentException
2808
+	 * @throws InvalidDataTypeException
2809
+	 * @throws InvalidInterfaceException
2810
+	 */
2811
+	public function display_about_admin_page()
2812
+	{
2813
+		$this->_display_admin_page(false, true);
2814
+	}
2815
+
2816
+
2817
+	/**
2818
+	 * display_admin_page
2819
+	 * contains the code for actually displaying an admin page
2820
+	 *
2821
+	 * @param boolean $sidebar true with sidebar, false without
2822
+	 * @param boolean $about   use the about admin wrapper instead of the default.
2823
+	 * @return void
2824
+	 * @throws DomainException
2825
+	 * @throws EE_Error
2826
+	 * @throws InvalidArgumentException
2827
+	 * @throws InvalidDataTypeException
2828
+	 * @throws InvalidInterfaceException
2829
+	 */
2830
+	private function _display_admin_page($sidebar = false, $about = false)
2831
+	{
2832
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2833
+		// custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2834
+		do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2835
+		// set current wp page slug - looks like: event-espresso_page_event_categories
2836
+		// keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2837
+		$this->_template_args['current_page'] = $this->_wp_page_slug;
2838
+		$this->_template_args['admin_page_wrapper_div_id'] = $this->_cpt_route
2839
+			? 'poststuff'
2840
+			: 'espresso-default-admin';
2841
+		$template_path = $sidebar
2842
+			? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2843
+			: EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2844
+		if (defined('DOING_AJAX') && DOING_AJAX) {
2845
+			$template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2846
+		}
2847
+		$template_path = ! empty($this->_column_template_path)
2848
+			? $this->_column_template_path : $template_path;
2849
+		$this->_template_args['post_body_content'] = isset($this->_template_args['admin_page_content'])
2850
+			? $this->_template_args['admin_page_content']
2851
+			: '';
2852
+		$this->_template_args['before_admin_page_content'] = isset($this->_template_args['before_admin_page_content'])
2853
+			? $this->_template_args['before_admin_page_content']
2854
+			: '';
2855
+		$this->_template_args['after_admin_page_content'] = isset($this->_template_args['after_admin_page_content'])
2856
+			? $this->_template_args['after_admin_page_content']
2857
+			: '';
2858
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2859
+			$template_path,
2860
+			$this->_template_args,
2861
+			true
2862
+		);
2863
+		// the final template wrapper
2864
+		$this->admin_page_wrapper($about);
2865
+	}
2866
+
2867
+
2868
+	/**
2869
+	 * This is used to display caf preview pages.
2870
+	 *
2871
+	 * @since 4.3.2
2872
+	 * @param string $utm_campaign_source what is the key used for google analytics link
2873
+	 * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2874
+	 *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2875
+	 * @return void
2876
+	 * @throws DomainException
2877
+	 * @throws EE_Error
2878
+	 * @throws InvalidArgumentException
2879
+	 * @throws InvalidDataTypeException
2880
+	 * @throws InvalidInterfaceException
2881
+	 */
2882
+	public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2883
+	{
2884
+		// let's generate a default preview action button if there isn't one already present.
2885
+		$this->_labels['buttons']['buy_now'] = esc_html__(
2886
+			'Upgrade to Event Espresso 4 Right Now',
2887
+			'event_espresso'
2888
+		);
2889
+		$buy_now_url = add_query_arg(
2890
+			array(
2891
+				'ee_ver'       => 'ee4',
2892
+				'utm_source'   => 'ee4_plugin_admin',
2893
+				'utm_medium'   => 'link',
2894
+				'utm_campaign' => $utm_campaign_source,
2895
+				'utm_content'  => 'buy_now_button',
2896
+			),
2897
+			'http://eventespresso.com/pricing/'
2898
+		);
2899
+		$this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2900
+			? $this->get_action_link_or_button(
2901
+				'',
2902
+				'buy_now',
2903
+				array(),
2904
+				'button-primary button-large',
2905
+				$buy_now_url,
2906
+				true
2907
+			)
2908
+			: $this->_template_args['preview_action_button'];
2909
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2910
+			EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2911
+			$this->_template_args,
2912
+			true
2913
+		);
2914
+		$this->_display_admin_page($display_sidebar);
2915
+	}
2916
+
2917
+
2918
+	/**
2919
+	 * display_admin_list_table_page_with_sidebar
2920
+	 * generates HTML wrapper for an admin_page with list_table
2921
+	 *
2922
+	 * @return void
2923
+	 * @throws DomainException
2924
+	 * @throws EE_Error
2925
+	 * @throws InvalidArgumentException
2926
+	 * @throws InvalidDataTypeException
2927
+	 * @throws InvalidInterfaceException
2928
+	 */
2929
+	public function display_admin_list_table_page_with_sidebar()
2930
+	{
2931
+		$this->_display_admin_list_table_page(true);
2932
+	}
2933
+
2934
+
2935
+	/**
2936
+	 * display_admin_list_table_page_with_no_sidebar
2937
+	 * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2938
+	 *
2939
+	 * @return void
2940
+	 * @throws DomainException
2941
+	 * @throws EE_Error
2942
+	 * @throws InvalidArgumentException
2943
+	 * @throws InvalidDataTypeException
2944
+	 * @throws InvalidInterfaceException
2945
+	 */
2946
+	public function display_admin_list_table_page_with_no_sidebar()
2947
+	{
2948
+		$this->_display_admin_list_table_page();
2949
+	}
2950
+
2951
+
2952
+	/**
2953
+	 * generates html wrapper for an admin_list_table page
2954
+	 *
2955
+	 * @param boolean $sidebar whether to display with sidebar or not.
2956
+	 * @return void
2957
+	 * @throws DomainException
2958
+	 * @throws EE_Error
2959
+	 * @throws InvalidArgumentException
2960
+	 * @throws InvalidDataTypeException
2961
+	 * @throws InvalidInterfaceException
2962
+	 */
2963
+	private function _display_admin_list_table_page($sidebar = false)
2964
+	{
2965
+		// setup search attributes
2966
+		$this->_set_search_attributes();
2967
+		$this->_template_args['current_page'] = $this->_wp_page_slug;
2968
+		$template_path = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2969
+		$this->_template_args['table_url'] = defined('DOING_AJAX')
2970
+			? add_query_arg(array('noheader' => 'true', 'route' => $this->_req_action), $this->_admin_base_url)
2971
+			: add_query_arg(array('route' => $this->_req_action), $this->_admin_base_url);
2972
+		$this->_template_args['list_table'] = $this->_list_table_object;
2973
+		$this->_template_args['current_route'] = $this->_req_action;
2974
+		$this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2975
+		$ajax_sorting_callback = $this->_list_table_object->get_ajax_sorting_callback();
2976
+		if (! empty($ajax_sorting_callback)) {
2977
+			$sortable_list_table_form_fields = wp_nonce_field(
2978
+				$ajax_sorting_callback . '_nonce',
2979
+				$ajax_sorting_callback . '_nonce',
2980
+				false,
2981
+				false
2982
+			);
2983
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2984
+												. $this->page_slug
2985
+												. '" />';
2986
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2987
+												. $ajax_sorting_callback
2988
+												. '" />';
2989
+		} else {
2990
+			$sortable_list_table_form_fields = '';
2991
+		}
2992
+		$this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2993
+		$hidden_form_fields = isset($this->_template_args['list_table_hidden_fields'])
2994
+			? $this->_template_args['list_table_hidden_fields']
2995
+			: '';
2996
+		$nonce_ref = $this->_req_action . '_nonce';
2997
+		$hidden_form_fields .= '<input type="hidden" name="'
2998
+							   . $nonce_ref
2999
+							   . '" value="'
3000
+							   . wp_create_nonce($nonce_ref)
3001
+							   . '">';
3002
+		$this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
3003
+		// display message about search results?
3004
+		$this->_template_args['before_list_table'] .= ! empty($this->_req_data['s'])
3005
+			? '<p class="ee-search-results">' . sprintf(
3006
+				esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
3007
+				trim($this->_req_data['s'], '%')
3008
+			) . '</p>'
3009
+			: '';
3010
+		// filter before_list_table template arg
3011
+		$this->_template_args['before_list_table'] = apply_filters(
3012
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
3013
+			$this->_template_args['before_list_table'],
3014
+			$this->page_slug,
3015
+			$this->_req_data,
3016
+			$this->_req_action
3017
+		);
3018
+		// convert to array and filter again
3019
+		// arrays are easier to inject new items in a specific location,
3020
+		// but would not be backwards compatible, so we have to add a new filter
3021
+		$this->_template_args['before_list_table'] = implode(
3022
+			" \n",
3023
+			(array) apply_filters(
3024
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
3025
+				(array) $this->_template_args['before_list_table'],
3026
+				$this->page_slug,
3027
+				$this->_req_data,
3028
+				$this->_req_action
3029
+			)
3030
+		);
3031
+		// filter after_list_table template arg
3032
+		$this->_template_args['after_list_table'] = apply_filters(
3033
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3034
+			$this->_template_args['after_list_table'],
3035
+			$this->page_slug,
3036
+			$this->_req_data,
3037
+			$this->_req_action
3038
+		);
3039
+		// convert to array and filter again
3040
+		// arrays are easier to inject new items in a specific location,
3041
+		// but would not be backwards compatible, so we have to add a new filter
3042
+		$this->_template_args['after_list_table'] = implode(
3043
+			" \n",
3044
+			(array) apply_filters(
3045
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3046
+				(array) $this->_template_args['after_list_table'],
3047
+				$this->page_slug,
3048
+				$this->_req_data,
3049
+				$this->_req_action
3050
+			)
3051
+		);
3052
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3053
+			$template_path,
3054
+			$this->_template_args,
3055
+			true
3056
+		);
3057
+		// the final template wrapper
3058
+		if ($sidebar) {
3059
+			$this->display_admin_page_with_sidebar();
3060
+		} else {
3061
+			$this->display_admin_page_with_no_sidebar();
3062
+		}
3063
+	}
3064
+
3065
+
3066
+	/**
3067
+	 * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3068
+	 * html string for the legend.
3069
+	 * $items are expected in an array in the following format:
3070
+	 * $legend_items = array(
3071
+	 *        'item_id' => array(
3072
+	 *            'icon' => 'http://url_to_icon_being_described.png',
3073
+	 *            'desc' => esc_html__('localized description of item');
3074
+	 *        )
3075
+	 * );
3076
+	 *
3077
+	 * @param  array $items see above for format of array
3078
+	 * @return string html string of legend
3079
+	 * @throws DomainException
3080
+	 */
3081
+	protected function _display_legend($items)
3082
+	{
3083
+		$this->_template_args['items'] = apply_filters(
3084
+			'FHEE__EE_Admin_Page___display_legend__items',
3085
+			(array) $items,
3086
+			$this
3087
+		);
3088
+		/** @var EventEspresso\core\admin\StatusChangeNotice $status_change_notice */
3089
+		$status_change_notice = $this->loader->getShared('EventEspresso\core\admin\StatusChangeNotice');
3090
+		if (! $status_change_notice->isDismissed()) {
3091
+			$this->_template_args['status_change_notice'] = EEH_Template::display_template(
3092
+				EE_ADMIN_TEMPLATE . 'status_change_notice.template.php',
3093
+				[ 'context' => '__admin-legend', 'page_slug' => $this->page_slug ],
3094
+				true
3095
+			);
3096
+		}
3097
+		return EEH_Template::display_template(
3098
+			EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3099
+			$this->_template_args,
3100
+			true
3101
+		);
3102
+	}
3103
+
3104
+
3105
+	/**
3106
+	 * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3107
+	 * The returned json object is created from an array in the following format:
3108
+	 * array(
3109
+	 *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3110
+	 *  'success' => FALSE, //(default FALSE) - contains any special success message.
3111
+	 *  'notices' => '', // - contains any EE_Error formatted notices
3112
+	 *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3113
+	 *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3114
+	 *  We're also going to include the template args with every package (so js can pick out any specific template args
3115
+	 *  that might be included in here)
3116
+	 * )
3117
+	 * The json object is populated by whatever is set in the $_template_args property.
3118
+	 *
3119
+	 * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3120
+	 *                                 instead of displayed.
3121
+	 * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3122
+	 * @return void
3123
+	 * @throws EE_Error
3124
+	 * @throws InvalidArgumentException
3125
+	 * @throws InvalidDataTypeException
3126
+	 * @throws InvalidInterfaceException
3127
+	 */
3128
+	protected function _return_json($sticky_notices = false, $notices_arguments = array())
3129
+	{
3130
+		// make sure any EE_Error notices have been handled.
3131
+		$this->_process_notices($notices_arguments, true, $sticky_notices);
3132
+		$data = isset($this->_template_args['data']) ? $this->_template_args['data'] : array();
3133
+		unset($this->_template_args['data']);
3134
+		$json = array(
3135
+			'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3136
+			'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3137
+			'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3138
+			'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3139
+			'notices'   => EE_Error::get_notices(),
3140
+			'content'   => isset($this->_template_args['admin_page_content'])
3141
+				? $this->_template_args['admin_page_content'] : '',
3142
+			'data'      => array_merge($data, array('template_args' => $this->_template_args)),
3143
+			'isEEajax'  => true
3144
+			// special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3145
+		);
3146
+		// make sure there are no php errors or headers_sent.  Then we can set correct json header.
3147
+		if (null === error_get_last() || ! headers_sent()) {
3148
+			header('Content-Type: application/json; charset=UTF-8');
3149
+		}
3150
+		echo wp_json_encode($json);
3151
+		exit();
3152
+	}
3153
+
3154
+
3155
+	/**
3156
+	 * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3157
+	 *
3158
+	 * @return void
3159
+	 * @throws EE_Error
3160
+	 * @throws InvalidArgumentException
3161
+	 * @throws InvalidDataTypeException
3162
+	 * @throws InvalidInterfaceException
3163
+	 */
3164
+	public function return_json()
3165
+	{
3166
+		if (defined('DOING_AJAX') && DOING_AJAX) {
3167
+			$this->_return_json();
3168
+		} else {
3169
+			throw new EE_Error(
3170
+				sprintf(
3171
+					esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3172
+					__FUNCTION__
3173
+				)
3174
+			);
3175
+		}
3176
+	}
3177
+
3178
+
3179
+	/**
3180
+	 * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3181
+	 * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3182
+	 *
3183
+	 * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3184
+	 */
3185
+	public function set_hook_object(EE_Admin_Hooks $hook_obj)
3186
+	{
3187
+		$this->_hook_obj = $hook_obj;
3188
+	}
3189
+
3190
+
3191
+	/**
3192
+	 *        generates  HTML wrapper with Tabbed nav for an admin page
3193
+	 *
3194
+	 * @param boolean $about whether to use the special about page wrapper or default.
3195
+	 * @return void
3196
+	 * @throws DomainException
3197
+	 * @throws EE_Error
3198
+	 * @throws InvalidArgumentException
3199
+	 * @throws InvalidDataTypeException
3200
+	 * @throws InvalidInterfaceException
3201
+	 */
3202
+	public function admin_page_wrapper($about = false)
3203
+	{
3204
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3205
+		$this->_nav_tabs = $this->_get_main_nav_tabs();
3206
+		$this->_template_args['nav_tabs'] = $this->_nav_tabs;
3207
+		$this->_template_args['admin_page_title'] = $this->_admin_page_title;
3208
+		$this->_template_args['before_admin_page_content'] = apply_filters(
3209
+			"FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3210
+			isset($this->_template_args['before_admin_page_content'])
3211
+				? $this->_template_args['before_admin_page_content']
3212
+				: ''
3213
+		);
3214
+		$this->_template_args['after_admin_page_content'] = apply_filters(
3215
+			"FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3216
+			isset($this->_template_args['after_admin_page_content'])
3217
+				? $this->_template_args['after_admin_page_content']
3218
+				: ''
3219
+		);
3220
+		$this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3221
+		// load settings page wrapper template
3222
+		$template_path = ! defined('DOING_AJAX')
3223
+			? EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php'
3224
+			: EE_ADMIN_TEMPLATE
3225
+			  . 'admin_wrapper_ajax.template.php';
3226
+		// about page?
3227
+		$template_path = $about
3228
+			? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3229
+			: $template_path;
3230
+		if (defined('DOING_AJAX')) {
3231
+			$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3232
+				$template_path,
3233
+				$this->_template_args,
3234
+				true
3235
+			);
3236
+			$this->_return_json();
3237
+		} else {
3238
+			EEH_Template::display_template($template_path, $this->_template_args);
3239
+		}
3240
+	}
3241
+
3242
+
3243
+	/**
3244
+	 * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3245
+	 *
3246
+	 * @return string html
3247
+	 * @throws EE_Error
3248
+	 */
3249
+	protected function _get_main_nav_tabs()
3250
+	{
3251
+		// let's generate the html using the EEH_Tabbed_Content helper.
3252
+		// We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3253
+		// (rather than setting in the page_routes array)
3254
+		return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs);
3255
+	}
3256
+
3257
+
3258
+	/**
3259
+	 *        sort nav tabs
3260
+	 *
3261
+	 * @param $a
3262
+	 * @param $b
3263
+	 * @return int
3264
+	 */
3265
+	private function _sort_nav_tabs($a, $b)
3266
+	{
3267
+		if ($a['order'] === $b['order']) {
3268
+			return 0;
3269
+		}
3270
+		return ($a['order'] < $b['order']) ? -1 : 1;
3271
+	}
3272
+
3273
+
3274
+	/**
3275
+	 *    generates HTML for the forms used on admin pages
3276
+	 *
3277
+	 * @param    array $input_vars - array of input field details
3278
+	 * @param string   $generator  (options are 'string' or 'array', basically use this to indicate which generator to
3279
+	 *                             use)
3280
+	 * @param bool     $id
3281
+	 * @return string
3282
+	 * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3283
+	 * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3284
+	 */
3285
+	protected function _generate_admin_form_fields($input_vars = array(), $generator = 'string', $id = false)
3286
+	{
3287
+		return $generator === 'string'
3288
+			? EEH_Form_Fields::get_form_fields($input_vars, $id)
3289
+			: EEH_Form_Fields::get_form_fields_array($input_vars);
3290
+	}
3291
+
3292
+
3293
+	/**
3294
+	 * generates the "Save" and "Save & Close" buttons for edit forms
3295
+	 *
3296
+	 * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3297
+	 *                                   Close" button.
3298
+	 * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3299
+	 *                                   'Save', [1] => 'save & close')
3300
+	 * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3301
+	 *                                   via the "name" value in the button).  We can also use this to just dump
3302
+	 *                                   default actions by submitting some other value.
3303
+	 * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3304
+	 *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3305
+	 *                                   close (normal form handling).
3306
+	 */
3307
+	protected function _set_save_buttons($both = true, $text = array(), $actions = array(), $referrer = null)
3308
+	{
3309
+		// make sure $text and $actions are in an array
3310
+		$text = (array) $text;
3311
+		$actions = (array) $actions;
3312
+		$referrer_url = empty($referrer)
3313
+			? '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3314
+			  . $_SERVER['REQUEST_URI']
3315
+			  . '" />'
3316
+			: '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3317
+			  . $referrer
3318
+			  . '" />';
3319
+		$button_text = ! empty($text)
3320
+			? $text
3321
+			: array(
3322
+				esc_html__('Save', 'event_espresso'),
3323
+				esc_html__('Save and Close', 'event_espresso'),
3324
+			);
3325
+		$default_names = array('save', 'save_and_close');
3326
+		// add in a hidden index for the current page (so save and close redirects properly)
3327
+		$this->_template_args['save_buttons'] = $referrer_url;
3328
+		foreach ($button_text as $key => $button) {
3329
+			$ref = $default_names[ $key ];
3330
+			$this->_template_args['save_buttons'] .= '<input type="submit" class="button-primary '
3331
+													 . $ref
3332
+													 . '" value="'
3333
+													 . $button
3334
+													 . '" name="'
3335
+													 . (! empty($actions) ? $actions[ $key ] : $ref)
3336
+													 . '" id="'
3337
+													 . $this->_current_view . '_' . $ref
3338
+													 . '" />';
3339
+			if (! $both) {
3340
+				break;
3341
+			}
3342
+		}
3343
+	}
3344
+
3345
+
3346
+	/**
3347
+	 * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3348
+	 *
3349
+	 * @see   $this->_set_add_edit_form_tags() for details on params
3350
+	 * @since 4.6.0
3351
+	 * @param string $route
3352
+	 * @param array  $additional_hidden_fields
3353
+	 */
3354
+	public function set_add_edit_form_tags($route = '', $additional_hidden_fields = array())
3355
+	{
3356
+		$this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3357
+	}
3358
+
3359
+
3360
+	/**
3361
+	 * set form open and close tags on add/edit pages.
3362
+	 *
3363
+	 * @param string $route                    the route you want the form to direct to
3364
+	 * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3365
+	 * @return void
3366
+	 */
3367
+	protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = array())
3368
+	{
3369
+		if (empty($route)) {
3370
+			$user_msg = esc_html__(
3371
+				'An error occurred. No action was set for this page\'s form.',
3372
+				'event_espresso'
3373
+			);
3374
+			$dev_msg = $user_msg . "\n"
3375
+					   . sprintf(
3376
+						   esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3377
+						   __FUNCTION__,
3378
+						   EE_Admin_Page::class
3379
+					   );
3380
+			EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3381
+		}
3382
+		// open form
3383
+		$this->_template_args['before_admin_page_content'] = '<form name="form" method="post" action="'
3384
+															 . $this->_admin_base_url
3385
+															 . '" id="'
3386
+															 . $route
3387
+															 . '_event_form" >';
3388
+		// add nonce
3389
+		$nonce = wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3390
+		$this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3391
+		// add REQUIRED form action
3392
+		$hidden_fields = array(
3393
+			'action' => array('type' => 'hidden', 'value' => $route),
3394
+		);
3395
+		// merge arrays
3396
+		$hidden_fields = is_array($additional_hidden_fields)
3397
+			? array_merge($hidden_fields, $additional_hidden_fields)
3398
+			: $hidden_fields;
3399
+		// generate form fields
3400
+		$form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3401
+		// add fields to form
3402
+		foreach ((array) $form_fields as $field_name => $form_field) {
3403
+			$this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3404
+		}
3405
+		// close form
3406
+		$this->_template_args['after_admin_page_content'] = '</form>';
3407
+	}
3408
+
3409
+
3410
+	/**
3411
+	 * Public Wrapper for _redirect_after_action() method since its
3412
+	 * discovered it would be useful for external code to have access.
3413
+	 *
3414
+	 * @param bool   $success
3415
+	 * @param string $what
3416
+	 * @param string $action_desc
3417
+	 * @param array  $query_args
3418
+	 * @param bool   $override_overwrite
3419
+	 * @throws EE_Error
3420
+	 * @throws InvalidArgumentException
3421
+	 * @throws InvalidDataTypeException
3422
+	 * @throws InvalidInterfaceException
3423
+	 * @see   EE_Admin_Page::_redirect_after_action() for params.
3424
+	 * @since 4.5.0
3425
+	 */
3426
+	public function redirect_after_action(
3427
+		$success = false,
3428
+		$what = 'item',
3429
+		$action_desc = 'processed',
3430
+		$query_args = array(),
3431
+		$override_overwrite = false
3432
+	) {
3433
+		$this->_redirect_after_action(
3434
+			$success,
3435
+			$what,
3436
+			$action_desc,
3437
+			$query_args,
3438
+			$override_overwrite
3439
+		);
3440
+	}
3441
+
3442
+
3443
+	/**
3444
+	 * Helper method for merging existing request data with the returned redirect url.
3445
+	 *
3446
+	 * This is typically used for redirects after an action so that if the original view was a filtered view those
3447
+	 * filters are still applied.
3448
+	 *
3449
+	 * @param array $new_route_data
3450
+	 * @return array
3451
+	 */
3452
+	protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3453
+	{
3454
+		foreach ($this->_req_data as $ref => $value) {
3455
+			// unset nonces
3456
+			if (strpos($ref, 'nonce') !== false) {
3457
+				unset($this->_req_data[ $ref ]);
3458
+				continue;
3459
+			}
3460
+			// urlencode values.
3461
+			$value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3462
+			$this->_req_data[ $ref ] = $value;
3463
+		}
3464
+		return array_merge($this->_req_data, $new_route_data);
3465
+	}
3466
+
3467
+
3468
+	/**
3469
+	 *    _redirect_after_action
3470
+	 *
3471
+	 * @param int    $success            - whether success was for two or more records, or just one, or none
3472
+	 * @param string $what               - what the action was performed on
3473
+	 * @param string $action_desc        - what was done ie: updated, deleted, etc
3474
+	 * @param array  $query_args         - an array of query_args to be added to the URL to redirect to after the admin
3475
+	 *                                   action is completed
3476
+	 * @param BOOL   $override_overwrite by default all EE_Error::success messages are overwritten, this allows you to
3477
+	 *                                   override this so that they show.
3478
+	 * @return void
3479
+	 * @throws EE_Error
3480
+	 * @throws InvalidArgumentException
3481
+	 * @throws InvalidDataTypeException
3482
+	 * @throws InvalidInterfaceException
3483
+	 */
3484
+	protected function _redirect_after_action(
3485
+		$success = 0,
3486
+		$what = 'item',
3487
+		$action_desc = 'processed',
3488
+		$query_args = array(),
3489
+		$override_overwrite = false
3490
+	) {
3491
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3492
+		// class name for actions/filters.
3493
+		$classname = get_class($this);
3494
+		// set redirect url.
3495
+		// Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3496
+		// otherwise we go with whatever is set as the _admin_base_url
3497
+		$redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3498
+		$notices = EE_Error::get_notices(false);
3499
+		// overwrite default success messages //BUT ONLY if overwrite not overridden
3500
+		if (! $override_overwrite || ! empty($notices['errors'])) {
3501
+			EE_Error::overwrite_success();
3502
+		}
3503
+		if (! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3504
+			// how many records affected ? more than one record ? or just one ?
3505
+			if ($success > 1) {
3506
+				// set plural msg
3507
+				EE_Error::add_success(
3508
+					sprintf(
3509
+						esc_html__('The "%s" have been successfully %s.', 'event_espresso'),
3510
+						$what,
3511
+						$action_desc
3512
+					),
3513
+					__FILE__,
3514
+					__FUNCTION__,
3515
+					__LINE__
3516
+				);
3517
+			} elseif ($success === 1) {
3518
+				// set singular msg
3519
+				EE_Error::add_success(
3520
+					sprintf(
3521
+						esc_html__('The "%s" has been successfully %s.', 'event_espresso'),
3522
+						$what,
3523
+						$action_desc
3524
+					),
3525
+					__FILE__,
3526
+					__FUNCTION__,
3527
+					__LINE__
3528
+				);
3529
+			}
3530
+		}
3531
+		// check that $query_args isn't something crazy
3532
+		if (! is_array($query_args)) {
3533
+			$query_args = array();
3534
+		}
3535
+		/**
3536
+		 * Allow injecting actions before the query_args are modified for possible different
3537
+		 * redirections on save and close actions
3538
+		 *
3539
+		 * @since 4.2.0
3540
+		 * @param array $query_args       The original query_args array coming into the
3541
+		 *                                method.
3542
+		 */
3543
+		do_action(
3544
+			"AHEE__{$classname}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3545
+			$query_args
3546
+		);
3547
+		// calculate where we're going (if we have a "save and close" button pushed)
3548
+		if (isset($this->_req_data['save_and_close'], $this->_req_data['save_and_close_referrer'])) {
3549
+			// even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3550
+			$parsed_url = parse_url($this->_req_data['save_and_close_referrer']);
3551
+			// regenerate query args array from referrer URL
3552
+			parse_str($parsed_url['query'], $query_args);
3553
+			// correct page and action will be in the query args now
3554
+			$redirect_url = admin_url('admin.php');
3555
+		}
3556
+		// merge any default query_args set in _default_route_query_args property
3557
+		if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3558
+			$args_to_merge = array();
3559
+			foreach ($this->_default_route_query_args as $query_param => $query_value) {
3560
+				// is there a wp_referer array in our _default_route_query_args property?
3561
+				if ($query_param === 'wp_referer') {
3562
+					$query_value = (array) $query_value;
3563
+					foreach ($query_value as $reference => $value) {
3564
+						if (strpos($reference, 'nonce') !== false) {
3565
+							continue;
3566
+						}
3567
+						// finally we will override any arguments in the referer with
3568
+						// what might be set on the _default_route_query_args array.
3569
+						if (isset($this->_default_route_query_args[ $reference ])) {
3570
+							$args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3571
+						} else {
3572
+							$args_to_merge[ $reference ] = urlencode($value);
3573
+						}
3574
+					}
3575
+					continue;
3576
+				}
3577
+				$args_to_merge[ $query_param ] = $query_value;
3578
+			}
3579
+			// now let's merge these arguments but override with what was specifically sent in to the
3580
+			// redirect.
3581
+			$query_args = array_merge($args_to_merge, $query_args);
3582
+		}
3583
+		$this->_process_notices($query_args);
3584
+		// generate redirect url
3585
+		// if redirecting to anything other than the main page, add a nonce
3586
+		if (isset($query_args['action'])) {
3587
+			// manually generate wp_nonce and merge that with the query vars
3588
+			// becuz the wp_nonce_url function wrecks havoc on some vars
3589
+			$query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3590
+		}
3591
+		// we're adding some hooks and filters in here for processing any things just before redirects
3592
+		// (example: an admin page has done an insert or update and we want to run something after that).
3593
+		do_action('AHEE_redirect_' . $classname . $this->_req_action, $query_args);
3594
+		$redirect_url = apply_filters(
3595
+			'FHEE_redirect_' . $classname . $this->_req_action,
3596
+			EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3597
+			$query_args
3598
+		);
3599
+		// check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3600
+		if (defined('DOING_AJAX')) {
3601
+			$default_data = array(
3602
+				'close'        => true,
3603
+				'redirect_url' => $redirect_url,
3604
+				'where'        => 'main',
3605
+				'what'         => 'append',
3606
+			);
3607
+			$this->_template_args['success'] = $success;
3608
+			$this->_template_args['data'] = ! empty($this->_template_args['data']) ? array_merge(
3609
+				$default_data,
3610
+				$this->_template_args['data']
3611
+			) : $default_data;
3612
+			$this->_return_json();
3613
+		}
3614
+		wp_safe_redirect($redirect_url);
3615
+		exit();
3616
+	}
3617
+
3618
+
3619
+	/**
3620
+	 * process any notices before redirecting (or returning ajax request)
3621
+	 * This method sets the $this->_template_args['notices'] attribute;
3622
+	 *
3623
+	 * @param array $query_args         any query args that need to be used for notice transient ('action')
3624
+	 * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3625
+	 *                                  page_routes haven't been defined yet.
3626
+	 * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3627
+	 *                                  still save a transient for the notice.
3628
+	 * @return void
3629
+	 * @throws EE_Error
3630
+	 * @throws InvalidArgumentException
3631
+	 * @throws InvalidDataTypeException
3632
+	 * @throws InvalidInterfaceException
3633
+	 */
3634
+	protected function _process_notices($query_args = array(), $skip_route_verify = false, $sticky_notices = true)
3635
+	{
3636
+		// first let's set individual error properties if doing_ajax and the properties aren't already set.
3637
+		if (defined('DOING_AJAX') && DOING_AJAX) {
3638
+			$notices = EE_Error::get_notices(false);
3639
+			if (empty($this->_template_args['success'])) {
3640
+				$this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3641
+			}
3642
+			if (empty($this->_template_args['errors'])) {
3643
+				$this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3644
+			}
3645
+			if (empty($this->_template_args['attention'])) {
3646
+				$this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3647
+			}
3648
+		}
3649
+		$this->_template_args['notices'] = EE_Error::get_notices();
3650
+		// IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3651
+		if (! defined('DOING_AJAX') || $sticky_notices) {
3652
+			$route = isset($query_args['action']) ? $query_args['action'] : 'default';
3653
+			$this->_add_transient(
3654
+				$route,
3655
+				$this->_template_args['notices'],
3656
+				true,
3657
+				$skip_route_verify
3658
+			);
3659
+		}
3660
+	}
3661
+
3662
+
3663
+	/**
3664
+	 * get_action_link_or_button
3665
+	 * returns the button html for adding, editing, or deleting an item (depending on given type)
3666
+	 *
3667
+	 * @param string $action        use this to indicate which action the url is generated with.
3668
+	 * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3669
+	 *                              property.
3670
+	 * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3671
+	 * @param string $class         Use this to give the class for the button. Defaults to 'button-primary'
3672
+	 * @param string $base_url      If this is not provided
3673
+	 *                              the _admin_base_url will be used as the default for the button base_url.
3674
+	 *                              Otherwise this value will be used.
3675
+	 * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3676
+	 * @return string
3677
+	 * @throws InvalidArgumentException
3678
+	 * @throws InvalidInterfaceException
3679
+	 * @throws InvalidDataTypeException
3680
+	 * @throws EE_Error
3681
+	 */
3682
+	public function get_action_link_or_button(
3683
+		$action,
3684
+		$type = 'add',
3685
+		$extra_request = array(),
3686
+		$class = 'button-primary',
3687
+		$base_url = '',
3688
+		$exclude_nonce = false
3689
+	) {
3690
+		// first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3691
+		if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3692
+			throw new EE_Error(
3693
+				sprintf(
3694
+					esc_html__(
3695
+						'There is no page route for given action for the button.  This action was given: %s',
3696
+						'event_espresso'
3697
+					),
3698
+					$action
3699
+				)
3700
+			);
3701
+		}
3702
+		if (! isset($this->_labels['buttons'][ $type ])) {
3703
+			throw new EE_Error(
3704
+				sprintf(
3705
+					__(
3706
+						'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3707
+						'event_espresso'
3708
+					),
3709
+					$type
3710
+				)
3711
+			);
3712
+		}
3713
+		// finally check user access for this button.
3714
+		$has_access = $this->check_user_access($action, true);
3715
+		if (! $has_access) {
3716
+			return '';
3717
+		}
3718
+		$_base_url = ! $base_url ? $this->_admin_base_url : $base_url;
3719
+		$query_args = array(
3720
+			'action' => $action,
3721
+		);
3722
+		// merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3723
+		if (! empty($extra_request)) {
3724
+			$query_args = array_merge($extra_request, $query_args);
3725
+		}
3726
+		$url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3727
+		return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3728
+	}
3729
+
3730
+
3731
+	/**
3732
+	 * _per_page_screen_option
3733
+	 * Utility function for adding in a per_page_option in the screen_options_dropdown.
3734
+	 *
3735
+	 * @return void
3736
+	 * @throws InvalidArgumentException
3737
+	 * @throws InvalidInterfaceException
3738
+	 * @throws InvalidDataTypeException
3739
+	 */
3740
+	protected function _per_page_screen_option()
3741
+	{
3742
+		$option = 'per_page';
3743
+		$args = array(
3744
+			'label'   => apply_filters(
3745
+				'FHEE__EE_Admin_Page___per_page_screen_options___label',
3746
+				$this->_admin_page_title,
3747
+				$this
3748
+			),
3749
+			'default' => (int) apply_filters(
3750
+				'FHEE__EE_Admin_Page___per_page_screen_options__default',
3751
+				20
3752
+			),
3753
+			'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3754
+		);
3755
+		// ONLY add the screen option if the user has access to it.
3756
+		if ($this->check_user_access($this->_current_view, true)) {
3757
+			add_screen_option($option, $args);
3758
+		}
3759
+	}
3760
+
3761
+
3762
+	/**
3763
+	 * set_per_page_screen_option
3764
+	 * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3765
+	 * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3766
+	 * admin_menu.
3767
+	 *
3768
+	 * @return void
3769
+	 */
3770
+	private function _set_per_page_screen_options()
3771
+	{
3772
+		if (isset($_POST['wp_screen_options']) && is_array($_POST['wp_screen_options'])) {
3773
+			check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3774
+			if (! $user = wp_get_current_user()) {
3775
+				return;
3776
+			}
3777
+			$option = $_POST['wp_screen_options']['option'];
3778
+			$value = $_POST['wp_screen_options']['value'];
3779
+			if ($option !== sanitize_key($option)) {
3780
+				return;
3781
+			}
3782
+			$map_option = $option;
3783
+			$option = str_replace('-', '_', $option);
3784
+			switch ($map_option) {
3785
+				case $this->_current_page . '_' . $this->_current_view . '_per_page':
3786
+					$value = (int) $value;
3787
+					$max_value = apply_filters(
3788
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3789
+						999,
3790
+						$this->_current_page,
3791
+						$this->_current_view
3792
+					);
3793
+					if ($value < 1) {
3794
+						return;
3795
+					}
3796
+					$value = min($value, $max_value);
3797
+					break;
3798
+				default:
3799
+					$value = apply_filters(
3800
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3801
+						false,
3802
+						$option,
3803
+						$value
3804
+					);
3805
+					if (false === $value) {
3806
+						return;
3807
+					}
3808
+					break;
3809
+			}
3810
+			update_user_meta($user->ID, $option, $value);
3811
+			wp_safe_redirect(remove_query_arg(array('pagenum', 'apage', 'paged'), wp_get_referer()));
3812
+			exit;
3813
+		}
3814
+	}
3815
+
3816
+
3817
+	/**
3818
+	 * This just allows for setting the $_template_args property if it needs to be set outside the object
3819
+	 *
3820
+	 * @param array $data array that will be assigned to template args.
3821
+	 */
3822
+	public function set_template_args($data)
3823
+	{
3824
+		$this->_template_args = array_merge($this->_template_args, (array) $data);
3825
+	}
3826
+
3827
+
3828
+	/**
3829
+	 * This makes available the WP transient system for temporarily moving data between routes
3830
+	 *
3831
+	 * @param string $route             the route that should receive the transient
3832
+	 * @param array  $data              the data that gets sent
3833
+	 * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3834
+	 *                                  normal route transient.
3835
+	 * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3836
+	 *                                  when we are adding a transient before page_routes have been defined.
3837
+	 * @return void
3838
+	 * @throws EE_Error
3839
+	 */
3840
+	protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3841
+	{
3842
+		$user_id = get_current_user_id();
3843
+		if (! $skip_route_verify) {
3844
+			$this->_verify_route($route);
3845
+		}
3846
+		// now let's set the string for what kind of transient we're setting
3847
+		$transient = $notices
3848
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3849
+			: 'rte_tx_' . $route . '_' . $user_id;
3850
+		$data = $notices ? array('notices' => $data) : $data;
3851
+		// is there already a transient for this route?  If there is then let's ADD to that transient
3852
+		$existing = is_multisite() && is_network_admin()
3853
+			? get_site_transient($transient)
3854
+			: get_transient($transient);
3855
+		if ($existing) {
3856
+			$data = array_merge((array) $data, (array) $existing);
3857
+		}
3858
+		if (is_multisite() && is_network_admin()) {
3859
+			set_site_transient($transient, $data, 8);
3860
+		} else {
3861
+			set_transient($transient, $data, 8);
3862
+		}
3863
+	}
3864
+
3865
+
3866
+	/**
3867
+	 * this retrieves the temporary transient that has been set for moving data between routes.
3868
+	 *
3869
+	 * @param bool   $notices true we get notices transient. False we just return normal route transient
3870
+	 * @param string $route
3871
+	 * @return mixed data
3872
+	 */
3873
+	protected function _get_transient($notices = false, $route = '')
3874
+	{
3875
+		$user_id = get_current_user_id();
3876
+		$route = ! $route ? $this->_req_action : $route;
3877
+		$transient = $notices
3878
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3879
+			: 'rte_tx_' . $route . '_' . $user_id;
3880
+		$data = is_multisite() && is_network_admin()
3881
+			? get_site_transient($transient)
3882
+			: get_transient($transient);
3883
+		// delete transient after retrieval (just in case it hasn't expired);
3884
+		if (is_multisite() && is_network_admin()) {
3885
+			delete_site_transient($transient);
3886
+		} else {
3887
+			delete_transient($transient);
3888
+		}
3889
+		return $notices && isset($data['notices']) ? $data['notices'] : $data;
3890
+	}
3891
+
3892
+
3893
+	/**
3894
+	 * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3895
+	 * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3896
+	 * default route callback on the EE_Admin page you want it run.)
3897
+	 *
3898
+	 * @return void
3899
+	 */
3900
+	protected function _transient_garbage_collection()
3901
+	{
3902
+		global $wpdb;
3903
+		// retrieve all existing transients
3904
+		$query = "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3905
+		if ($results = $wpdb->get_results($query)) {
3906
+			foreach ($results as $result) {
3907
+				$transient = str_replace('_transient_', '', $result->option_name);
3908
+				get_transient($transient);
3909
+				if (is_multisite() && is_network_admin()) {
3910
+					get_site_transient($transient);
3911
+				}
3912
+			}
3913
+		}
3914
+	}
3915
+
3916
+
3917
+	/**
3918
+	 * get_view
3919
+	 *
3920
+	 * @return string content of _view property
3921
+	 */
3922
+	public function get_view()
3923
+	{
3924
+		return $this->_view;
3925
+	}
3926
+
3927
+
3928
+	/**
3929
+	 * getter for the protected $_views property
3930
+	 *
3931
+	 * @return array
3932
+	 */
3933
+	public function get_views()
3934
+	{
3935
+		return $this->_views;
3936
+	}
3937
+
3938
+
3939
+	/**
3940
+	 * get_current_page
3941
+	 *
3942
+	 * @return string _current_page property value
3943
+	 */
3944
+	public function get_current_page()
3945
+	{
3946
+		return $this->_current_page;
3947
+	}
3948
+
3949
+
3950
+	/**
3951
+	 * get_current_view
3952
+	 *
3953
+	 * @return string _current_view property value
3954
+	 */
3955
+	public function get_current_view()
3956
+	{
3957
+		return $this->_current_view;
3958
+	}
3959
+
3960
+
3961
+	/**
3962
+	 * get_current_screen
3963
+	 *
3964
+	 * @return object The current WP_Screen object
3965
+	 */
3966
+	public function get_current_screen()
3967
+	{
3968
+		return $this->_current_screen;
3969
+	}
3970
+
3971
+
3972
+	/**
3973
+	 * get_current_page_view_url
3974
+	 *
3975
+	 * @return string This returns the url for the current_page_view.
3976
+	 */
3977
+	public function get_current_page_view_url()
3978
+	{
3979
+		return $this->_current_page_view_url;
3980
+	}
3981
+
3982
+
3983
+	/**
3984
+	 * just returns the _req_data property
3985
+	 *
3986
+	 * @return array
3987
+	 */
3988
+	public function get_request_data()
3989
+	{
3990
+		return $this->_req_data;
3991
+	}
3992
+
3993
+
3994
+	/**
3995
+	 * returns the _req_data protected property
3996
+	 *
3997
+	 * @return string
3998
+	 */
3999
+	public function get_req_action()
4000
+	{
4001
+		return $this->_req_action;
4002
+	}
4003
+
4004
+
4005
+	/**
4006
+	 * @return bool  value of $_is_caf property
4007
+	 */
4008
+	public function is_caf()
4009
+	{
4010
+		return $this->_is_caf;
4011
+	}
4012
+
4013
+
4014
+	/**
4015
+	 * @return mixed
4016
+	 */
4017
+	public function default_espresso_metaboxes()
4018
+	{
4019
+		return $this->_default_espresso_metaboxes;
4020
+	}
4021
+
4022
+
4023
+	/**
4024
+	 * @return mixed
4025
+	 */
4026
+	public function admin_base_url()
4027
+	{
4028
+		return $this->_admin_base_url;
4029
+	}
4030
+
4031
+
4032
+	/**
4033
+	 * @return mixed
4034
+	 */
4035
+	public function wp_page_slug()
4036
+	{
4037
+		return $this->_wp_page_slug;
4038
+	}
4039
+
4040
+
4041
+	/**
4042
+	 * updates  espresso configuration settings
4043
+	 *
4044
+	 * @param string                   $tab
4045
+	 * @param EE_Config_Base|EE_Config $config
4046
+	 * @param string                   $file file where error occurred
4047
+	 * @param string                   $func function  where error occurred
4048
+	 * @param string                   $line line no where error occurred
4049
+	 * @return boolean
4050
+	 */
4051
+	protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
4052
+	{
4053
+		// remove any options that are NOT going to be saved with the config settings.
4054
+		if (isset($config->core->ee_ueip_optin)) {
4055
+			// TODO: remove the following two lines and make sure values are migrated from 3.1
4056
+			update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4057
+			update_option('ee_ueip_has_notified', true);
4058
+		}
4059
+		// and save it (note we're also doing the network save here)
4060
+		$net_saved = is_main_site() ? EE_Network_Config::instance()->update_config(false, false) : true;
4061
+		$config_saved = EE_Config::instance()->update_espresso_config(false, false);
4062
+		if ($config_saved && $net_saved) {
4063
+			EE_Error::add_success(sprintf(__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4064
+			return true;
4065
+		}
4066
+		EE_Error::add_error(sprintf(__('The "%s" were not updated.', 'event_espresso'), $tab), $file, $func, $line);
4067
+		return false;
4068
+	}
4069
+
4070
+
4071
+	/**
4072
+	 * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4073
+	 *
4074
+	 * @return array
4075
+	 */
4076
+	public function get_yes_no_values()
4077
+	{
4078
+		return $this->_yes_no_values;
4079
+	}
4080
+
4081
+
4082
+	/**
4083
+	 * @return string
4084
+	 * @throws ReflectionException
4085
+	 * @since $VID:$
4086
+	 */
4087
+	protected function _get_dir()
4088
+	{
4089
+		$reflector = new ReflectionClass(get_class($this));
4090
+		return dirname($reflector->getFileName());
4091
+	}
4092
+
4093
+
4094
+	/**
4095
+	 * A helper for getting a "next link".
4096
+	 *
4097
+	 * @param string $url   The url to link to
4098
+	 * @param string $class The class to use.
4099
+	 * @return string
4100
+	 */
4101
+	protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4102
+	{
4103
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4104
+	}
4105
+
4106
+
4107
+	/**
4108
+	 * A helper for getting a "previous link".
4109
+	 *
4110
+	 * @param string $url   The url to link to
4111
+	 * @param string $class The class to use.
4112
+	 * @return string
4113
+	 */
4114
+	protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4115
+	{
4116
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4117
+	}
4118
+
4119
+
4120
+
4121
+
4122
+
4123
+
4124
+
4125
+	// below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4126
+
4127
+
4128
+	/**
4129
+	 * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4130
+	 * 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
4131
+	 * _req_data array.
4132
+	 *
4133
+	 * @return bool success/fail
4134
+	 * @throws EE_Error
4135
+	 * @throws InvalidArgumentException
4136
+	 * @throws ReflectionException
4137
+	 * @throws InvalidDataTypeException
4138
+	 * @throws InvalidInterfaceException
4139
+	 */
4140
+	protected function _process_resend_registration()
4141
+	{
4142
+		$this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4143
+		do_action(
4144
+			'AHEE__EE_Admin_Page___process_resend_registration',
4145
+			$this->_template_args['success'],
4146
+			$this->_req_data
4147
+		);
4148
+		return $this->_template_args['success'];
4149
+	}
4150
+
4151
+
4152
+	/**
4153
+	 * This automatically processes any payment message notifications when manual payment has been applied.
4154
+	 *
4155
+	 * @param EE_Payment $payment
4156
+	 * @return bool success/fail
4157
+	 */
4158
+	protected function _process_payment_notification(EE_Payment $payment)
4159
+	{
4160
+		add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4161
+		do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4162
+		$this->_template_args['success'] = apply_filters(
4163
+			'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4164
+			false,
4165
+			$payment
4166
+		);
4167
+		return $this->_template_args['success'];
4168
+	}
4169 4169
 }
Please login to merge, or discard this patch.
languages/event_espresso-translations-js.php 1 patch
Spacing   +598 added lines, -598 removed lines patch added patch discarded remove patch
@@ -2,226 +2,226 @@  discard block
 block discarded – undo
2 2
 /* THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. */
3 3
 $generated_i18n_strings = array(
4 4
 	// Reference: packages/ui-components/src/Pagination/constants.ts:6
5
-	__( '2', 'event_espresso' ),
5
+	__('2', 'event_espresso'),
6 6
 
7 7
 	// Reference: packages/ui-components/src/Pagination/constants.ts:7
8
-	__( '6', 'event_espresso' ),
8
+	__('6', 'event_espresso'),
9 9
 
10 10
 	// Reference: packages/ui-components/src/Pagination/constants.ts:8
11
-	__( '12', 'event_espresso' ),
11
+	__('12', 'event_espresso'),
12 12
 
13 13
 	// Reference: packages/ui-components/src/Pagination/constants.ts:9
14
-	__( '24', 'event_espresso' ),
14
+	__('24', 'event_espresso'),
15 15
 
16 16
 	// Reference: packages/ui-components/src/Pagination/constants.ts:10
17
-	__( '48', 'event_espresso' ),
17
+	__('48', 'event_espresso'),
18 18
 
19 19
 	// Reference: domains/core/admin/blocks/src/components/AvatarImage.tsx:27
20
-	__( 'contact avatar', 'event_espresso' ),
20
+	__('contact avatar', 'event_espresso'),
21 21
 
22 22
 	// Reference: domains/core/admin/blocks/src/components/OrderByControl.tsx:12
23
-	__( 'Order by', 'event_espresso' ),
23
+	__('Order by', 'event_espresso'),
24 24
 
25 25
 	// Reference: domains/core/admin/blocks/src/components/RegStatusControl.tsx:17
26 26
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectStatus.tsx:13
27
-	__( 'Select Registration Status', 'event_espresso' ),
27
+	__('Select Registration Status', 'event_espresso'),
28 28
 
29 29
 	// Reference: domains/core/admin/blocks/src/components/SortOrderControl.tsx:14
30
-	__( 'Ascending', 'event_espresso' ),
30
+	__('Ascending', 'event_espresso'),
31 31
 
32 32
 	// Reference: domains/core/admin/blocks/src/components/SortOrderControl.tsx:18
33
-	__( 'Descending', 'event_espresso' ),
33
+	__('Descending', 'event_espresso'),
34 34
 
35 35
 	// Reference: domains/core/admin/blocks/src/components/SortOrderControl.tsx:24
36
-	__( 'Sort order:', 'event_espresso' ),
36
+	__('Sort order:', 'event_espresso'),
37 37
 
38 38
 	// Reference: domains/core/admin/blocks/src/event-attendees/AttendeesDisplay.tsx:41
39
-	__( 'There was some error fetching attendees list', 'event_espresso' ),
39
+	__('There was some error fetching attendees list', 'event_espresso'),
40 40
 
41 41
 	// Reference: domains/core/admin/blocks/src/event-attendees/AttendeesDisplay.tsx:47
42
-	__( 'To get started, select what event you want to show attendees from in the block settings.', 'event_espresso' ),
42
+	__('To get started, select what event you want to show attendees from in the block settings.', 'event_espresso'),
43 43
 
44 44
 	// Reference: domains/core/admin/blocks/src/event-attendees/AttendeesDisplay.tsx:53
45
-	__( 'There are no attendees for selected options.', 'event_espresso' ),
45
+	__('There are no attendees for selected options.', 'event_espresso'),
46 46
 
47 47
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/ArchiveSettings.tsx:12
48
-	__( 'Display on Archives', 'event_espresso' ),
48
+	__('Display on Archives', 'event_espresso'),
49 49
 
50 50
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/ArchiveSettings.tsx:17
51
-	__( 'Attendees are shown whenever this post is listed in an archive view.', 'event_espresso' ),
51
+	__('Attendees are shown whenever this post is listed in an archive view.', 'event_espresso'),
52 52
 
53 53
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/ArchiveSettings.tsx:18
54
-	__( 'Attendees are hidden whenever this post is listed in an archive view.', 'event_espresso' ),
54
+	__('Attendees are hidden whenever this post is listed in an archive view.', 'event_espresso'),
55 55
 
56 56
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/AttendeeLimit.tsx:29
57
-	__( 'Number of Attendees to Display:', 'event_espresso' ),
57
+	__('Number of Attendees to Display:', 'event_espresso'),
58 58
 
59 59
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/AttendeeLimit.tsx:34
60 60
 	/* translators: %d attendees count */
61
-	_n_noop( 'Used to adjust the number of attendees displayed (There is %d total attendee for the current filter settings).', 'Used to adjust the number of attendees displayed (There are %d total attendees for the current filter settings).', 'event_espresso' ),
61
+	_n_noop('Used to adjust the number of attendees displayed (There is %d total attendee for the current filter settings).', 'Used to adjust the number of attendees displayed (There are %d total attendees for the current filter settings).', 'event_espresso'),
62 62
 
63 63
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/GravatarSettings.tsx:27
64
-	__( 'Display Gravatar', 'event_espresso' ),
64
+	__('Display Gravatar', 'event_espresso'),
65 65
 
66 66
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/GravatarSettings.tsx:32
67
-	__( 'Gravatar images are shown for each attendee.', 'event_espresso' ),
67
+	__('Gravatar images are shown for each attendee.', 'event_espresso'),
68 68
 
69 69
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/GravatarSettings.tsx:33
70
-	__( 'No gravatar images are shown for each attendee.', 'event_espresso' ),
70
+	__('No gravatar images are shown for each attendee.', 'event_espresso'),
71 71
 
72 72
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/GravatarSettings.tsx:38
73
-	__( 'Size of Gravatar', 'event_espresso' ),
73
+	__('Size of Gravatar', 'event_espresso'),
74 74
 
75 75
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectDatetime.tsx:22
76
-	__( 'Select Datetime', 'event_espresso' ),
76
+	__('Select Datetime', 'event_espresso'),
77 77
 
78 78
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectEvent.tsx:22
79
-	__( 'Select Event', 'event_espresso' ),
79
+	__('Select Event', 'event_espresso'),
80 80
 
81 81
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectOrderBy.tsx:11
82
-	__( 'Attendee id', 'event_espresso' ),
82
+	__('Attendee id', 'event_espresso'),
83 83
 
84 84
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectOrderBy.tsx:15
85
-	__( 'Last name only', 'event_espresso' ),
85
+	__('Last name only', 'event_espresso'),
86 86
 
87 87
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectOrderBy.tsx:19
88
-	__( 'First name only', 'event_espresso' ),
88
+	__('First name only', 'event_espresso'),
89 89
 
90 90
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectOrderBy.tsx:23
91
-	__( 'First, then Last name', 'event_espresso' ),
91
+	__('First, then Last name', 'event_espresso'),
92 92
 
93 93
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectOrderBy.tsx:27
94
-	__( 'Last, then First name', 'event_espresso' ),
94
+	__('Last, then First name', 'event_espresso'),
95 95
 
96 96
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectOrderBy.tsx:41
97
-	__( 'Order Attendees by:', 'event_espresso' ),
97
+	__('Order Attendees by:', 'event_espresso'),
98 98
 
99 99
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/SelectTicket.tsx:22
100
-	__( 'Select Ticket', 'event_espresso' ),
100
+	__('Select Ticket', 'event_espresso'),
101 101
 
102 102
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/index.tsx:21
103
-	__( 'Filter By Settings', 'event_espresso' ),
103
+	__('Filter By Settings', 'event_espresso'),
104 104
 
105 105
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/index.tsx:36
106
-	__( 'Gravatar Setttings', 'event_espresso' ),
106
+	__('Gravatar Setttings', 'event_espresso'),
107 107
 
108 108
 	// Reference: domains/core/admin/blocks/src/event-attendees/controls/index.tsx:39
109
-	__( 'Archive Settings', 'event_espresso' ),
109
+	__('Archive Settings', 'event_espresso'),
110 110
 
111 111
 	// Reference: domains/core/admin/blocks/src/event-attendees/index.tsx:10
112
-	__( 'Event Attendees', 'event_espresso' ),
112
+	__('Event Attendees', 'event_espresso'),
113 113
 
114 114
 	// Reference: domains/core/admin/blocks/src/event-attendees/index.tsx:11
115
-	__( 'Displays a list of people that have registered for the specified event', 'event_espresso' ),
115
+	__('Displays a list of people that have registered for the specified event', 'event_espresso'),
116 116
 
117 117
 	// Reference: domains/core/admin/blocks/src/event-attendees/index.tsx:14
118
-	__( 'event', 'event_espresso' ),
118
+	__('event', 'event_espresso'),
119 119
 
120 120
 	// Reference: domains/core/admin/blocks/src/event-attendees/index.tsx:14
121
-	__( 'attendees', 'event_espresso' ),
121
+	__('attendees', 'event_espresso'),
122 122
 
123 123
 	// Reference: domains/core/admin/blocks/src/event-attendees/index.tsx:14
124
-	__( 'list', 'event_espresso' ),
124
+	__('list', 'event_espresso'),
125 125
 
126 126
 	// Reference: domains/core/admin/blocks/src/services/utils.ts:17
127
-	__( 'Error', 'event_espresso' ),
127
+	__('Error', 'event_espresso'),
128 128
 
129 129
 	// Reference: domains/core/admin/blocks/src/services/utils.ts:24
130 130
 	// Reference: packages/ui-components/src/SimpleEntityList/EntityTemplate.tsx:16
131
-	__( 'Select…', 'event_espresso' ),
131
+	__('Select…', 'event_espresso'),
132 132
 
133 133
 	// Reference: domains/core/admin/blocks/src/services/utils.ts:9
134
-	__( 'Loading…', 'event_espresso' ),
134
+	__('Loading…', 'event_espresso'),
135 135
 
136 136
 	// Reference: domains/core/admin/eventEditor/src/ui/EventDescription.tsx:33
137
-	__( 'Event Description', 'event_espresso' ),
137
+	__('Event Description', 'event_espresso'),
138 138
 
139 139
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/ActiveStatus.tsx:22
140
-	__( 'Active status', 'event_espresso' ),
140
+	__('Active status', 'event_espresso'),
141 141
 
142 142
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/AltRegPage.tsx:14
143
-	__( 'Alternative Registration Page', 'event_espresso' ),
143
+	__('Alternative Registration Page', 'event_espresso'),
144 144
 
145 145
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/DefaultRegistrationStatus.tsx:15
146
-	__( 'Default Registration Status', 'event_espresso' ),
146
+	__('Default Registration Status', 'event_espresso'),
147 147
 
148 148
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/Donations.tsx:9
149
-	__( 'Donations Enabled', 'event_espresso' ),
149
+	__('Donations Enabled', 'event_espresso'),
150 150
 
151 151
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/Donations.tsx:9
152
-	__( 'Donations Disabled', 'event_espresso' ),
152
+	__('Donations Disabled', 'event_espresso'),
153 153
 
154 154
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/EventManager.tsx:16
155
-	__( 'Event Manager', 'event_espresso' ),
155
+	__('Event Manager', 'event_espresso'),
156 156
 
157 157
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/EventPhoneNumber.tsx:11
158
-	__( 'Event Phone Number', 'event_espresso' ),
158
+	__('Event Phone Number', 'event_espresso'),
159 159
 
160 160
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/MaxRegistrations.tsx:12
161
-	__( 'Max Registrations per Transaction', 'event_espresso' ),
161
+	__('Max Registrations per Transaction', 'event_espresso'),
162 162
 
163 163
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/TicketSelector.tsx:9
164
-	__( 'Ticket Selector Enabled', 'event_espresso' ),
164
+	__('Ticket Selector Enabled', 'event_espresso'),
165 165
 
166 166
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/TicketSelector.tsx:9
167
-	__( 'Ticket Selector Disabled', 'event_espresso' ),
167
+	__('Ticket Selector Disabled', 'event_espresso'),
168 168
 
169 169
 	// Reference: domains/core/admin/eventEditor/src/ui/EventRegistrationOptions/index.tsx:43
170
-	__( 'Registration Options', 'event_espresso' ),
170
+	__('Registration Options', 'event_espresso'),
171 171
 
172 172
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/DateRegistrationsLink.tsx:13
173
-	__( 'view ALL registrations for this date.', 'event_espresso' ),
173
+	__('view ALL registrations for this date.', 'event_espresso'),
174 174
 
175 175
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/DateFormSteps.tsx:10
176
-	__( 'primary information about the date', 'event_espresso' ),
176
+	__('primary information about the date', 'event_espresso'),
177 177
 
178 178
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/DateFormSteps.tsx:10
179
-	__( 'Date Details', 'event_espresso' ),
179
+	__('Date Details', 'event_espresso'),
180 180
 
181 181
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/DateFormSteps.tsx:11
182 182
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/TicketFormSteps.tsx:16
183 183
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/TicketFormSteps.tsx:16
184
-	__( 'relations between tickets and dates', 'event_espresso' ),
184
+	__('relations between tickets and dates', 'event_espresso'),
185 185
 
186 186
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/DateFormSteps.tsx:11
187
-	__( 'Assign Tickets', 'event_espresso' ),
187
+	__('Assign Tickets', 'event_espresso'),
188 188
 
189 189
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/FooterButtons.tsx:22
190
-	__( 'Save and assign tickets', 'event_espresso' ),
190
+	__('Save and assign tickets', 'event_espresso'),
191 191
 
192 192
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/Modal.tsx:27
193 193
 	/* translators: %s datetime id */
194
-	__( 'Edit datetime %s', 'event_espresso' ),
194
+	__('Edit datetime %s', 'event_espresso'),
195 195
 
196 196
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/multiStep/Modal.tsx:30
197
-	__( 'New Datetime', 'event_espresso' ),
197
+	__('New Datetime', 'event_espresso'),
198 198
 
199 199
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:110
200 200
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:108
201 201
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:126
202 202
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:108
203
-	__( 'Details', 'event_espresso' ),
203
+	__('Details', 'event_espresso'),
204 204
 
205 205
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:114
206 206
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:112
207 207
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:81
208
-	__( 'Capacity', 'event_espresso' ),
208
+	__('Capacity', 'event_espresso'),
209 209
 
210 210
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:119
211
-	__( 'The maximum number of registrants that can attend the event at this particular date.', 'event_espresso' ),
211
+	__('The maximum number of registrants that can attend the event at this particular date.', 'event_espresso'),
212 212
 
213 213
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:123
214
-	__( 'Set to 0 to close registration or leave blank for no limit.', 'event_espresso' ),
214
+	__('Set to 0 to close registration or leave blank for no limit.', 'event_espresso'),
215 215
 
216 216
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:129
217 217
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:203
218
-	__( 'Trash', 'event_espresso' ),
218
+	__('Trash', 'event_espresso'),
219 219
 
220 220
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:71
221 221
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:45
222 222
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:87
223 223
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:45
224
-	__( 'Basics', 'event_espresso' ),
224
+	__('Basics', 'event_espresso'),
225 225
 
226 226
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:75
227 227
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:49
@@ -229,249 +229,249 @@  discard block
 block discarded – undo
229 229
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:91
230 230
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:49
231 231
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:40
232
-	__( 'Name', 'event_espresso' ),
232
+	__('Name', 'event_espresso'),
233 233
 
234 234
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:80
235 235
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:55
236 236
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:96
237 237
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:55
238 238
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:47
239
-	__( 'Description', 'event_espresso' ),
239
+	__('Description', 'event_espresso'),
240 240
 
241 241
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:88
242 242
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:63
243 243
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:63
244
-	__( 'Dates', 'event_espresso' ),
244
+	__('Dates', 'event_espresso'),
245 245
 
246 246
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:92
247 247
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:51
248 248
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:108
249
-	__( 'Start Date', 'event_espresso' ),
249
+	__('Start Date', 'event_espresso'),
250 250
 
251 251
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/dateForm/useDateFormConfig.ts:99
252 252
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:65
253 253
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:115
254
-	__( 'End Date', 'event_espresso' ),
254
+	__('End Date', 'event_espresso'),
255 255
 
256 256
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/DatesList.tsx:35
257 257
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/TableView.tsx:33
258
-	__( 'Event Dates', 'event_espresso' ),
258
+	__('Event Dates', 'event_espresso'),
259 259
 
260 260
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/DatesList.tsx:38
261
-	__( 'loading event dates…', 'event_espresso' ),
261
+	__('loading event dates…', 'event_espresso'),
262 262
 
263 263
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/DatesListButtons.tsx:20
264
-	__( 'Add a date or a ticket in order to use Ticket Assignment Manager', 'event_espresso' ),
264
+	__('Add a date or a ticket in order to use Ticket Assignment Manager', 'event_espresso'),
265 265
 
266 266
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/DatesListButtons.tsx:30
267
-	__( 'Ticket Assignments', 'event_espresso' ),
267
+	__('Ticket Assignments', 'event_espresso'),
268 268
 
269 269
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/AssignTicketsButton.tsx:25
270
-	__( 'Number of related tickets', 'event_espresso' ),
270
+	__('Number of related tickets', 'event_espresso'),
271 271
 
272 272
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/AssignTicketsButton.tsx:26
273
-	__( 'There are no tickets assigned to this datetime. Please click the ticket icon to update the assignments.', 'event_espresso' ),
273
+	__('There are no tickets assigned to this datetime. Please click the ticket icon to update the assignments.', 'event_espresso'),
274 274
 
275 275
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/AssignTicketsButton.tsx:34
276
-	__( 'assign tickets', 'event_espresso' ),
276
+	__('assign tickets', 'event_espresso'),
277 277
 
278 278
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:25
279
-	__( 'Permanently delete Datetime?', 'event_espresso' ),
279
+	__('Permanently delete Datetime?', 'event_espresso'),
280 280
 
281 281
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:25
282
-	__( 'Move Datetime to Trash?', 'event_espresso' ),
282
+	__('Move Datetime to Trash?', 'event_espresso'),
283 283
 
284 284
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:27
285
-	__( 'Are you sure you want to permanently delete this datetime? This action is permanent and can not be undone.', 'event_espresso' ),
285
+	__('Are you sure you want to permanently delete this datetime? This action is permanent and can not be undone.', 'event_espresso'),
286 286
 
287 287
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:30
288
-	__( 'Are you sure you want to move this datetime to the trash? You can "untrash" this datetime later if you need to.', 'event_espresso' ),
288
+	__('Are you sure you want to move this datetime to the trash? You can "untrash" this datetime later if you need to.', 'event_espresso'),
289 289
 
290 290
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:39
291 291
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/DeleteTicket.tsx:44
292
-	__( 'delete permanently', 'event_espresso' ),
292
+	__('delete permanently', 'event_espresso'),
293 293
 
294 294
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:39
295
-	__( 'trash datetime', 'event_espresso' ),
295
+	__('trash datetime', 'event_espresso'),
296 296
 
297 297
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:45
298
-	__( 'event date main menu', 'event_espresso' ),
298
+	__('event date main menu', 'event_espresso'),
299 299
 
300 300
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:59
301
-	__( 'edit datetime', 'event_espresso' ),
301
+	__('edit datetime', 'event_espresso'),
302 302
 
303 303
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/actionsMenu/dropdown/DateMainMenu.tsx:60
304
-	__( 'copy datetime', 'event_espresso' ),
304
+	__('copy datetime', 'event_espresso'),
305 305
 
306 306
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/actions/Actions.tsx:36
307 307
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/actions/Actions.tsx:39
308 308
 	// Reference: packages/ui-components/src/bulkEdit/BulkActions.tsx:43
309
-	__( 'bulk actions', 'event_espresso' ),
309
+	__('bulk actions', 'event_espresso'),
310 310
 
311 311
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/actions/Actions.tsx:40
312
-	__( 'edit datetime details', 'event_espresso' ),
312
+	__('edit datetime details', 'event_espresso'),
313 313
 
314 314
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/actions/Actions.tsx:44
315
-	__( 'delete datetimes', 'event_espresso' ),
315
+	__('delete datetimes', 'event_espresso'),
316 316
 
317 317
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/actions/Actions.tsx:44
318
-	__( 'trash datetimes', 'event_espresso' ),
318
+	__('trash datetimes', 'event_espresso'),
319 319
 
320 320
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/delete/Delete.tsx:14
321
-	__( 'Are you sure you want to permanently delete these datetimes? This action can NOT be undone!', 'event_espresso' ),
321
+	__('Are you sure you want to permanently delete these datetimes? This action can NOT be undone!', 'event_espresso'),
322 322
 
323 323
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/delete/Delete.tsx:15
324
-	__( 'Are you sure you want to trash these datetimes?', 'event_espresso' ),
324
+	__('Are you sure you want to trash these datetimes?', 'event_espresso'),
325 325
 
326 326
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/delete/Delete.tsx:16
327
-	__( 'Delete datetimes permanently', 'event_espresso' ),
327
+	__('Delete datetimes permanently', 'event_espresso'),
328 328
 
329 329
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/delete/Delete.tsx:16
330
-	__( 'Trash datetimes', 'event_espresso' ),
330
+	__('Trash datetimes', 'event_espresso'),
331 331
 
332 332
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/EditDetails.tsx:21
333
-	__( 'Bulk edit date details', 'event_espresso' ),
333
+	__('Bulk edit date details', 'event_espresso'),
334 334
 
335 335
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/EditDetails.tsx:22
336
-	__( 'any changes will be applied to ALL of the selected dates.', 'event_espresso' ),
336
+	__('any changes will be applied to ALL of the selected dates.', 'event_espresso'),
337 337
 
338 338
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/formValidation.ts:12
339 339
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/formValidation.ts:12
340
-	__( 'Name must be at least three characters', 'event_espresso' ),
340
+	__('Name must be at least three characters', 'event_espresso'),
341 341
 
342 342
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:67
343 343
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:67
344
-	__( 'Shift dates', 'event_espresso' ),
344
+	__('Shift dates', 'event_espresso'),
345 345
 
346 346
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:92
347 347
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:92
348
-	__( 'earlier', 'event_espresso' ),
348
+	__('earlier', 'event_espresso'),
349 349
 
350 350
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/bulkEdit/details/useBulkEditFormConfig.ts:96
351 351
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:96
352
-	__( 'later', 'event_espresso' ),
352
+	__('later', 'event_espresso'),
353 353
 
354 354
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/DateCapacity.tsx:37
355
-	__( 'edit capacity (registration limit)…', 'event_espresso' ),
355
+	__('edit capacity (registration limit)…', 'event_espresso'),
356 356
 
357 357
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/DateCardSidebar.tsx:38
358
-	__( 'Edit Event Date', 'event_espresso' ),
358
+	__('Edit Event Date', 'event_espresso'),
359 359
 
360 360
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/DateCardSidebar.tsx:42
361
-	__( 'edit start and end dates', 'event_espresso' ),
361
+	__('edit start and end dates', 'event_espresso'),
362 362
 
363 363
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/DateDetailsPanel.tsx:15
364 364
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/TicketDetailsPanel.tsx:15
365
-	__( 'sold', 'event_espresso' ),
365
+	__('sold', 'event_espresso'),
366 366
 
367 367
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/DateDetailsPanel.tsx:28
368
-	__( 'capacity', 'event_espresso' ),
368
+	__('capacity', 'event_espresso'),
369 369
 
370 370
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/DateDetailsPanel.tsx:34
371 371
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/TicketDetailsPanel.tsx:33
372
-	__( 'reg list', 'event_espresso' ),
372
+	__('reg list', 'event_espresso'),
373 373
 
374 374
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/Details.tsx:46
375 375
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/Details.tsx:45
376
-	__( 'Edit description', 'event_espresso' ),
376
+	__('Edit description', 'event_espresso'),
377 377
 
378 378
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/cardView/Details.tsx:47
379 379
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/Details.tsx:46
380
-	__( 'edit description…', 'event_espresso' ),
380
+	__('edit description…', 'event_espresso'),
381 381
 
382 382
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:10
383
-	__( 'Move Date to Trash', 'event_espresso' ),
383
+	__('Move Date to Trash', 'event_espresso'),
384 384
 
385 385
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:13
386 386
 	// Reference: packages/constants/src/datetime.ts:6
387
-	__( 'Active', 'event_espresso' ),
387
+	__('Active', 'event_espresso'),
388 388
 
389 389
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:14
390 390
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:13
391
-	__( 'Trashed', 'event_espresso' ),
391
+	__('Trashed', 'event_espresso'),
392 392
 
393 393
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:15
394 394
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:14
395 395
 	// Reference: packages/constants/src/datetime.ts:8
396
-	__( 'Expired', 'event_espresso' ),
396
+	__('Expired', 'event_espresso'),
397 397
 
398 398
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:16
399 399
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:16
400
-	__( 'Sold Out', 'event_espresso' ),
400
+	__('Sold Out', 'event_espresso'),
401 401
 
402 402
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:17
403 403
 	// Reference: packages/constants/src/datetime.ts:12
404
-	__( 'Upcoming', 'event_espresso' ),
404
+	__('Upcoming', 'event_espresso'),
405 405
 
406 406
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:7
407
-	__( 'Edit Event Date Details', 'event_espresso' ),
407
+	__('Edit Event Date Details', 'event_espresso'),
408 408
 
409 409
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:8
410
-	__( 'View Registrations for this Date', 'event_espresso' ),
410
+	__('View Registrations for this Date', 'event_espresso'),
411 411
 
412 412
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/config.ts:9
413
-	__( 'Manage Ticket Assignments', 'event_espresso' ),
413
+	__('Manage Ticket Assignments', 'event_espresso'),
414 414
 
415 415
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/editable/EditableName.tsx:23
416 416
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/editable/EditableName.tsx:34
417
-	__( 'edit title…', 'event_espresso' ),
417
+	__('edit title…', 'event_espresso'),
418 418
 
419 419
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/ActiveDatesFilters.tsx:17
420 420
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/ActiveTicketsFilters.tsx:17
421
-	__( 'ON', 'event_espresso' ),
421
+	__('ON', 'event_espresso'),
422 422
 
423 423
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:10
424
-	__( 'end dates only', 'event_espresso' ),
424
+	__('end dates only', 'event_espresso'),
425 425
 
426 426
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:11
427
-	__( 'start and end dates', 'event_espresso' ),
427
+	__('start and end dates', 'event_espresso'),
428 428
 
429 429
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:16
430
-	__( 'dates above 90% capacity', 'event_espresso' ),
430
+	__('dates above 90% capacity', 'event_espresso'),
431 431
 
432 432
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:17
433
-	__( 'dates above 75% capacity', 'event_espresso' ),
433
+	__('dates above 75% capacity', 'event_espresso'),
434 434
 
435 435
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:18
436
-	__( 'dates above 50% capacity', 'event_espresso' ),
436
+	__('dates above 50% capacity', 'event_espresso'),
437 437
 
438 438
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:19
439
-	__( 'dates below 50% capacity', 'event_espresso' ),
439
+	__('dates below 50% capacity', 'event_espresso'),
440 440
 
441 441
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:23
442
-	__( 'all dates', 'event_espresso' ),
442
+	__('all dates', 'event_espresso'),
443 443
 
444 444
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:24
445
-	__( 'all active and upcoming', 'event_espresso' ),
445
+	__('all active and upcoming', 'event_espresso'),
446 446
 
447 447
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:25
448
-	__( 'active dates only', 'event_espresso' ),
448
+	__('active dates only', 'event_espresso'),
449 449
 
450 450
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:26
451
-	__( 'upcoming dates only', 'event_espresso' ),
451
+	__('upcoming dates only', 'event_espresso'),
452 452
 
453 453
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:27
454
-	__( 'next active or upcoming only', 'event_espresso' ),
454
+	__('next active or upcoming only', 'event_espresso'),
455 455
 
456 456
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:28
457
-	__( 'sold out dates only', 'event_espresso' ),
457
+	__('sold out dates only', 'event_espresso'),
458 458
 
459 459
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:29
460
-	__( 'recently expired dates', 'event_espresso' ),
460
+	__('recently expired dates', 'event_espresso'),
461 461
 
462 462
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:30
463
-	__( 'all expired dates', 'event_espresso' ),
463
+	__('all expired dates', 'event_espresso'),
464 464
 
465 465
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:31
466
-	__( 'trashed dates only', 'event_espresso' ),
466
+	__('trashed dates only', 'event_espresso'),
467 467
 
468 468
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:35
469 469
 	// Reference: packages/dates/src/components/DateRangePicker/DateRangePickerLegend.tsx:9
470 470
 	// Reference: packages/dates/src/components/DateRangePicker/index.tsx:61
471
-	__( 'start date', 'event_espresso' ),
471
+	__('start date', 'event_espresso'),
472 472
 
473 473
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:36
474
-	__( 'name', 'event_espresso' ),
474
+	__('name', 'event_espresso'),
475 475
 
476 476
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:37
477 477
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:31
@@ -479,141 +479,141 @@  discard block
 block discarded – undo
479 479
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/HeaderCell.tsx:27
480 480
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:31
481 481
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:23
482
-	__( 'ID', 'event_espresso' ),
482
+	__('ID', 'event_espresso'),
483 483
 
484 484
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:38
485 485
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:47
486
-	__( 'custom order', 'event_espresso' ),
486
+	__('custom order', 'event_espresso'),
487 487
 
488 488
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:42
489 489
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:51
490
-	__( 'display', 'event_espresso' ),
490
+	__('display', 'event_espresso'),
491 491
 
492 492
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:43
493
-	__( 'recurrence', 'event_espresso' ),
493
+	__('recurrence', 'event_espresso'),
494 494
 
495 495
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:44
496 496
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:53
497
-	__( 'sales', 'event_espresso' ),
497
+	__('sales', 'event_espresso'),
498 498
 
499 499
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:45
500 500
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:55
501
-	__( 'sort by', 'event_espresso' ),
501
+	__('sort by', 'event_espresso'),
502 502
 
503 503
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:46
504 504
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:54
505 505
 	// Reference: packages/ee-components/src/EntityList/EntityListFilterBar.tsx:46
506
-	__( 'search', 'event_espresso' ),
506
+	__('search', 'event_espresso'),
507 507
 
508 508
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:47
509 509
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:56
510
-	__( 'status', 'event_espresso' ),
510
+	__('status', 'event_espresso'),
511 511
 
512 512
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/filterBar/controls/options.ts:9
513
-	__( 'start dates only', 'event_espresso' ),
513
+	__('start dates only', 'event_espresso'),
514 514
 
515 515
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/newDateOptions/AddSingleDate.tsx:26
516 516
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/newDateOptions/NewDateModal.tsx:12
517 517
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/newDateOptions/OptionsModalButton.tsx:16
518
-	__( 'Add New Date', 'event_espresso' ),
518
+	__('Add New Date', 'event_espresso'),
519 519
 
520 520
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/newDateOptions/AddSingleDate.tsx:26
521
-	__( 'Add Single Date', 'event_espresso' ),
521
+	__('Add Single Date', 'event_espresso'),
522 522
 
523 523
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/newDateOptions/AddSingleDate.tsx:43
524
-	__( 'Add a single date that only occurs once', 'event_espresso' ),
524
+	__('Add a single date that only occurs once', 'event_espresso'),
525 525
 
526 526
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/newDateOptions/AddSingleDate.tsx:45
527
-	__( 'Single Date', 'event_espresso' ),
527
+	__('Single Date', 'event_espresso'),
528 528
 
529 529
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:106
530
-	__( 'Reg list', 'event_espresso' ),
530
+	__('Reg list', 'event_espresso'),
531 531
 
532 532
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:107
533 533
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:111
534
-	__( 'Regs', 'event_espresso' ),
534
+	__('Regs', 'event_espresso'),
535 535
 
536 536
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:122
537 537
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:126
538 538
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:59
539
-	__( 'Actions', 'event_espresso' ),
539
+	__('Actions', 'event_espresso'),
540 540
 
541 541
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:52
542
-	__( 'Start', 'event_espresso' ),
542
+	__('Start', 'event_espresso'),
543 543
 
544 544
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:66
545
-	__( 'End', 'event_espresso' ),
545
+	__('End', 'event_espresso'),
546 546
 
547 547
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:82
548
-	__( 'Cap', 'event_espresso' ),
548
+	__('Cap', 'event_espresso'),
549 549
 
550 550
 	// Reference: domains/core/admin/eventEditor/src/ui/datetimes/datesList/tableView/useHeaderRowGenerator.tsx:94
551 551
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:98
552
-	__( 'Sold', 'event_espresso' ),
552
+	__('Sold', 'event_espresso'),
553 553
 
554 554
 	// Reference: domains/core/admin/eventEditor/src/ui/registrationForm/RegistrationForm.tsx:11
555
-	__( 'Registration Form', 'event_espresso' ),
555
+	__('Registration Form', 'event_espresso'),
556 556
 
557 557
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ErrorMessage.tsx:18
558
-	__( 'Tickets must always have at least one date assigned to them but one or more of the tickets below does not have any. 
559
-Please correct the assignments for the highlighted cells.', 'event_espresso' ),
558
+	__('Tickets must always have at least one date assigned to them but one or more of the tickets below does not have any. 
559
+Please correct the assignments for the highlighted cells.', 'event_espresso'),
560 560
 
561 561
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ErrorMessage.tsx:22
562
-	__( 'Event Dates must always have at least one Ticket assigned to them but one or more of the Event Dates below does not have any. 
563
-Please correct the assignments for the highlighted cells.', 'event_espresso' ),
562
+	__('Event Dates must always have at least one Ticket assigned to them but one or more of the Event Dates below does not have any. 
563
+Please correct the assignments for the highlighted cells.', 'event_espresso'),
564 564
 
565 565
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ErrorMessage.tsx:32
566
-	__( 'Please Update Assignments', 'event_espresso' ),
566
+	__('Please Update Assignments', 'event_espresso'),
567 567
 
568 568
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ModalContainer.tsx:26
569
-	__( 'There seem to be some dates/tickets which have no tickets/dates assigned. Do you want to fix them now?', 'event_espresso' ),
569
+	__('There seem to be some dates/tickets which have no tickets/dates assigned. Do you want to fix them now?', 'event_espresso'),
570 570
 
571 571
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ModalContainer.tsx:29
572 572
 	// Reference: packages/tpc/src/hooks/useLockedTicketAction.ts:74
573 573
 	// Reference: packages/ui-components/src/Modal/ModalWithAlert.tsx:21
574
-	__( 'Alert!', 'event_espresso' ),
574
+	__('Alert!', 'event_espresso'),
575 575
 
576 576
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ModalContainer.tsx:42
577 577
 	/* translators: 1 entity id, 2 entity name */
578
-	__( 'Ticket Assignment Manager for Datetime: %1$s - %2$s', 'event_espresso' ),
578
+	__('Ticket Assignment Manager for Datetime: %1$s - %2$s', 'event_espresso'),
579 579
 
580 580
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/ModalContainer.tsx:49
581 581
 	/* translators: 1 entity id, 2 entity name */
582
-	__( 'Ticket Assignment Manager for Ticket: %1$s - %2$s', 'event_espresso' ),
582
+	__('Ticket Assignment Manager for Ticket: %1$s - %2$s', 'event_espresso'),
583 583
 
584 584
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/TicketAssignmentsManagerModal.tsx:28
585 585
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/Table.tsx:13
586
-	__( 'Ticket Assignment Manager', 'event_espresso' ),
586
+	__('Ticket Assignment Manager', 'event_espresso'),
587 587
 
588 588
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/config.ts:10
589
-	__( 'existing relation', 'event_espresso' ),
589
+	__('existing relation', 'event_espresso'),
590 590
 
591 591
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/config.ts:15
592
-	__( 'remove existing relation', 'event_espresso' ),
592
+	__('remove existing relation', 'event_espresso'),
593 593
 
594 594
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/config.ts:20
595
-	__( 'add new relation', 'event_espresso' ),
595
+	__('add new relation', 'event_espresso'),
596 596
 
597 597
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/config.ts:25
598
-	__( 'invalid relation', 'event_espresso' ),
598
+	__('invalid relation', 'event_espresso'),
599 599
 
600 600
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/config.ts:29
601
-	__( 'no relation', 'event_espresso' ),
601
+	__('no relation', 'event_espresso'),
602 602
 
603 603
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/BodyCell.tsx:23
604
-	__( 'assign ticket', 'event_espresso' ),
604
+	__('assign ticket', 'event_espresso'),
605 605
 
606 606
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/useGetHeaderRows.tsx:15
607
-	__( 'Assignments', 'event_espresso' ),
607
+	__('Assignments', 'event_espresso'),
608 608
 
609 609
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/useGetHeaderRows.tsx:16
610
-	__( 'Event Dates are listed below', 'event_espresso' ),
610
+	__('Event Dates are listed below', 'event_espresso'),
611 611
 
612 612
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/useGetHeaderRows.tsx:17
613
-	__( 'Tickets are listed along the top', 'event_espresso' ),
613
+	__('Tickets are listed along the top', 'event_espresso'),
614 614
 
615 615
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/table/useGetHeaderRows.tsx:18
616
-	__( 'Click the cell buttons to toggle assigments', 'event_espresso' ),
616
+	__('Click the cell buttons to toggle assigments', 'event_espresso'),
617 617
 
618 618
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/components/useSubmitButtonProps.ts:29
619 619
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/FooterButtons.tsx:16
@@ -622,1313 +622,1313 @@  discard block
 block discarded – undo
622 622
 	// Reference: packages/tpc/src/buttons/useSubmitButtonProps.tsx:29
623 623
 	// Reference: packages/ui-components/src/Modal/useSubmitButtonProps.tsx:13
624 624
 	// Reference: packages/ui-components/src/Stepper/buttons/Submit.tsx:7
625
-	__( 'Submit', 'event_espresso' ),
625
+	__('Submit', 'event_espresso'),
626 626
 
627 627
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/filters/controls/DatesByMonthControl.tsx:20
628
-	__( 'All Dates', 'event_espresso' ),
628
+	__('All Dates', 'event_espresso'),
629 629
 
630 630
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/filters/controls/DatesByMonthControl.tsx:27
631
-	__( 'dates by month', 'event_espresso' ),
631
+	__('dates by month', 'event_espresso'),
632 632
 
633 633
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/filters/controls/ShowExpiredTicketsControl.tsx:16
634
-	__( 'show expired tickets', 'event_espresso' ),
634
+	__('show expired tickets', 'event_espresso'),
635 635
 
636 636
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/filters/controls/ShowTrashedDatesControl.tsx:13
637
-	__( 'show trashed dates', 'event_espresso' ),
637
+	__('show trashed dates', 'event_espresso'),
638 638
 
639 639
 	// Reference: domains/core/admin/eventEditor/src/ui/ticketAssignmentsManager/filters/controls/ShowTrashedTicketsControl.tsx:16
640
-	__( 'show trashed tickets', 'event_espresso' ),
640
+	__('show trashed tickets', 'event_espresso'),
641 641
 
642 642
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/TicketRegistrationsLink.tsx:13
643
-	__( 'total registrations.', 'event_espresso' ),
643
+	__('total registrations.', 'event_espresso'),
644 644
 
645 645
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/TicketRegistrationsLink.tsx:14
646
-	__( 'view ALL registrations for this ticket.', 'event_espresso' ),
646
+	__('view ALL registrations for this ticket.', 'event_espresso'),
647 647
 
648 648
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/Container.tsx:38
649 649
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actions/Actions.tsx:21
650
-	__( 'Default tickets', 'event_espresso' ),
650
+	__('Default tickets', 'event_espresso'),
651 651
 
652 652
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/FooterButtons.tsx:26
653 653
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/FooterButtons.tsx:33
654
-	__( 'Set ticket prices', 'event_espresso' ),
654
+	__('Set ticket prices', 'event_espresso'),
655 655
 
656 656
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/FooterButtons.tsx:31
657
-	__( 'Skip prices - Save', 'event_espresso' ),
657
+	__('Skip prices - Save', 'event_espresso'),
658 658
 
659 659
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/FooterButtons.tsx:37
660 660
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/FooterButtons.tsx:57
661
-	__( 'Ticket details', 'event_espresso' ),
661
+	__('Ticket details', 'event_espresso'),
662 662
 
663 663
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/FooterButtons.tsx:38
664
-	__( 'Save', 'event_espresso' ),
664
+	__('Save', 'event_espresso'),
665 665
 
666 666
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/Modal.tsx:22
667 667
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/Modal.tsx:26
668 668
 	/* translators: %s ticket id */
669
-	__( 'Edit ticket %s', 'event_espresso' ),
669
+	__('Edit ticket %s', 'event_espresso'),
670 670
 
671 671
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/Modal.tsx:25
672 672
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/Modal.tsx:29
673
-	__( 'New Ticket Details', 'event_espresso' ),
673
+	__('New Ticket Details', 'event_espresso'),
674 674
 
675 675
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/TicketFormSteps.tsx:10
676 676
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/TicketFormSteps.tsx:10
677
-	__( 'primary information about the ticket', 'event_espresso' ),
677
+	__('primary information about the ticket', 'event_espresso'),
678 678
 
679 679
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/TicketFormSteps.tsx:10
680 680
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/TicketFormSteps.tsx:10
681
-	__( 'Ticket Details', 'event_espresso' ),
681
+	__('Ticket Details', 'event_espresso'),
682 682
 
683 683
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/TicketFormSteps.tsx:12
684 684
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/TicketFormSteps.tsx:12
685
-	__( 'apply ticket price modifiers and taxes', 'event_espresso' ),
685
+	__('apply ticket price modifiers and taxes', 'event_espresso'),
686 686
 
687 687
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/TicketFormSteps.tsx:14
688 688
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/TicketFormSteps.tsx:14
689
-	__( 'Price Calculator', 'event_espresso' ),
689
+	__('Price Calculator', 'event_espresso'),
690 690
 
691 691
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/defaultTickets/multiStep/TicketFormSteps.tsx:16
692 692
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/TicketFormSteps.tsx:16
693
-	__( 'Assign Dates', 'event_espresso' ),
693
+	__('Assign Dates', 'event_espresso'),
694 694
 
695 695
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/FooterButtons.tsx:39
696
-	__( 'Skip prices - assign dates', 'event_espresso' ),
696
+	__('Skip prices - assign dates', 'event_espresso'),
697 697
 
698 698
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/multiStep/FooterButtons.tsx:50
699
-	__( 'Save and assign dates', 'event_espresso' ),
699
+	__('Save and assign dates', 'event_espresso'),
700 700
 
701 701
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:104
702
-	__( 'Ticket Sales', 'event_espresso' ),
702
+	__('Ticket Sales', 'event_espresso'),
703 703
 
704 704
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:130
705 705
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:112
706
-	__( 'Quantity For Sale', 'event_espresso' ),
706
+	__('Quantity For Sale', 'event_espresso'),
707 707
 
708 708
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:136
709
-	__( 'The maximum number of this ticket available for sale.', 'event_espresso' ),
709
+	__('The maximum number of this ticket available for sale.', 'event_espresso'),
710 710
 
711 711
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:138
712
-	__( 'Set to 0 to stop sales, or leave blank for no limit.', 'event_espresso' ),
712
+	__('Set to 0 to stop sales, or leave blank for no limit.', 'event_espresso'),
713 713
 
714 714
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:144
715 715
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:121
716
-	__( 'Number of Uses', 'event_espresso' ),
716
+	__('Number of Uses', 'event_espresso'),
717 717
 
718 718
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:150
719
-	__( 'Controls the total number of times this ticket can be used, regardless of the number of dates it is assigned to.', 'event_espresso' ),
719
+	__('Controls the total number of times this ticket can be used, regardless of the number of dates it is assigned to.', 'event_espresso'),
720 720
 
721 721
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:154
722
-	__( 'Example: A ticket might have access to 4 different dates, but setting this field to 2 would mean that the ticket could only be used twice. Leave blank for no limit.', 'event_espresso' ),
722
+	__('Example: A ticket might have access to 4 different dates, but setting this field to 2 would mean that the ticket could only be used twice. Leave blank for no limit.', 'event_espresso'),
723 723
 
724 724
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:162
725 725
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:129
726
-	__( 'Minimum Quantity', 'event_espresso' ),
726
+	__('Minimum Quantity', 'event_espresso'),
727 727
 
728 728
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:167
729
-	__( 'The minimum quantity that can be selected for this ticket. Use this to create ticket bundles or graduated pricing.', 'event_espresso' ),
729
+	__('The minimum quantity that can be selected for this ticket. Use this to create ticket bundles or graduated pricing.', 'event_espresso'),
730 730
 
731 731
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:171
732
-	__( 'Leave blank for no minimum.', 'event_espresso' ),
732
+	__('Leave blank for no minimum.', 'event_espresso'),
733 733
 
734 734
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:177
735 735
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:137
736
-	__( 'Maximum Quantity', 'event_espresso' ),
736
+	__('Maximum Quantity', 'event_espresso'),
737 737
 
738 738
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:183
739
-	__( 'The maximum quantity that can be selected for this ticket. Use this to create ticket bundles or graduated pricing.', 'event_espresso' ),
739
+	__('The maximum quantity that can be selected for this ticket. Use this to create ticket bundles or graduated pricing.', 'event_espresso'),
740 740
 
741 741
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:187
742
-	__( 'Leave blank for no maximum.', 'event_espresso' ),
742
+	__('Leave blank for no maximum.', 'event_espresso'),
743 743
 
744 744
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:193
745 745
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/useBulkEditFormConfig.ts:146
746
-	__( 'Required Ticket', 'event_espresso' ),
746
+	__('Required Ticket', 'event_espresso'),
747 747
 
748 748
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:195
749
-	__( 'If enabled, the ticket must be selected and will appear first in frontend ticket lists.', 'event_espresso' ),
749
+	__('If enabled, the ticket must be selected and will appear first in frontend ticket lists.', 'event_espresso'),
750 750
 
751 751
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:209
752
-	__( 'Visibility', 'event_espresso' ),
752
+	__('Visibility', 'event_espresso'),
753 753
 
754 754
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketForm/useTicketFormConfig.ts:211
755
-	__( 'Where the ticket can be viewed throughout the UI.', 'event_espresso' ),
755
+	__('Where the ticket can be viewed throughout the UI.', 'event_espresso'),
756 756
 
757 757
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/TicketsList.tsx:36
758
-	__( 'Available Tickets', 'event_espresso' ),
758
+	__('Available Tickets', 'event_espresso'),
759 759
 
760 760
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/TicketsList.tsx:39
761
-	__( 'loading tickets…', 'event_espresso' ),
761
+	__('loading tickets…', 'event_espresso'),
762 762
 
763 763
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/AssignDatesButton.tsx:26
764
-	__( 'Number of related dates', 'event_espresso' ),
764
+	__('Number of related dates', 'event_espresso'),
765 765
 
766 766
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/AssignDatesButton.tsx:27
767
-	__( 'There are no event dates assigned to this ticket. Please click the calendar icon to update the assignments.', 'event_espresso' ),
767
+	__('There are no event dates assigned to this ticket. Please click the calendar icon to update the assignments.', 'event_espresso'),
768 768
 
769 769
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/AssignDatesButton.tsx:37
770
-	__( 'assign dates', 'event_espresso' ),
770
+	__('assign dates', 'event_espresso'),
771 771
 
772 772
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/DeleteTicket.tsx:18
773
-	__( 'Permanently delete Ticket?', 'event_espresso' ),
773
+	__('Permanently delete Ticket?', 'event_espresso'),
774 774
 
775 775
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/DeleteTicket.tsx:18
776
-	__( 'Move Ticket to Trash?', 'event_espresso' ),
776
+	__('Move Ticket to Trash?', 'event_espresso'),
777 777
 
778 778
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/DeleteTicket.tsx:21
779
-	__( 'Are you sure you want to permanently delete this ticket? This action is permanent and can not be undone.', 'event_espresso' ),
779
+	__('Are you sure you want to permanently delete this ticket? This action is permanent and can not be undone.', 'event_espresso'),
780 780
 
781 781
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/DeleteTicket.tsx:22
782
-	__( 'Are you sure you want to move this ticket to the trash? You can "untrash" this ticket later if you need to.', 'event_espresso' ),
782
+	__('Are you sure you want to move this ticket to the trash? You can "untrash" this ticket later if you need to.', 'event_espresso'),
783 783
 
784 784
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/DeleteTicket.tsx:44
785 785
 	// Reference: packages/ee-components/src/SimpleTicketCard/actions/Trash.tsx:6
786
-	__( 'trash ticket', 'event_espresso' ),
786
+	__('trash ticket', 'event_espresso'),
787 787
 
788 788
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/TicketMainMenu.tsx:25
789
-	__( 'ticket main menu', 'event_espresso' ),
789
+	__('ticket main menu', 'event_espresso'),
790 790
 
791 791
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/TicketMainMenu.tsx:38
792 792
 	// Reference: packages/ee-components/src/SimpleTicketCard/actions/Edit.tsx:15
793
-	__( 'edit ticket', 'event_espresso' ),
793
+	__('edit ticket', 'event_espresso'),
794 794
 
795 795
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/actionsMenu/dropdown/TicketMainMenu.tsx:39
796
-	__( 'copy ticket', 'event_espresso' ),
796
+	__('copy ticket', 'event_espresso'),
797 797
 
798 798
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/actions/Actions.tsx:43
799
-	__( 'edit ticket details', 'event_espresso' ),
799
+	__('edit ticket details', 'event_espresso'),
800 800
 
801 801
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/actions/Actions.tsx:47
802
-	__( 'delete tickets', 'event_espresso' ),
802
+	__('delete tickets', 'event_espresso'),
803 803
 
804 804
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/actions/Actions.tsx:47
805
-	__( 'trash tickets', 'event_espresso' ),
805
+	__('trash tickets', 'event_espresso'),
806 806
 
807 807
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/actions/Actions.tsx:51
808
-	__( 'edit ticket prices', 'event_espresso' ),
808
+	__('edit ticket prices', 'event_espresso'),
809 809
 
810 810
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/delete/Delete.tsx:14
811
-	__( 'Are you sure you want to permanently delete these tickets? This action can NOT be undone!', 'event_espresso' ),
811
+	__('Are you sure you want to permanently delete these tickets? This action can NOT be undone!', 'event_espresso'),
812 812
 
813 813
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/delete/Delete.tsx:15
814
-	__( 'Are you sure you want to trash these tickets?', 'event_espresso' ),
814
+	__('Are you sure you want to trash these tickets?', 'event_espresso'),
815 815
 
816 816
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/delete/Delete.tsx:16
817
-	__( 'Delete tickets permanently', 'event_espresso' ),
817
+	__('Delete tickets permanently', 'event_espresso'),
818 818
 
819 819
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/delete/Delete.tsx:16
820
-	__( 'Trash tickets', 'event_espresso' ),
820
+	__('Trash tickets', 'event_espresso'),
821 821
 
822 822
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/EditDetails.tsx:21
823
-	__( 'Bulk edit ticket details', 'event_espresso' ),
823
+	__('Bulk edit ticket details', 'event_espresso'),
824 824
 
825 825
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/details/EditDetails.tsx:22
826
-	__( 'any changes will be applied to ALL of the selected tickets.', 'event_espresso' ),
826
+	__('any changes will be applied to ALL of the selected tickets.', 'event_espresso'),
827 827
 
828 828
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/EditPrices.tsx:19
829
-	__( 'Bulk edit ticket prices', 'event_espresso' ),
829
+	__('Bulk edit ticket prices', 'event_espresso'),
830 830
 
831 831
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/EditModeButtons.tsx:20
832
-	__( 'Edit all prices together', 'event_espresso' ),
832
+	__('Edit all prices together', 'event_espresso'),
833 833
 
834 834
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/EditModeButtons.tsx:21
835
-	__( 'Edit all the selected ticket prices dynamically', 'event_espresso' ),
835
+	__('Edit all the selected ticket prices dynamically', 'event_espresso'),
836 836
 
837 837
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/EditModeButtons.tsx:25
838
-	__( 'Edit prices individually', 'event_espresso' ),
838
+	__('Edit prices individually', 'event_espresso'),
839 839
 
840 840
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/EditModeButtons.tsx:26
841
-	__( 'Edit prices for each ticket individually', 'event_espresso' ),
841
+	__('Edit prices for each ticket individually', 'event_espresso'),
842 842
 
843 843
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/FooterButtons.tsx:14
844 844
 	// Reference: packages/ee-components/src/bulkEdit/details/Submit.tsx:34
845 845
 	// Reference: packages/form/src/ResetButton.tsx:18
846 846
 	// Reference: packages/tpc/src/buttons/useResetButtonProps.tsx:12
847
-	__( 'Reset', 'event_espresso' ),
847
+	__('Reset', 'event_espresso'),
848 848
 
849 849
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/buttons/FooterButtons.tsx:15
850 850
 	// Reference: packages/tpc/src/hooks/useLockedTicketAction.ts:76
851 851
 	// Reference: packages/ui-components/src/Modal/useCancelButtonProps.tsx:10
852
-	__( 'Cancel', 'event_espresso' ),
852
+	__('Cancel', 'event_espresso'),
853 853
 
854 854
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/bulkEdit/prices/editSeparately/TPCInstance.tsx:26
855 855
 	/* translators: %s ticket name */
856
-	__( 'Edit prices for Ticket: %s', 'event_espresso' ),
856
+	__('Edit prices for Ticket: %s', 'event_espresso'),
857 857
 
858 858
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/TicketCardSidebar.tsx:37
859
-	__( 'Edit Ticket Sale Dates', 'event_espresso' ),
859
+	__('Edit Ticket Sale Dates', 'event_espresso'),
860 860
 
861 861
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/TicketCardSidebar.tsx:41
862
-	__( 'edit ticket sales start and end dates', 'event_espresso' ),
862
+	__('edit ticket sales start and end dates', 'event_espresso'),
863 863
 
864 864
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/TicketDetailsPanel.tsx:28
865
-	__( 'quantity', 'event_espresso' ),
865
+	__('quantity', 'event_espresso'),
866 866
 
867 867
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/cardView/TicketQuantity.tsx:27
868
-	__( 'edit quantity of tickets available…', 'event_espresso' ),
868
+	__('edit quantity of tickets available…', 'event_espresso'),
869 869
 
870 870
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:10
871
-	__( 'Move Ticket to Trash', 'event_espresso' ),
871
+	__('Move Ticket to Trash', 'event_espresso'),
872 872
 
873 873
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:15
874 874
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:52
875
-	__( 'On Sale', 'event_espresso' ),
875
+	__('On Sale', 'event_espresso'),
876 876
 
877 877
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:17
878
-	__( 'Pending', 'event_espresso' ),
878
+	__('Pending', 'event_espresso'),
879 879
 
880 880
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:7
881
-	__( 'Edit Ticket Details', 'event_espresso' ),
881
+	__('Edit Ticket Details', 'event_espresso'),
882 882
 
883 883
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:8
884
-	__( 'Manage Date Assignments', 'event_espresso' ),
884
+	__('Manage Date Assignments', 'event_espresso'),
885 885
 
886 886
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/config.ts:9
887 887
 	// Reference: packages/tpc/src/components/table/Table.tsx:43
888
-	__( 'Ticket Price Calculator', 'event_espresso' ),
888
+	__('Ticket Price Calculator', 'event_espresso'),
889 889
 
890 890
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/editable/EditablePrice.tsx:39
891
-	__( 'edit ticket total…', 'event_espresso' ),
891
+	__('edit ticket total…', 'event_espresso'),
892 892
 
893 893
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/editable/EditablePrice.tsx:53
894
-	__( 'set price…', 'event_espresso' ),
894
+	__('set price…', 'event_espresso'),
895 895
 
896 896
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/IsChainedButton.tsx:23
897
-	__( 'tickets list is linked to dates list and is showing tickets for above dates only', 'event_espresso' ),
897
+	__('tickets list is linked to dates list and is showing tickets for above dates only', 'event_espresso'),
898 898
 
899 899
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/IsChainedButton.tsx:24
900
-	__( 'tickets list is unlinked and is showing tickets for all event dates', 'event_espresso' ),
900
+	__('tickets list is unlinked and is showing tickets for all event dates', 'event_espresso'),
901 901
 
902 902
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:10
903
-	__( 'ticket sales start and end dates', 'event_espresso' ),
903
+	__('ticket sales start and end dates', 'event_espresso'),
904 904
 
905 905
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:15
906
-	__( 'tickets with 90% or more sold', 'event_espresso' ),
906
+	__('tickets with 90% or more sold', 'event_espresso'),
907 907
 
908 908
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:16
909
-	__( 'tickets with 75% or more sold', 'event_espresso' ),
909
+	__('tickets with 75% or more sold', 'event_espresso'),
910 910
 
911 911
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:17
912
-	__( 'tickets with 50% or more sold', 'event_espresso' ),
912
+	__('tickets with 50% or more sold', 'event_espresso'),
913 913
 
914 914
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:19
915
-	__( 'tickets with less than 50% sold', 'event_espresso' ),
915
+	__('tickets with less than 50% sold', 'event_espresso'),
916 916
 
917 917
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:28
918
-	__( 'all tickets for all dates', 'event_espresso' ),
918
+	__('all tickets for all dates', 'event_espresso'),
919 919
 
920 920
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:29
921
-	__( 'all on sale and sale pending', 'event_espresso' ),
921
+	__('all on sale and sale pending', 'event_espresso'),
922 922
 
923 923
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:30
924
-	__( 'on sale tickets only', 'event_espresso' ),
924
+	__('on sale tickets only', 'event_espresso'),
925 925
 
926 926
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:31
927
-	__( 'sale pending tickets only', 'event_espresso' ),
927
+	__('sale pending tickets only', 'event_espresso'),
928 928
 
929 929
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:32
930
-	__( 'next on sale or sale pending only', 'event_espresso' ),
930
+	__('next on sale or sale pending only', 'event_espresso'),
931 931
 
932 932
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:33
933
-	__( 'sold out tickets only', 'event_espresso' ),
933
+	__('sold out tickets only', 'event_espresso'),
934 934
 
935 935
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:34
936
-	__( 'expired tickets only', 'event_espresso' ),
936
+	__('expired tickets only', 'event_espresso'),
937 937
 
938 938
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:35
939
-	__( 'trashed tickets only', 'event_espresso' ),
939
+	__('trashed tickets only', 'event_espresso'),
940 940
 
941 941
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:40
942
-	__( 'all tickets for above dates', 'event_espresso' ),
942
+	__('all tickets for above dates', 'event_espresso'),
943 943
 
944 944
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:44
945
-	__( 'ticket sale date', 'event_espresso' ),
945
+	__('ticket sale date', 'event_espresso'),
946 946
 
947 947
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:45
948
-	__( 'ticket name', 'event_espresso' ),
948
+	__('ticket name', 'event_espresso'),
949 949
 
950 950
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:46
951
-	__( 'ticket ID', 'event_espresso' ),
951
+	__('ticket ID', 'event_espresso'),
952 952
 
953 953
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:52
954
-	__( 'link', 'event_espresso' ),
954
+	__('link', 'event_espresso'),
955 955
 
956 956
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:8
957
-	__( 'ticket sales start date only', 'event_espresso' ),
957
+	__('ticket sales start date only', 'event_espresso'),
958 958
 
959 959
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/filterBar/controls/options.ts:9
960
-	__( 'ticket sales end date only', 'event_espresso' ),
960
+	__('ticket sales end date only', 'event_espresso'),
961 961
 
962 962
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/newTicketOptions/AddSingleTicket.tsx:18
963
-	__( 'Add New Ticket', 'event_espresso' ),
963
+	__('Add New Ticket', 'event_espresso'),
964 964
 
965 965
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/newTicketOptions/AddSingleTicket.tsx:32
966
-	__( 'Add a single ticket and assign the dates to it', 'event_espresso' ),
966
+	__('Add a single ticket and assign the dates to it', 'event_espresso'),
967 967
 
968 968
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/newTicketOptions/AddSingleTicket.tsx:34
969
-	__( 'Single Ticket', 'event_espresso' ),
969
+	__('Single Ticket', 'event_espresso'),
970 970
 
971 971
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/TableView.tsx:39
972
-	__( 'Tickets', 'event_espresso' ),
972
+	__('Tickets', 'event_espresso'),
973 973
 
974 974
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:110
975
-	__( 'Registrations', 'event_espresso' ),
975
+	__('Registrations', 'event_espresso'),
976 976
 
977 977
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:51
978
-	__( 'Goes on Sale', 'event_espresso' ),
978
+	__('Goes on Sale', 'event_espresso'),
979 979
 
980 980
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:65
981
-	__( 'Sale Ends', 'event_espresso' ),
981
+	__('Sale Ends', 'event_espresso'),
982 982
 
983 983
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:66
984
-	__( 'Ends', 'event_espresso' ),
984
+	__('Ends', 'event_espresso'),
985 985
 
986 986
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:78
987
-	__( 'Price', 'event_espresso' ),
987
+	__('Price', 'event_espresso'),
988 988
 
989 989
 	// Reference: domains/core/admin/eventEditor/src/ui/tickets/ticketsList/tableView/useHeaderRowGenerator.tsx:88
990
-	__( 'Quantity', 'event_espresso' ),
990
+	__('Quantity', 'event_espresso'),
991 991
 
992 992
 	// Reference: domains/core/admin/wpPluginsPage/src/exitSurvey/Popup.tsx:29
993
-	__( 'Do you have a moment to share why you are deactivating Event Espresso?', 'event_espresso' ),
993
+	__('Do you have a moment to share why you are deactivating Event Espresso?', 'event_espresso'),
994 994
 
995 995
 	// Reference: domains/core/admin/wpPluginsPage/src/exitSurvey/Popup.tsx:40
996
-	__( 'Skip', 'event_espresso' ),
996
+	__('Skip', 'event_espresso'),
997 997
 
998 998
 	// Reference: domains/core/admin/wpPluginsPage/src/exitSurvey/Popup.tsx:42
999
-	__( 'Sure I\'ll help', 'event_espresso' ),
999
+	__('Sure I\'ll help', 'event_espresso'),
1000 1000
 
1001 1001
 	// Reference: packages/adapters/src/Pagination/Pagination.tsx:23
1002
-	__( 'pagination', 'event_espresso' ),
1002
+	__('pagination', 'event_espresso'),
1003 1003
 
1004 1004
 	// Reference: packages/adapters/src/TagSelector/TagSelector.tsx:112
1005
-	__( 'toggle menu', 'event_espresso' ),
1005
+	__('toggle menu', 'event_espresso'),
1006 1006
 
1007 1007
 	// Reference: packages/constants/src/datetime.ts:10
1008
-	__( 'Postponed', 'event_espresso' ),
1008
+	__('Postponed', 'event_espresso'),
1009 1009
 
1010 1010
 	// Reference: packages/constants/src/datetime.ts:11
1011
-	__( 'SoldOut', 'event_espresso' ),
1011
+	__('SoldOut', 'event_espresso'),
1012 1012
 
1013 1013
 	// Reference: packages/constants/src/datetime.ts:7
1014 1014
 	// Reference: packages/predicates/src/registration/statusOptions.ts:10
1015
-	__( 'Cancelled', 'event_espresso' ),
1015
+	__('Cancelled', 'event_espresso'),
1016 1016
 
1017 1017
 	// Reference: packages/constants/src/datetime.ts:9
1018
-	__( 'Inactive', 'event_espresso' ),
1018
+	__('Inactive', 'event_espresso'),
1019 1019
 
1020 1020
 	// Reference: packages/dates/src/components/DateRangePicker/DateRangePickerLegend.tsx:13
1021
-	__( 'day in range', 'event_espresso' ),
1021
+	__('day in range', 'event_espresso'),
1022 1022
 
1023 1023
 	// Reference: packages/dates/src/components/DateRangePicker/DateRangePickerLegend.tsx:17
1024 1024
 	// Reference: packages/dates/src/components/DateRangePicker/index.tsx:79
1025
-	__( 'end date', 'event_espresso' ),
1025
+	__('end date', 'event_espresso'),
1026 1026
 
1027 1027
 	// Reference: packages/dates/src/components/DateTimePicker.tsx:13
1028 1028
 	// Reference: packages/dates/src/components/TimePicker.tsx:14
1029
-	__( 'time', 'event_espresso' ),
1029
+	__('time', 'event_espresso'),
1030 1030
 
1031 1031
 	// Reference: packages/dates/src/constants.ts:5
1032
-	__( 'End Date & Time must be set later than the Start Date & Time', 'event_espresso' ),
1032
+	__('End Date & Time must be set later than the Start Date & Time', 'event_espresso'),
1033 1033
 
1034 1034
 	// Reference: packages/dates/src/constants.ts:7
1035
-	__( 'Start Date & Time must be set before the End Date & Time', 'event_espresso' ),
1035
+	__('Start Date & Time must be set before the End Date & Time', 'event_espresso'),
1036 1036
 
1037 1037
 	// Reference: packages/dates/src/utils/misc.ts:15
1038
-	__( 'month(s)', 'event_espresso' ),
1038
+	__('month(s)', 'event_espresso'),
1039 1039
 
1040 1040
 	// Reference: packages/dates/src/utils/misc.ts:16
1041
-	__( 'week(s)', 'event_espresso' ),
1041
+	__('week(s)', 'event_espresso'),
1042 1042
 
1043 1043
 	// Reference: packages/dates/src/utils/misc.ts:17
1044
-	__( 'day(s)', 'event_espresso' ),
1044
+	__('day(s)', 'event_espresso'),
1045 1045
 
1046 1046
 	// Reference: packages/dates/src/utils/misc.ts:18
1047
-	__( 'hour(s)', 'event_espresso' ),
1047
+	__('hour(s)', 'event_espresso'),
1048 1048
 
1049 1049
 	// Reference: packages/dates/src/utils/misc.ts:19
1050
-	__( 'minute(s)', 'event_espresso' ),
1050
+	__('minute(s)', 'event_espresso'),
1051 1051
 
1052 1052
 	// Reference: packages/edtr-services/src/apollo/initialization/useCacheRehydration.ts:105
1053
-	__( 'datetimes initialized', 'event_espresso' ),
1053
+	__('datetimes initialized', 'event_espresso'),
1054 1054
 
1055 1055
 	// Reference: packages/edtr-services/src/apollo/initialization/useCacheRehydration.ts:115
1056
-	__( 'tickets initialized', 'event_espresso' ),
1056
+	__('tickets initialized', 'event_espresso'),
1057 1057
 
1058 1058
 	// Reference: packages/edtr-services/src/apollo/initialization/useCacheRehydration.ts:125
1059
-	__( 'prices initialized', 'event_espresso' ),
1059
+	__('prices initialized', 'event_espresso'),
1060 1060
 
1061 1061
 	// Reference: packages/edtr-services/src/apollo/initialization/useCacheRehydration.ts:95
1062
-	__( 'price types initialized', 'event_espresso' ),
1062
+	__('price types initialized', 'event_espresso'),
1063 1063
 
1064 1064
 	// Reference: packages/edtr-services/src/apollo/mutations/useReorderEntities.ts:72
1065
-	__( 'reordering has been applied', 'event_espresso' ),
1065
+	__('reordering has been applied', 'event_espresso'),
1066 1066
 
1067 1067
 	// Reference: packages/edtr-services/src/utils/dateAndTime.ts:38
1068 1068
 	// Reference: packages/ui-components/src/EditDateRangeButton/EditDateRangeButton.tsx:39
1069
-	__( 'End date has been adjusted', 'event_espresso' ),
1069
+	__('End date has been adjusted', 'event_espresso'),
1070 1070
 
1071 1071
 	// Reference: packages/edtr-services/src/utils/dateAndTime.ts:59
1072
-	__( 'Required', 'event_espresso' ),
1072
+	__('Required', 'event_espresso'),
1073 1073
 
1074 1074
 	// Reference: packages/edtr-services/src/utils/dateAndTime.ts:64
1075
-	__( 'Start Date is required', 'event_espresso' ),
1075
+	__('Start Date is required', 'event_espresso'),
1076 1076
 
1077 1077
 	// Reference: packages/edtr-services/src/utils/dateAndTime.ts:68
1078
-	__( 'End Date is required', 'event_espresso' ),
1078
+	__('End Date is required', 'event_espresso'),
1079 1079
 
1080 1080
 	// Reference: packages/ee-components/src/EntityList/EntityList.tsx:31
1081
-	__( 'no results found', 'event_espresso' ),
1081
+	__('no results found', 'event_espresso'),
1082 1082
 
1083 1083
 	// Reference: packages/ee-components/src/EntityList/EntityList.tsx:32
1084
-	__( 'try changing filter settings', 'event_espresso' ),
1084
+	__('try changing filter settings', 'event_espresso'),
1085 1085
 
1086 1086
 	// Reference: packages/ee-components/src/SimpleTicketCard/SimpleTicketCard.tsx:27
1087 1087
 	// Reference: packages/ui-components/src/CalendarDateSwitcher/CalendarDateSwitcher.tsx:34
1088
-	__( 'starts', 'event_espresso' ),
1088
+	__('starts', 'event_espresso'),
1089 1089
 
1090 1090
 	// Reference: packages/ee-components/src/SimpleTicketCard/SimpleTicketCard.tsx:34
1091 1091
 	// Reference: packages/ui-components/src/CalendarDateSwitcher/CalendarDateSwitcher.tsx:47
1092
-	__( 'ends', 'event_espresso' ),
1092
+	__('ends', 'event_espresso'),
1093 1093
 
1094 1094
 	// Reference: packages/ee-components/src/bulkEdit/ActionCheckbox.tsx:38
1095 1095
 	/* translators: %d entity id */
1096
-	__( 'select entity with id %d', 'event_espresso' ),
1096
+	__('select entity with id %d', 'event_espresso'),
1097 1097
 
1098 1098
 	// Reference: packages/ee-components/src/bulkEdit/ActionCheckbox.tsx:41
1099
-	__( 'select all entities', 'event_espresso' ),
1099
+	__('select all entities', 'event_espresso'),
1100 1100
 
1101 1101
 	// Reference: packages/ee-components/src/bulkEdit/details/BulkEditDetails.tsx:20
1102
-	__( 'Note: ', 'event_espresso' ),
1102
+	__('Note: ', 'event_espresso'),
1103 1103
 
1104 1104
 	// Reference: packages/ee-components/src/bulkEdit/details/BulkEditDetails.tsx:20
1105
-	__( 'any changes will be applied to ALL of the selected entities.', 'event_espresso' ),
1105
+	__('any changes will be applied to ALL of the selected entities.', 'event_espresso'),
1106 1106
 
1107 1107
 	// Reference: packages/ee-components/src/bulkEdit/details/BulkEditDetails.tsx:26
1108
-	__( 'Bulk edit details', 'event_espresso' ),
1108
+	__('Bulk edit details', 'event_espresso'),
1109 1109
 
1110 1110
 	// Reference: packages/ee-components/src/bulkEdit/details/Submit.tsx:17
1111
-	__( 'Are you sure you want to bulk update the details?', 'event_espresso' ),
1111
+	__('Are you sure you want to bulk update the details?', 'event_espresso'),
1112 1112
 
1113 1113
 	// Reference: packages/ee-components/src/bulkEdit/details/Submit.tsx:18
1114
-	__( 'Bulk update details', 'event_espresso' ),
1114
+	__('Bulk update details', 'event_espresso'),
1115 1115
 
1116 1116
 	// Reference: packages/ee-components/src/filterBar/SortByControl/index.tsx:26
1117
-	__( 'reorder dates', 'event_espresso' ),
1117
+	__('reorder dates', 'event_espresso'),
1118 1118
 
1119 1119
 	// Reference: packages/ee-components/src/filterBar/SortByControl/index.tsx:26
1120
-	__( 'reorder tickets', 'event_espresso' ),
1120
+	__('reorder tickets', 'event_espresso'),
1121 1121
 
1122 1122
 	// Reference: packages/form-builder/src/FormElement/FormElementToolbar.tsx:32
1123
-	__( 'delete form element', 'event_espresso' ),
1123
+	__('delete form element', 'event_espresso'),
1124 1124
 
1125 1125
 	// Reference: packages/form-builder/src/FormElement/FormElementToolbar.tsx:49
1126
-	__( 'form element settings', 'event_espresso' ),
1126
+	__('form element settings', 'event_espresso'),
1127 1127
 
1128 1128
 	// Reference: packages/form-builder/src/FormElement/FormElementToolbar.tsx:59
1129
-	__( 'copy form element', 'event_espresso' ),
1129
+	__('copy form element', 'event_espresso'),
1130 1130
 
1131 1131
 	// Reference: packages/form-builder/src/FormElement/FormElementToolbar.tsx:69
1132
-	__( 'click, hold, and drag to reorder form element', 'event_espresso' ),
1132
+	__('click, hold, and drag to reorder form element', 'event_espresso'),
1133 1133
 
1134 1134
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOption.tsx:20
1135
-	__( 'remove option', 'event_espresso' ),
1135
+	__('remove option', 'event_espresso'),
1136 1136
 
1137 1137
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOption.tsx:42
1138
-	__( 'value', 'event_espresso' ),
1138
+	__('value', 'event_espresso'),
1139 1139
 
1140 1140
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOption.tsx:52
1141
-	__( 'label', 'event_espresso' ),
1141
+	__('label', 'event_espresso'),
1142 1142
 
1143 1143
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOption.tsx:63
1144
-	__( 'click, hold, and drag to reorder field option', 'event_espresso' ),
1144
+	__('click, hold, and drag to reorder field option', 'event_espresso'),
1145 1145
 
1146 1146
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOptions.tsx:61
1147
-	__( 'Options are the choices you give people to select from.', 'event_espresso' ),
1147
+	__('Options are the choices you give people to select from.', 'event_espresso'),
1148 1148
 
1149 1149
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOptions.tsx:63
1150
-	__( 'The value is a simple key that will be saved to the database and the label is what is shown to the user.', 'event_espresso' ),
1150
+	__('The value is a simple key that will be saved to the database and the label is what is shown to the user.', 'event_espresso'),
1151 1151
 
1152 1152
 	// Reference: packages/form-builder/src/FormElement/Tabs/FieldOptions.tsx:96
1153
-	__( 'add new option', 'event_espresso' ),
1153
+	__('add new option', 'event_espresso'),
1154 1154
 
1155 1155
 	// Reference: packages/form-builder/src/FormElement/Tabs/FormElementTabs.tsx:22
1156 1156
 	// Reference: packages/form-builder/src/FormSection/Tabs/FormSectionTabs.tsx:21
1157
-	__( 'Settings', 'event_espresso' ),
1157
+	__('Settings', 'event_espresso'),
1158 1158
 
1159 1159
 	// Reference: packages/form-builder/src/FormElement/Tabs/FormElementTabs.tsx:26
1160 1160
 	// Reference: packages/form-builder/src/FormSection/Tabs/FormSectionTabs.tsx:25
1161
-	__( 'Styles', 'event_espresso' ),
1161
+	__('Styles', 'event_espresso'),
1162 1162
 
1163 1163
 	// Reference: packages/form-builder/src/FormElement/Tabs/FormElementTabs.tsx:30
1164
-	__( 'Validation', 'event_espresso' ),
1164
+	__('Validation', 'event_espresso'),
1165 1165
 
1166 1166
 	// Reference: packages/form-builder/src/FormElement/Tabs/FormElementTabs.tsx:34
1167 1167
 	// Reference: packages/form-builder/src/FormSection/Tabs/FormSectionTabs.tsx:29
1168
-	__( 'Rules', 'event_espresso' ),
1168
+	__('Rules', 'event_espresso'),
1169 1169
 
1170 1170
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:18
1171 1171
 	// Reference: packages/form-builder/src/FormSection/Tabs/Settings.tsx:19
1172
-	__( 'admin label', 'event_espresso' ),
1172
+	__('admin label', 'event_espresso'),
1173 1173
 
1174 1174
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:23
1175
-	__( 'admin only', 'event_espresso' ),
1175
+	__('admin only', 'event_espresso'),
1176 1176
 
1177 1177
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:30
1178
-	__( 'content', 'event_espresso' ),
1178
+	__('content', 'event_espresso'),
1179 1179
 
1180 1180
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:39
1181 1181
 	// Reference: packages/form-builder/src/FormSection/Tabs/Settings.tsx:14
1182
-	__( 'public label', 'event_espresso' ),
1182
+	__('public label', 'event_espresso'),
1183 1183
 
1184 1184
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:44
1185
-	__( 'options', 'event_espresso' ),
1185
+	__('options', 'event_espresso'),
1186 1186
 
1187 1187
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:47
1188
-	__( 'placeholder', 'event_espresso' ),
1188
+	__('placeholder', 'event_espresso'),
1189 1189
 
1190 1190
 	// Reference: packages/form-builder/src/FormElement/Tabs/Settings.tsx:52
1191
-	__( 'help text', 'event_espresso' ),
1191
+	__('help text', 'event_espresso'),
1192 1192
 
1193 1193
 	// Reference: packages/form-builder/src/FormElement/Tabs/Styles.tsx:14
1194 1194
 	// Reference: packages/form-builder/src/FormSection/Tabs/Styles.tsx:13
1195
-	__( 'css class', 'event_espresso' ),
1195
+	__('css class', 'event_espresso'),
1196 1196
 
1197 1197
 	// Reference: packages/form-builder/src/FormElement/Tabs/Styles.tsx:19
1198
-	__( 'help text css class', 'event_espresso' ),
1198
+	__('help text css class', 'event_espresso'),
1199 1199
 
1200 1200
 	// Reference: packages/form-builder/src/FormElement/Tabs/Validation.tsx:16
1201
-	__( 'required', 'event_espresso' ),
1201
+	__('required', 'event_espresso'),
1202 1202
 
1203 1203
 	// Reference: packages/form-builder/src/FormElement/Tabs/Validation.tsx:21
1204
-	__( 'required text', 'event_espresso' ),
1204
+	__('required text', 'event_espresso'),
1205 1205
 
1206 1206
 	// Reference: packages/form-builder/src/FormElement/Tabs/Validation.tsx:28
1207
-	__( 'min', 'event_espresso' ),
1207
+	__('min', 'event_espresso'),
1208 1208
 
1209 1209
 	// Reference: packages/form-builder/src/FormElement/Tabs/Validation.tsx:33
1210
-	__( 'max', 'event_espresso' ),
1210
+	__('max', 'event_espresso'),
1211 1211
 
1212 1212
 	// Reference: packages/form-builder/src/FormSection/AddFormElementPopover.tsx:114
1213
-	__( 'add new form element', 'event_espresso' ),
1213
+	__('add new form element', 'event_espresso'),
1214 1214
 
1215 1215
 	// Reference: packages/form-builder/src/FormSection/AddFormElementPopover.tsx:121
1216 1216
 	// Reference: packages/form/src/renderers/RepeatableRenderer.tsx:52
1217 1217
 	// Reference: packages/ui-components/src/SimpleEntityList/EntityTemplate.tsx:27
1218
-	__( 'Add', 'event_espresso' ),
1218
+	__('Add', 'event_espresso'),
1219 1219
 
1220 1220
 	// Reference: packages/form-builder/src/FormSection/AddFormElementPopover.tsx:81
1221
-	__( 'Add Form Element', 'event_espresso' ),
1221
+	__('Add Form Element', 'event_espresso'),
1222 1222
 
1223 1223
 	// Reference: packages/form-builder/src/FormSection/AddFormElementPopover.tsx:90
1224
-	__( 'form element order can be changed after adding by using the drag handles in the form element toolbar', 'event_espresso' ),
1224
+	__('form element order can be changed after adding by using the drag handles in the form element toolbar', 'event_espresso'),
1225 1225
 
1226 1226
 	// Reference: packages/form-builder/src/FormSection/AddFormElementPopover.tsx:97
1227
-	__( 'load existing form section', 'event_espresso' ),
1227
+	__('load existing form section', 'event_espresso'),
1228 1228
 
1229 1229
 	// Reference: packages/form-builder/src/FormSection/FormSectionToolbar.tsx:32
1230
-	__( 'delete form section', 'event_espresso' ),
1230
+	__('delete form section', 'event_espresso'),
1231 1231
 
1232 1232
 	// Reference: packages/form-builder/src/FormSection/FormSectionToolbar.tsx:47
1233
-	__( 'form section settings', 'event_espresso' ),
1233
+	__('form section settings', 'event_espresso'),
1234 1234
 
1235 1235
 	// Reference: packages/form-builder/src/FormSection/FormSectionToolbar.tsx:57
1236
-	__( 'copy form section', 'event_espresso' ),
1236
+	__('copy form section', 'event_espresso'),
1237 1237
 
1238 1238
 	// Reference: packages/form-builder/src/FormSection/FormSectionToolbar.tsx:68
1239
-	__( 'click, hold, and drag to reorder form section', 'event_espresso' ),
1239
+	__('click, hold, and drag to reorder form section', 'event_espresso'),
1240 1240
 
1241 1241
 	// Reference: packages/form-builder/src/FormSection/SaveSection.tsx:43
1242
-	__( 'save form section for use in other forms', 'event_espresso' ),
1242
+	__('save form section for use in other forms', 'event_espresso'),
1243 1243
 
1244 1244
 	// Reference: packages/form-builder/src/FormSection/SaveSection.tsx:47
1245
-	__( 'save as', 'event_espresso' ),
1245
+	__('save as', 'event_espresso'),
1246 1246
 
1247 1247
 	// Reference: packages/form-builder/src/FormSection/SaveSection.tsx:51
1248
-	__( 'default', 'event_espresso' ),
1248
+	__('default', 'event_espresso'),
1249 1249
 
1250 1250
 	// Reference: packages/form-builder/src/FormSection/SaveSection.tsx:54
1251
-	__( ' a copy of this form section will be automatically added to ALL new events', 'event_espresso' ),
1251
+	__(' a copy of this form section will be automatically added to ALL new events', 'event_espresso'),
1252 1252
 
1253 1253
 	// Reference: packages/form-builder/src/FormSection/SaveSection.tsx:57
1254
-	__( 'shared', 'event_espresso' ),
1254
+	__('shared', 'event_espresso'),
1255 1255
 
1256 1256
 	// Reference: packages/form-builder/src/FormSection/SaveSection.tsx:60
1257
-	__( 'a copy of this form section will be saved for use in other events but not loaded by default', 'event_espresso' ),
1257
+	__('a copy of this form section will be saved for use in other events but not loaded by default', 'event_espresso'),
1258 1258
 
1259 1259
 	// Reference: packages/form-builder/src/FormSection/Tabs/Settings.tsx:24
1260
-	__( 'show label', 'event_espresso' ),
1260
+	__('show label', 'event_espresso'),
1261 1261
 
1262 1262
 	// Reference: packages/form-builder/src/constants.ts:100
1263
-	__( 'Week', 'event_espresso' ),
1263
+	__('Week', 'event_espresso'),
1264 1264
 
1265 1265
 	// Reference: packages/form-builder/src/constants.ts:102
1266
-	__( 'adds a text input that allows users to enter a week and year directly via keyboard or a datepicker', 'event_espresso' ),
1266
+	__('adds a text input that allows users to enter a week and year directly via keyboard or a datepicker', 'event_espresso'),
1267 1267
 
1268 1268
 	// Reference: packages/form-builder/src/constants.ts:105
1269
-	__( 'Day Selector', 'event_espresso' ),
1269
+	__('Day Selector', 'event_espresso'),
1270 1270
 
1271 1271
 	// Reference: packages/form-builder/src/constants.ts:107
1272
-	__( 'adds a dropdown selector that allows users to select the day of the month (01 to 31)', 'event_espresso' ),
1272
+	__('adds a dropdown selector that allows users to select the day of the month (01 to 31)', 'event_espresso'),
1273 1273
 
1274 1274
 	// Reference: packages/form-builder/src/constants.ts:110
1275
-	__( 'Month Selector', 'event_espresso' ),
1275
+	__('Month Selector', 'event_espresso'),
1276 1276
 
1277 1277
 	// Reference: packages/form-builder/src/constants.ts:112
1278
-	__( 'adds a dropdown selector that allows users to select the month of the year (01 to 12)', 'event_espresso' ),
1278
+	__('adds a dropdown selector that allows users to select the month of the year (01 to 12)', 'event_espresso'),
1279 1279
 
1280 1280
 	// Reference: packages/form-builder/src/constants.ts:115
1281
-	__( 'Year Selector', 'event_espresso' ),
1281
+	__('Year Selector', 'event_espresso'),
1282 1282
 
1283 1283
 	// Reference: packages/form-builder/src/constants.ts:117
1284
-	__( 'adds a dropdown selector that allows users to select the year from a configurable range', 'event_espresso' ),
1284
+	__('adds a dropdown selector that allows users to select the year from a configurable range', 'event_espresso'),
1285 1285
 
1286 1286
 	// Reference: packages/form-builder/src/constants.ts:120
1287
-	__( 'Radio Buttons', 'event_espresso' ),
1287
+	__('Radio Buttons', 'event_espresso'),
1288 1288
 
1289 1289
 	// Reference: packages/form-builder/src/constants.ts:122
1290
-	__( 'adds one or more radio buttons that allow users to only select one option from those provided', 'event_espresso' ),
1290
+	__('adds one or more radio buttons that allow users to only select one option from those provided', 'event_espresso'),
1291 1291
 
1292 1292
 	// Reference: packages/form-builder/src/constants.ts:125
1293
-	__( 'Decimal Number', 'event_espresso' ),
1293
+	__('Decimal Number', 'event_espresso'),
1294 1294
 
1295 1295
 	// Reference: packages/form-builder/src/constants.ts:127
1296
-	__( 'adds a text input that only accepts numbers whose value is a decimal (float)', 'event_espresso' ),
1296
+	__('adds a text input that only accepts numbers whose value is a decimal (float)', 'event_espresso'),
1297 1297
 
1298 1298
 	// Reference: packages/form-builder/src/constants.ts:130
1299
-	__( 'Whole Number', 'event_espresso' ),
1299
+	__('Whole Number', 'event_espresso'),
1300 1300
 
1301 1301
 	// Reference: packages/form-builder/src/constants.ts:132
1302
-	__( 'adds a text input that only accepts numbers whose value is an integer (whole number)', 'event_espresso' ),
1302
+	__('adds a text input that only accepts numbers whose value is an integer (whole number)', 'event_espresso'),
1303 1303
 
1304 1304
 	// Reference: packages/form-builder/src/constants.ts:135
1305
-	__( 'Number Range', 'event_espresso' ),
1305
+	__('Number Range', 'event_espresso'),
1306 1306
 
1307 1307
 	// Reference: packages/form-builder/src/constants.ts:140
1308
-	__( 'Phone Number', 'event_espresso' ),
1308
+	__('Phone Number', 'event_espresso'),
1309 1309
 
1310 1310
 	// Reference: packages/form-builder/src/constants.ts:145
1311
-	__( 'Dropdown', 'event_espresso' ),
1311
+	__('Dropdown', 'event_espresso'),
1312 1312
 
1313 1313
 	// Reference: packages/form-builder/src/constants.ts:147
1314
-	__( 'adds a dropdown selector that accepts a single value', 'event_espresso' ),
1314
+	__('adds a dropdown selector that accepts a single value', 'event_espresso'),
1315 1315
 
1316 1316
 	// Reference: packages/form-builder/src/constants.ts:150
1317
-	__( 'Multi Select', 'event_espresso' ),
1317
+	__('Multi Select', 'event_espresso'),
1318 1318
 
1319 1319
 	// Reference: packages/form-builder/src/constants.ts:152
1320
-	__( 'adds a dropdown selector that accepts multiple values', 'event_espresso' ),
1320
+	__('adds a dropdown selector that accepts multiple values', 'event_espresso'),
1321 1321
 
1322 1322
 	// Reference: packages/form-builder/src/constants.ts:155
1323
-	__( 'Toggle/Switch', 'event_espresso' ),
1323
+	__('Toggle/Switch', 'event_espresso'),
1324 1324
 
1325 1325
 	// Reference: packages/form-builder/src/constants.ts:157
1326
-	__( 'adds a toggle or a switch to accept true or false value', 'event_espresso' ),
1326
+	__('adds a toggle or a switch to accept true or false value', 'event_espresso'),
1327 1327
 
1328 1328
 	// Reference: packages/form-builder/src/constants.ts:160
1329
-	__( 'Multi Checkbox', 'event_espresso' ),
1329
+	__('Multi Checkbox', 'event_espresso'),
1330 1330
 
1331 1331
 	// Reference: packages/form-builder/src/constants.ts:162
1332
-	__( 'adds checkboxes that allow users to select zero or more options from those provided', 'event_espresso' ),
1332
+	__('adds checkboxes that allow users to select zero or more options from those provided', 'event_espresso'),
1333 1333
 
1334 1334
 	// Reference: packages/form-builder/src/constants.ts:165
1335
-	__( 'Country Selector', 'event_espresso' ),
1335
+	__('Country Selector', 'event_espresso'),
1336 1336
 
1337 1337
 	// Reference: packages/form-builder/src/constants.ts:167
1338
-	__( 'adds a dropdown selector populated with names of countries that are enabled for the site', 'event_espresso' ),
1338
+	__('adds a dropdown selector populated with names of countries that are enabled for the site', 'event_espresso'),
1339 1339
 
1340 1340
 	// Reference: packages/form-builder/src/constants.ts:170
1341
-	__( 'State Selector', 'event_espresso' ),
1341
+	__('State Selector', 'event_espresso'),
1342 1342
 
1343 1343
 	// Reference: packages/form-builder/src/constants.ts:175
1344
-	__( 'Button', 'event_espresso' ),
1344
+	__('Button', 'event_espresso'),
1345 1345
 
1346 1346
 	// Reference: packages/form-builder/src/constants.ts:177
1347
-	__( 'adds a button to the form that can be used for triggering fucntionality (requires custom coding)', 'event_espresso' ),
1347
+	__('adds a button to the form that can be used for triggering fucntionality (requires custom coding)', 'event_espresso'),
1348 1348
 
1349 1349
 	// Reference: packages/form-builder/src/constants.ts:180
1350
-	__( 'Reset Button', 'event_espresso' ),
1350
+	__('Reset Button', 'event_espresso'),
1351 1351
 
1352 1352
 	// Reference: packages/form-builder/src/constants.ts:182
1353
-	__( 'adds a button that will reset the form back to its orginial state.', 'event_espresso' ),
1353
+	__('adds a button that will reset the form back to its orginial state.', 'event_espresso'),
1354 1354
 
1355 1355
 	// Reference: packages/form-builder/src/constants.ts:33
1356
-	__( 'Form Section', 'event_espresso' ),
1356
+	__('Form Section', 'event_espresso'),
1357 1357
 
1358 1358
 	// Reference: packages/form-builder/src/constants.ts:35
1359
-	__( 'Used for creating logical groupings for questions and form elements. Need to add a heading or description? Use the HTML form element.', 'event_espresso' ),
1359
+	__('Used for creating logical groupings for questions and form elements. Need to add a heading or description? Use the HTML form element.', 'event_espresso'),
1360 1360
 
1361 1361
 	// Reference: packages/form-builder/src/constants.ts:40
1362
-	__( 'HTML Block', 'event_espresso' ),
1362
+	__('HTML Block', 'event_espresso'),
1363 1363
 
1364 1364
 	// Reference: packages/form-builder/src/constants.ts:42
1365
-	__( 'allows you to add HTML like headings or text paragraphs to your form', 'event_espresso' ),
1365
+	__('allows you to add HTML like headings or text paragraphs to your form', 'event_espresso'),
1366 1366
 
1367 1367
 	// Reference: packages/form-builder/src/constants.ts:45
1368
-	__( 'Text Input', 'event_espresso' ),
1368
+	__('Text Input', 'event_espresso'),
1369 1369
 
1370 1370
 	// Reference: packages/form-builder/src/constants.ts:47
1371
-	__( 'adds a text input that only accepts plain text', 'event_espresso' ),
1371
+	__('adds a text input that only accepts plain text', 'event_espresso'),
1372 1372
 
1373 1373
 	// Reference: packages/form-builder/src/constants.ts:50
1374
-	__( 'Plain Text Area', 'event_espresso' ),
1374
+	__('Plain Text Area', 'event_espresso'),
1375 1375
 
1376 1376
 	// Reference: packages/form-builder/src/constants.ts:52
1377
-	__( 'adds a textarea block that only accepts plain text', 'event_espresso' ),
1377
+	__('adds a textarea block that only accepts plain text', 'event_espresso'),
1378 1378
 
1379 1379
 	// Reference: packages/form-builder/src/constants.ts:55
1380
-	__( 'HTML Text Area', 'event_espresso' ),
1380
+	__('HTML Text Area', 'event_espresso'),
1381 1381
 
1382 1382
 	// Reference: packages/form-builder/src/constants.ts:57
1383
-	__( 'adds a textarea block that accepts text including simple HTML markup', 'event_espresso' ),
1383
+	__('adds a textarea block that accepts text including simple HTML markup', 'event_espresso'),
1384 1384
 
1385 1385
 	// Reference: packages/form-builder/src/constants.ts:60
1386
-	__( 'Email Address', 'event_espresso' ),
1386
+	__('Email Address', 'event_espresso'),
1387 1387
 
1388 1388
 	// Reference: packages/form-builder/src/constants.ts:62
1389
-	__( 'adds a text input that only accets a valid email address', 'event_espresso' ),
1389
+	__('adds a text input that only accets a valid email address', 'event_espresso'),
1390 1390
 
1391 1391
 	// Reference: packages/form-builder/src/constants.ts:65
1392
-	__( 'Email Confirmation', 'event_espresso' ),
1392
+	__('Email Confirmation', 'event_espresso'),
1393 1393
 
1394 1394
 	// Reference: packages/form-builder/src/constants.ts:70
1395
-	__( 'Password', 'event_espresso' ),
1395
+	__('Password', 'event_espresso'),
1396 1396
 
1397 1397
 	// Reference: packages/form-builder/src/constants.ts:72
1398
-	__( 'adds a text input that accepts text but masks what the user enters', 'event_espresso' ),
1398
+	__('adds a text input that accepts text but masks what the user enters', 'event_espresso'),
1399 1399
 
1400 1400
 	// Reference: packages/form-builder/src/constants.ts:75
1401
-	__( 'URL', 'event_espresso' ),
1401
+	__('URL', 'event_espresso'),
1402 1402
 
1403 1403
 	// Reference: packages/form-builder/src/constants.ts:77
1404
-	__( 'adds a text input for entering a URL address', 'event_espresso' ),
1404
+	__('adds a text input for entering a URL address', 'event_espresso'),
1405 1405
 
1406 1406
 	// Reference: packages/form-builder/src/constants.ts:80
1407
-	__( 'Date', 'event_espresso' ),
1407
+	__('Date', 'event_espresso'),
1408 1408
 
1409 1409
 	// Reference: packages/form-builder/src/constants.ts:82
1410
-	__( 'adds a text input that allows users to enter a date directly via keyboard or a datepicker', 'event_espresso' ),
1410
+	__('adds a text input that allows users to enter a date directly via keyboard or a datepicker', 'event_espresso'),
1411 1411
 
1412 1412
 	// Reference: packages/form-builder/src/constants.ts:85
1413
-	__( 'Local Date', 'event_espresso' ),
1413
+	__('Local Date', 'event_espresso'),
1414 1414
 
1415 1415
 	// Reference: packages/form-builder/src/constants.ts:90
1416
-	__( 'Month', 'event_espresso' ),
1416
+	__('Month', 'event_espresso'),
1417 1417
 
1418 1418
 	// Reference: packages/form-builder/src/constants.ts:92
1419
-	__( 'adds a text input that allows users to enter a month and year directly via keyboard or a datepicker', 'event_espresso' ),
1419
+	__('adds a text input that allows users to enter a month and year directly via keyboard or a datepicker', 'event_espresso'),
1420 1420
 
1421 1421
 	// Reference: packages/form-builder/src/constants.ts:95
1422
-	__( 'Time', 'event_espresso' ),
1422
+	__('Time', 'event_espresso'),
1423 1423
 
1424 1424
 	// Reference: packages/form-builder/src/constants.ts:97
1425
-	__( 'adds a text input that allows users to enter a time directly via keyboard or a timepicker', 'event_espresso' ),
1425
+	__('adds a text input that allows users to enter a time directly via keyboard or a timepicker', 'event_espresso'),
1426 1426
 
1427 1427
 	// Reference: packages/form/src/adapters/WPMediaImage.tsx:12
1428
-	__( 'Select Image', 'event_espresso' ),
1428
+	__('Select Image', 'event_espresso'),
1429 1429
 
1430 1430
 	// Reference: packages/form/src/adapters/WPMediaImage.tsx:44
1431 1431
 	// Reference: packages/rich-text-editor/src/components/AdvancedTextEditor/toolbarButtons/WPMedia.tsx:11
1432 1432
 	// Reference: packages/rich-text-editor/src/rte-old/components/toolbarButtons/WPMedia.tsx:12
1433
-	__( 'Select', 'event_espresso' ),
1433
+	__('Select', 'event_espresso'),
1434 1434
 
1435 1435
 	// Reference: packages/form/src/renderers/RepeatableRenderer.tsx:36
1436 1436
 	/* translators: %d the entry number */
1437
-	__( 'Entry %d', 'event_espresso' ),
1437
+	__('Entry %d', 'event_espresso'),
1438 1438
 
1439 1439
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:11
1440 1440
 	// Reference: packages/helpers/src/tickets/getStatusTextLabel.ts:17
1441
-	__( 'sold out', 'event_espresso' ),
1441
+	__('sold out', 'event_espresso'),
1442 1442
 
1443 1443
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:14
1444 1444
 	// Reference: packages/helpers/src/tickets/getStatusTextLabel.ts:14
1445
-	__( 'expired', 'event_espresso' ),
1445
+	__('expired', 'event_espresso'),
1446 1446
 
1447 1447
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:17
1448
-	__( 'upcoming', 'event_espresso' ),
1448
+	__('upcoming', 'event_espresso'),
1449 1449
 
1450 1450
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:20
1451
-	__( 'active', 'event_espresso' ),
1451
+	__('active', 'event_espresso'),
1452 1452
 
1453 1453
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:23
1454 1454
 	// Reference: packages/helpers/src/tickets/getStatusTextLabel.ts:11
1455
-	__( 'trashed', 'event_espresso' ),
1455
+	__('trashed', 'event_espresso'),
1456 1456
 
1457 1457
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:26
1458
-	__( 'cancelled', 'event_espresso' ),
1458
+	__('cancelled', 'event_espresso'),
1459 1459
 
1460 1460
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:29
1461
-	__( 'postponed', 'event_espresso' ),
1461
+	__('postponed', 'event_espresso'),
1462 1462
 
1463 1463
 	// Reference: packages/helpers/src/datetimes/getStatusTextLabel.ts:33
1464
-	__( 'inactive', 'event_espresso' ),
1464
+	__('inactive', 'event_espresso'),
1465 1465
 
1466 1466
 	// Reference: packages/helpers/src/tickets/getStatusTextLabel.ts:20
1467
-	__( 'pending', 'event_espresso' ),
1467
+	__('pending', 'event_espresso'),
1468 1468
 
1469 1469
 	// Reference: packages/helpers/src/tickets/getStatusTextLabel.ts:23
1470
-	__( 'on sale', 'event_espresso' ),
1470
+	__('on sale', 'event_espresso'),
1471 1471
 
1472 1472
 	// Reference: packages/predicates/src/registration/statusOptions.ts:14
1473
-	__( 'Declined', 'event_espresso' ),
1473
+	__('Declined', 'event_espresso'),
1474 1474
 
1475 1475
 	// Reference: packages/predicates/src/registration/statusOptions.ts:18
1476
-	__( 'Incomplete', 'event_espresso' ),
1476
+	__('Incomplete', 'event_espresso'),
1477 1477
 
1478 1478
 	// Reference: packages/predicates/src/registration/statusOptions.ts:22
1479
-	__( 'Not Approved', 'event_espresso' ),
1479
+	__('Not Approved', 'event_espresso'),
1480 1480
 
1481 1481
 	// Reference: packages/predicates/src/registration/statusOptions.ts:26
1482
-	__( 'Pending Payment', 'event_espresso' ),
1482
+	__('Pending Payment', 'event_espresso'),
1483 1483
 
1484 1484
 	// Reference: packages/predicates/src/registration/statusOptions.ts:30
1485
-	__( 'Wait List', 'event_espresso' ),
1485
+	__('Wait List', 'event_espresso'),
1486 1486
 
1487 1487
 	// Reference: packages/predicates/src/registration/statusOptions.ts:6
1488
-	__( 'Approved', 'event_espresso' ),
1488
+	__('Approved', 'event_espresso'),
1489 1489
 
1490 1490
 	// Reference: packages/rich-text-editor/src/components/AdvancedTextEditor/toolbarButtons/WPMedia.tsx:9
1491 1491
 	// Reference: packages/rich-text-editor/src/rte-old/components/toolbarButtons/WPMedia.tsx:10
1492
-	__( 'Select media', 'event_espresso' ),
1492
+	__('Select media', 'event_espresso'),
1493 1493
 
1494 1494
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/RichTextEditor.tsx:84
1495
-	__( 'Write something…', 'event_espresso' ),
1495
+	__('Write something…', 'event_espresso'),
1496 1496
 
1497 1497
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/Toolbar.tsx:20
1498
-	__( 'RTE Toolbar', 'event_espresso' ),
1498
+	__('RTE Toolbar', 'event_espresso'),
1499 1499
 
1500 1500
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:11
1501
-	__( 'Normal', 'event_espresso' ),
1501
+	__('Normal', 'event_espresso'),
1502 1502
 
1503 1503
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:12
1504
-	__( 'H1', 'event_espresso' ),
1504
+	__('H1', 'event_espresso'),
1505 1505
 
1506 1506
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:13
1507
-	__( 'H2', 'event_espresso' ),
1507
+	__('H2', 'event_espresso'),
1508 1508
 
1509 1509
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:14
1510
-	__( 'H3', 'event_espresso' ),
1510
+	__('H3', 'event_espresso'),
1511 1511
 
1512 1512
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:15
1513
-	__( 'H4', 'event_espresso' ),
1513
+	__('H4', 'event_espresso'),
1514 1514
 
1515 1515
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:16
1516
-	__( 'H5', 'event_espresso' ),
1516
+	__('H5', 'event_espresso'),
1517 1517
 
1518 1518
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:17
1519
-	__( 'H6', 'event_espresso' ),
1519
+	__('H6', 'event_espresso'),
1520 1520
 
1521 1521
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:18
1522
-	__( 'Block quote', 'event_espresso' ),
1522
+	__('Block quote', 'event_espresso'),
1523 1523
 
1524 1524
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/blockType/Component.tsx:19
1525
-	__( 'Code', 'event_espresso' ),
1525
+	__('Code', 'event_espresso'),
1526 1526
 
1527 1527
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/colorPicker/Component.tsx:36
1528
-	__( 'Set color', 'event_espresso' ),
1528
+	__('Set color', 'event_espresso'),
1529 1529
 
1530 1530
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/colorPicker/Component.tsx:45
1531
-	__( 'Text color', 'event_espresso' ),
1531
+	__('Text color', 'event_espresso'),
1532 1532
 
1533 1533
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/colorPicker/Component.tsx:47
1534
-	__( 'Background color', 'event_espresso' ),
1534
+	__('Background color', 'event_espresso'),
1535 1535
 
1536 1536
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/image/Component.tsx:39
1537
-	__( 'Add image', 'event_espresso' ),
1537
+	__('Add image', 'event_espresso'),
1538 1538
 
1539 1539
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/image/Component.tsx:51
1540
-	__( 'Image URL', 'event_espresso' ),
1540
+	__('Image URL', 'event_espresso'),
1541 1541
 
1542 1542
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/image/Component.tsx:55
1543
-	__( 'Alt text', 'event_espresso' ),
1543
+	__('Alt text', 'event_espresso'),
1544 1544
 
1545 1545
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/image/Component.tsx:56
1546
-	__( 'Width', 'event_espresso' ),
1546
+	__('Width', 'event_espresso'),
1547 1547
 
1548 1548
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/image/Component.tsx:60
1549
-	__( 'Height', 'event_espresso' ),
1549
+	__('Height', 'event_espresso'),
1550 1550
 
1551 1551
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/link/Component.tsx:54
1552
-	__( 'Edit link', 'event_espresso' ),
1552
+	__('Edit link', 'event_espresso'),
1553 1553
 
1554 1554
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/link/Component.tsx:64
1555
-	__( 'URL title', 'event_espresso' ),
1555
+	__('URL title', 'event_espresso'),
1556 1556
 
1557 1557
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/list/Component.tsx:11
1558
-	__( 'Unordered list', 'event_espresso' ),
1558
+	__('Unordered list', 'event_espresso'),
1559 1559
 
1560 1560
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/list/Component.tsx:12
1561
-	__( 'Ordered list', 'event_espresso' ),
1561
+	__('Ordered list', 'event_espresso'),
1562 1562
 
1563 1563
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/list/Component.tsx:13
1564 1564
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/textAlign/Component.tsx:13
1565
-	__( 'Indent', 'event_espresso' ),
1565
+	__('Indent', 'event_espresso'),
1566 1566
 
1567 1567
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/list/Component.tsx:14
1568 1568
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/textAlign/Component.tsx:14
1569
-	__( 'Outdent', 'event_espresso' ),
1569
+	__('Outdent', 'event_espresso'),
1570 1570
 
1571 1571
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/textAlign/Component.tsx:11
1572
-	__( 'Unordered textalign', 'event_espresso' ),
1572
+	__('Unordered textalign', 'event_espresso'),
1573 1573
 
1574 1574
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/Toolbar/controls/textAlign/Component.tsx:12
1575
-	__( 'Ordered textalign', 'event_espresso' ),
1575
+	__('Ordered textalign', 'event_espresso'),
1576 1576
 
1577 1577
 	// Reference: packages/rich-text-editor/src/components/RichTextEditor/render/Image/Toolbar.tsx:32
1578
-	__( 'Image toolbar', 'event_espresso' ),
1578
+	__('Image toolbar', 'event_espresso'),
1579 1579
 
1580 1580
 	// Reference: packages/rich-text-editor/src/components/WithEditMode/WithEditMode.tsx:62
1581 1581
 	// Reference: packages/rich-text-editor/src/rte-old/components/RTEWithEditMode/RTEWithEditMode.tsx:35
1582
-	__( 'Visual editor', 'event_espresso' ),
1582
+	__('Visual editor', 'event_espresso'),
1583 1583
 
1584 1584
 	// Reference: packages/rich-text-editor/src/components/WithEditMode/WithEditMode.tsx:66
1585 1585
 	// Reference: packages/rich-text-editor/src/rte-old/components/RTEWithEditMode/RTEWithEditMode.tsx:39
1586
-	__( 'HTML editor', 'event_espresso' ),
1586
+	__('HTML editor', 'event_espresso'),
1587 1587
 
1588 1588
 	// Reference: packages/rich-text-editor/src/rte-old/components/toolbarButtons/WPMedia.tsx:68
1589
-	__( 'Add Media', 'event_espresso' ),
1589
+	__('Add Media', 'event_espresso'),
1590 1590
 
1591 1591
 	// Reference: packages/tpc/src/buttons/AddPriceModifierButton.tsx:14
1592
-	__( 'add new price modifier after this row', 'event_espresso' ),
1592
+	__('add new price modifier after this row', 'event_espresso'),
1593 1593
 
1594 1594
 	// Reference: packages/tpc/src/buttons/DeleteAllPricesButton.tsx:14
1595
-	__( 'Delete all prices', 'event_espresso' ),
1595
+	__('Delete all prices', 'event_espresso'),
1596 1596
 
1597 1597
 	// Reference: packages/tpc/src/buttons/DeleteAllPricesButton.tsx:27
1598
-	__( 'Are you sure you want to delete all of this ticket\'s prices and make it free? This action is permanent and can not be undone.', 'event_espresso' ),
1598
+	__('Are you sure you want to delete all of this ticket\'s prices and make it free? This action is permanent and can not be undone.', 'event_espresso'),
1599 1599
 
1600 1600
 	// Reference: packages/tpc/src/buttons/DeleteAllPricesButton.tsx:31
1601
-	__( 'Delete all prices?', 'event_espresso' ),
1601
+	__('Delete all prices?', 'event_espresso'),
1602 1602
 
1603 1603
 	// Reference: packages/tpc/src/buttons/DeletePriceModifierButton.tsx:12
1604
-	__( 'delete price modifier', 'event_espresso' ),
1604
+	__('delete price modifier', 'event_espresso'),
1605 1605
 
1606 1606
 	// Reference: packages/tpc/src/buttons/ReverseCalculateButton.tsx:14
1607
-	__( 'Ticket base price is being reverse calculated from bottom to top starting with the ticket total. Entering a new ticket total will reverse calculate the ticket base price after applying all price modifiers in reverse. Click to turn off reverse calculations', 'event_espresso' ),
1607
+	__('Ticket base price is being reverse calculated from bottom to top starting with the ticket total. Entering a new ticket total will reverse calculate the ticket base price after applying all price modifiers in reverse. Click to turn off reverse calculations', 'event_espresso'),
1608 1608
 
1609 1609
 	// Reference: packages/tpc/src/buttons/ReverseCalculateButton.tsx:17
1610
-	__( 'Ticket total is being calculated normally from top to bottom starting from the base price. Entering a new ticket base price will recalculate the ticket total after applying all price modifiers. Click to turn on reverse calculations', 'event_espresso' ),
1610
+	__('Ticket total is being calculated normally from top to bottom starting from the base price. Entering a new ticket base price will recalculate the ticket total after applying all price modifiers. Click to turn on reverse calculations', 'event_espresso'),
1611 1611
 
1612 1612
 	// Reference: packages/tpc/src/buttons/ReverseCalculateButton.tsx:21
1613
-	__( 'Disable reverse calculate', 'event_espresso' ),
1613
+	__('Disable reverse calculate', 'event_espresso'),
1614 1614
 
1615 1615
 	// Reference: packages/tpc/src/buttons/ReverseCalculateButton.tsx:21
1616
-	__( 'Enable reverse calculate', 'event_espresso' ),
1616
+	__('Enable reverse calculate', 'event_espresso'),
1617 1617
 
1618 1618
 	// Reference: packages/tpc/src/buttons/TicketPriceCalculatorButton.tsx:28
1619
-	__( 'ticket price calculator', 'event_espresso' ),
1619
+	__('ticket price calculator', 'event_espresso'),
1620 1620
 
1621 1621
 	// Reference: packages/tpc/src/buttons/taxes/AddDefaultTaxesButton.tsx:9
1622
-	__( 'Add default taxes', 'event_espresso' ),
1622
+	__('Add default taxes', 'event_espresso'),
1623 1623
 
1624 1624
 	// Reference: packages/tpc/src/buttons/taxes/RemoveTaxesButton.tsx:10
1625
-	__( 'Are you sure you want to remove all of this ticket\'s taxes?', 'event_espresso' ),
1625
+	__('Are you sure you want to remove all of this ticket\'s taxes?', 'event_espresso'),
1626 1626
 
1627 1627
 	// Reference: packages/tpc/src/buttons/taxes/RemoveTaxesButton.tsx:14
1628
-	__( 'Remove all taxes?', 'event_espresso' ),
1628
+	__('Remove all taxes?', 'event_espresso'),
1629 1629
 
1630 1630
 	// Reference: packages/tpc/src/buttons/taxes/RemoveTaxesButton.tsx:7
1631
-	__( 'Remove taxes', 'event_espresso' ),
1631
+	__('Remove taxes', 'event_espresso'),
1632 1632
 
1633 1633
 	// Reference: packages/tpc/src/components/DefaultPricesInfo.tsx:29
1634
-	__( 'Modify default prices.', 'event_espresso' ),
1634
+	__('Modify default prices.', 'event_espresso'),
1635 1635
 
1636 1636
 	// Reference: packages/tpc/src/components/DefaultTaxesInfo.tsx:29
1637
-	__( 'New default taxes are available. Click the - Add default taxes - button to add them now.', 'event_espresso' ),
1637
+	__('New default taxes are available. Click the - Add default taxes - button to add them now.', 'event_espresso'),
1638 1638
 
1639 1639
 	// Reference: packages/tpc/src/components/LockedTicketsBanner.tsx:12
1640
-	__( 'Editing of prices is disabled', 'event_espresso' ),
1640
+	__('Editing of prices is disabled', 'event_espresso'),
1641 1641
 
1642 1642
 	// Reference: packages/tpc/src/components/NoPricesBanner/AddDefaultPricesButton.tsx:9
1643
-	__( 'Add default prices', 'event_espresso' ),
1643
+	__('Add default prices', 'event_espresso'),
1644 1644
 
1645 1645
 	// Reference: packages/tpc/src/components/NoPricesBanner/index.tsx:13
1646
-	__( 'This Ticket is Currently Free', 'event_espresso' ),
1646
+	__('This Ticket is Currently Free', 'event_espresso'),
1647 1647
 
1648 1648
 	// Reference: packages/tpc/src/components/NoPricesBanner/index.tsx:21
1649 1649
 	/* translators: %s default prices */
1650
-	__( 'Click the button below to load your %s into the calculator.', 'event_espresso' ),
1650
+	__('Click the button below to load your %s into the calculator.', 'event_espresso'),
1651 1651
 
1652 1652
 	// Reference: packages/tpc/src/components/NoPricesBanner/index.tsx:22
1653
-	__( 'default prices', 'event_espresso' ),
1653
+	__('default prices', 'event_espresso'),
1654 1654
 
1655 1655
 	// Reference: packages/tpc/src/components/NoPricesBanner/index.tsx:29
1656
-	__( 'Additional ticket price modifiers can be added or removed.', 'event_espresso' ),
1656
+	__('Additional ticket price modifiers can be added or removed.', 'event_espresso'),
1657 1657
 
1658 1658
 	// Reference: packages/tpc/src/components/NoPricesBanner/index.tsx:32
1659
-	__( 'Click the save button below to assign which dates this ticket will be available for purchase on.', 'event_espresso' ),
1659
+	__('Click the save button below to assign which dates this ticket will be available for purchase on.', 'event_espresso'),
1660 1660
 
1661 1661
 	// Reference: packages/tpc/src/components/TicketPriceCalculatorModal.tsx:32
1662 1662
 	/* translators: %s ticket name */
1663
-	__( 'Price Calculator for Ticket: %s', 'event_espresso' ),
1663
+	__('Price Calculator for Ticket: %s', 'event_espresso'),
1664 1664
 
1665 1665
 	// Reference: packages/tpc/src/components/table/useFooterRowGenerator.tsx:48
1666
-	__( 'Total', 'event_espresso' ),
1666
+	__('Total', 'event_espresso'),
1667 1667
 
1668 1668
 	// Reference: packages/tpc/src/components/table/useFooterRowGenerator.tsx:57
1669
-	__( 'ticket total', 'event_espresso' ),
1669
+	__('ticket total', 'event_espresso'),
1670 1670
 
1671 1671
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:29
1672
-	__( 'Order', 'event_espresso' ),
1672
+	__('Order', 'event_espresso'),
1673 1673
 
1674 1674
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:35
1675
-	__( 'Price Type', 'event_espresso' ),
1675
+	__('Price Type', 'event_espresso'),
1676 1676
 
1677 1677
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:41
1678
-	__( 'Label', 'event_espresso' ),
1678
+	__('Label', 'event_espresso'),
1679 1679
 
1680 1680
 	// Reference: packages/tpc/src/components/table/useHeaderRowGenerator.ts:53
1681
-	__( 'Amount', 'event_espresso' ),
1681
+	__('Amount', 'event_espresso'),
1682 1682
 
1683 1683
 	// Reference: packages/tpc/src/hooks/useLockedTicketAction.ts:22
1684
-	__( 'Copy ticket', 'event_espresso' ),
1684
+	__('Copy ticket', 'event_espresso'),
1685 1685
 
1686 1686
 	// Reference: packages/tpc/src/hooks/useLockedTicketAction.ts:26
1687
-	__( 'Copy and archive this ticket', 'event_espresso' ),
1687
+	__('Copy and archive this ticket', 'event_espresso'),
1688 1688
 
1689 1689
 	// Reference: packages/tpc/src/hooks/useLockedTicketAction.ts:29
1690
-	__( 'OK', 'event_espresso' ),
1690
+	__('OK', 'event_espresso'),
1691 1691
 
1692 1692
 	// Reference: packages/tpc/src/inputs/PriceAmountInput.tsx:32
1693
-	__( 'amount', 'event_espresso' ),
1693
+	__('amount', 'event_espresso'),
1694 1694
 
1695 1695
 	// Reference: packages/tpc/src/inputs/PriceAmountInput.tsx:44
1696
-	__( 'amount…', 'event_espresso' ),
1696
+	__('amount…', 'event_espresso'),
1697 1697
 
1698 1698
 	// Reference: packages/tpc/src/inputs/PriceDescriptionInput.tsx:14
1699
-	__( 'description…', 'event_espresso' ),
1699
+	__('description…', 'event_espresso'),
1700 1700
 
1701 1701
 	// Reference: packages/tpc/src/inputs/PriceDescriptionInput.tsx:9
1702
-	__( 'price description', 'event_espresso' ),
1702
+	__('price description', 'event_espresso'),
1703 1703
 
1704 1704
 	// Reference: packages/tpc/src/inputs/PriceIdInput.tsx:5
1705
-	__( 'price id', 'event_espresso' ),
1705
+	__('price id', 'event_espresso'),
1706 1706
 
1707 1707
 	// Reference: packages/tpc/src/inputs/PriceNameInput.tsx:13
1708
-	__( 'label…', 'event_espresso' ),
1708
+	__('label…', 'event_espresso'),
1709 1709
 
1710 1710
 	// Reference: packages/tpc/src/inputs/PriceNameInput.tsx:8
1711
-	__( 'price name', 'event_espresso' ),
1711
+	__('price name', 'event_espresso'),
1712 1712
 
1713 1713
 	// Reference: packages/tpc/src/inputs/PriceOrderInput.tsx:14
1714
-	__( 'price order', 'event_espresso' ),
1714
+	__('price order', 'event_espresso'),
1715 1715
 
1716 1716
 	// Reference: packages/tpc/src/inputs/PriceTypeInput.tsx:19
1717
-	__( 'price type', 'event_espresso' ),
1717
+	__('price type', 'event_espresso'),
1718 1718
 
1719 1719
 	// Reference: packages/tpc/src/utils/constants.ts:8
1720
-	__( 'Ticket price modifications are blocked for Tickets that have already been sold to registrants, because doing so would negatively affect internal accounting for the event. If you still need to modify ticket prices, then create a copy of those tickets, edit the prices for the new tickets, and then trash the old tickets.', 'event_espresso' ),
1720
+	__('Ticket price modifications are blocked for Tickets that have already been sold to registrants, because doing so would negatively affect internal accounting for the event. If you still need to modify ticket prices, then create a copy of those tickets, edit the prices for the new tickets, and then trash the old tickets.', 'event_espresso'),
1721 1721
 
1722 1722
 	// Reference: packages/ui-components/src/ActiveFilters/ActiveFilters.tsx:8
1723
-	__( 'active filters:', 'event_espresso' ),
1723
+	__('active filters:', 'event_espresso'),
1724 1724
 
1725 1725
 	// Reference: packages/ui-components/src/ActiveFilters/FilterTag/index.tsx:15
1726 1726
 	/* translators: %s filter name */
1727
-	__( 'remove filter - %s', 'event_espresso' ),
1727
+	__('remove filter - %s', 'event_espresso'),
1728 1728
 
1729 1729
 	// Reference: packages/ui-components/src/CalendarDateRange/CalendarDateRange.tsx:37
1730
-	__( 'to', 'event_espresso' ),
1730
+	__('to', 'event_espresso'),
1731 1731
 
1732 1732
 	// Reference: packages/ui-components/src/CalendarPageDate/CalendarPageDate.tsx:54
1733
-	__( 'TO', 'event_espresso' ),
1733
+	__('TO', 'event_espresso'),
1734 1734
 
1735 1735
 	// Reference: packages/ui-components/src/ColorPicker/ColorPicker.tsx:60
1736
-	__( 'Custom color', 'event_espresso' ),
1736
+	__('Custom color', 'event_espresso'),
1737 1737
 
1738 1738
 	// Reference: packages/ui-components/src/ColorPicker/Swatch.tsx:23
1739 1739
 	/* translators: color name */
1740
-	__( 'Color: %s', 'event_espresso' ),
1740
+	__('Color: %s', 'event_espresso'),
1741 1741
 
1742 1742
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:13
1743
-	__( 'Cyan bluish gray', 'event_espresso' ),
1743
+	__('Cyan bluish gray', 'event_espresso'),
1744 1744
 
1745 1745
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:17
1746
-	__( 'White', 'event_espresso' ),
1746
+	__('White', 'event_espresso'),
1747 1747
 
1748 1748
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:21
1749
-	__( 'Pale pink', 'event_espresso' ),
1749
+	__('Pale pink', 'event_espresso'),
1750 1750
 
1751 1751
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:25
1752
-	__( 'Vivid red', 'event_espresso' ),
1752
+	__('Vivid red', 'event_espresso'),
1753 1753
 
1754 1754
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:29
1755
-	__( 'Luminous vivid orange', 'event_espresso' ),
1755
+	__('Luminous vivid orange', 'event_espresso'),
1756 1756
 
1757 1757
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:33
1758
-	__( 'Luminous vivid amber', 'event_espresso' ),
1758
+	__('Luminous vivid amber', 'event_espresso'),
1759 1759
 
1760 1760
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:37
1761
-	__( 'Light green cyan', 'event_espresso' ),
1761
+	__('Light green cyan', 'event_espresso'),
1762 1762
 
1763 1763
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:41
1764
-	__( 'Vivid green cyan', 'event_espresso' ),
1764
+	__('Vivid green cyan', 'event_espresso'),
1765 1765
 
1766 1766
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:45
1767
-	__( 'Pale cyan blue', 'event_espresso' ),
1767
+	__('Pale cyan blue', 'event_espresso'),
1768 1768
 
1769 1769
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:49
1770
-	__( 'Vivid cyan blue', 'event_espresso' ),
1770
+	__('Vivid cyan blue', 'event_espresso'),
1771 1771
 
1772 1772
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:53
1773
-	__( 'Vivid purple', 'event_espresso' ),
1773
+	__('Vivid purple', 'event_espresso'),
1774 1774
 
1775 1775
 	// Reference: packages/ui-components/src/ColorPicker/constants.ts:9
1776
-	__( 'Black', 'event_espresso' ),
1776
+	__('Black', 'event_espresso'),
1777 1777
 
1778 1778
 	// Reference: packages/ui-components/src/Confirm/ConfirmClose.tsx:7
1779 1779
 	// Reference: packages/ui-components/src/Modal/ModalWithAlert.tsx:22
1780
-	__( 'Are you sure you want to close this?', 'event_espresso' ),
1780
+	__('Are you sure you want to close this?', 'event_espresso'),
1781 1781
 
1782 1782
 	// Reference: packages/ui-components/src/Confirm/ConfirmDelete.tsx:7
1783
-	__( 'Are you sure you want to delete this?', 'event_espresso' ),
1783
+	__('Are you sure you want to delete this?', 'event_espresso'),
1784 1784
 
1785 1785
 	// Reference: packages/ui-components/src/Confirm/useConfirmWithButton.tsx:10
1786
-	__( 'Please confirm this action.', 'event_espresso' ),
1786
+	__('Please confirm this action.', 'event_espresso'),
1787 1787
 
1788 1788
 	// Reference: packages/ui-components/src/Confirm/useConfirmationDialog.tsx:32
1789
-	__( 'No', 'event_espresso' ),
1789
+	__('No', 'event_espresso'),
1790 1790
 
1791 1791
 	// Reference: packages/ui-components/src/Confirm/useConfirmationDialog.tsx:33
1792
-	__( 'Yes', 'event_espresso' ),
1792
+	__('Yes', 'event_espresso'),
1793 1793
 
1794 1794
 	// Reference: packages/ui-components/src/CurrencyDisplay/CurrencyDisplay.tsx:34
1795
-	__( 'free', 'event_espresso' ),
1795
+	__('free', 'event_espresso'),
1796 1796
 
1797 1797
 	// Reference: packages/ui-components/src/DateTimeRangePicker/DateTimeRangePicker.tsx:117
1798 1798
 	// Reference: packages/ui-components/src/Popover/PopoverForm/PopoverForm.tsx:44
1799
-	__( 'save', 'event_espresso' ),
1799
+	__('save', 'event_espresso'),
1800 1800
 
1801 1801
 	// Reference: packages/ui-components/src/DebugInfo/DebugInfo.tsx:36
1802
-	__( 'Hide Debug Info', 'event_espresso' ),
1802
+	__('Hide Debug Info', 'event_espresso'),
1803 1803
 
1804 1804
 	// Reference: packages/ui-components/src/DebugInfo/DebugInfo.tsx:36
1805
-	__( 'Show Debug Info', 'event_espresso' ),
1805
+	__('Show Debug Info', 'event_espresso'),
1806 1806
 
1807 1807
 	// Reference: packages/ui-components/src/EditDateRangeButton/EditDateRangeButton.tsx:49
1808
-	__( 'Edit Start and End Dates and Times', 'event_espresso' ),
1808
+	__('Edit Start and End Dates and Times', 'event_espresso'),
1809 1809
 
1810 1810
 	// Reference: packages/ui-components/src/EntityActionsMenu/entityMenuItems/Copy.tsx:8
1811
-	__( 'copy', 'event_espresso' ),
1811
+	__('copy', 'event_espresso'),
1812 1812
 
1813 1813
 	// Reference: packages/ui-components/src/EntityActionsMenu/entityMenuItems/Edit.tsx:8
1814
-	__( 'edit', 'event_espresso' ),
1814
+	__('edit', 'event_espresso'),
1815 1815
 
1816 1816
 	// Reference: packages/ui-components/src/EntityActionsMenu/entityMenuItems/Trash.tsx:8
1817
-	__( 'trash', 'event_espresso' ),
1817
+	__('trash', 'event_espresso'),
1818 1818
 
1819 1819
 	// Reference: packages/ui-components/src/EntityActionsMenu/entityMenuItems/Untrash.tsx:8
1820
-	__( 'untrash', 'event_espresso' ),
1820
+	__('untrash', 'event_espresso'),
1821 1821
 
1822 1822
 	// Reference: packages/ui-components/src/EntityDetailsPanel/EntityDetailsPanelSold.tsx:37
1823
-	__( 'view approved registrations for this date.', 'event_espresso' ),
1823
+	__('view approved registrations for this date.', 'event_espresso'),
1824 1824
 
1825 1825
 	// Reference: packages/ui-components/src/EntityDetailsPanel/EntityDetailsPanelSold.tsx:38
1826
-	__( 'view approved registrations for this ticket.', 'event_espresso' ),
1826
+	__('view approved registrations for this ticket.', 'event_espresso'),
1827 1827
 
1828 1828
 	// Reference: packages/ui-components/src/EntityList/filterBar/buttons/CardViewFilterButton.tsx:21
1829
-	__( 'card view', 'event_espresso' ),
1829
+	__('card view', 'event_espresso'),
1830 1830
 
1831 1831
 	// Reference: packages/ui-components/src/EntityList/filterBar/buttons/TableViewFilterButton.tsx:20
1832
-	__( 'table view', 'event_espresso' ),
1832
+	__('table view', 'event_espresso'),
1833 1833
 
1834 1834
 	// Reference: packages/ui-components/src/EntityList/filterBar/buttons/ToggleBulkActionsButton.tsx:8
1835
-	__( 'hide bulk actions', 'event_espresso' ),
1835
+	__('hide bulk actions', 'event_espresso'),
1836 1836
 
1837 1837
 	// Reference: packages/ui-components/src/EntityList/filterBar/buttons/ToggleBulkActionsButton.tsx:8
1838
-	__( 'show bulk actions', 'event_espresso' ),
1838
+	__('show bulk actions', 'event_espresso'),
1839 1839
 
1840 1840
 	// Reference: packages/ui-components/src/EntityList/filterBar/buttons/ToggleFiltersButton.tsx:9
1841
-	__( 'hide filters', 'event_espresso' ),
1841
+	__('hide filters', 'event_espresso'),
1842 1842
 
1843 1843
 	// Reference: packages/ui-components/src/EntityList/filterBar/buttons/ToggleFiltersButton.tsx:9
1844
-	__( 'show filters', 'event_espresso' ),
1844
+	__('show filters', 'event_espresso'),
1845 1845
 
1846 1846
 	// Reference: packages/ui-components/src/Legend/ToggleLegendButton.tsx:26
1847
-	__( 'hide legend', 'event_espresso' ),
1847
+	__('hide legend', 'event_espresso'),
1848 1848
 
1849 1849
 	// Reference: packages/ui-components/src/Legend/ToggleLegendButton.tsx:26
1850
-	__( 'show legend', 'event_espresso' ),
1850
+	__('show legend', 'event_espresso'),
1851 1851
 
1852 1852
 	// Reference: packages/ui-components/src/LoadingNotice/LoadingNotice.tsx:11
1853
-	__( 'loading…', 'event_espresso' ),
1853
+	__('loading…', 'event_espresso'),
1854 1854
 
1855 1855
 	// Reference: packages/ui-components/src/Modal/Modal.tsx:58
1856
-	__( 'close modal', 'event_espresso' ),
1856
+	__('close modal', 'event_espresso'),
1857 1857
 
1858 1858
 	// Reference: packages/ui-components/src/Pagination/ItemRender.tsx:10
1859
-	__( 'jump to previous', 'event_espresso' ),
1859
+	__('jump to previous', 'event_espresso'),
1860 1860
 
1861 1861
 	// Reference: packages/ui-components/src/Pagination/ItemRender.tsx:11
1862
-	__( 'jump to next', 'event_espresso' ),
1862
+	__('jump to next', 'event_espresso'),
1863 1863
 
1864 1864
 	// Reference: packages/ui-components/src/Pagination/ItemRender.tsx:12
1865
-	__( 'page', 'event_espresso' ),
1865
+	__('page', 'event_espresso'),
1866 1866
 
1867 1867
 	// Reference: packages/ui-components/src/Pagination/ItemRender.tsx:8
1868
-	__( 'previous', 'event_espresso' ),
1868
+	__('previous', 'event_espresso'),
1869 1869
 
1870 1870
 	// Reference: packages/ui-components/src/Pagination/ItemRender.tsx:9
1871
-	__( 'next', 'event_espresso' ),
1871
+	__('next', 'event_espresso'),
1872 1872
 
1873 1873
 	// Reference: packages/ui-components/src/Pagination/PerPage.tsx:37
1874
-	__( 'items per page', 'event_espresso' ),
1874
+	__('items per page', 'event_espresso'),
1875 1875
 
1876 1876
 	// Reference: packages/ui-components/src/Pagination/constants.ts:10
1877 1877
 	/* translators: %s is per page value */
1878
-	__( '%s / page', 'event_espresso' ),
1878
+	__('%s / page', 'event_espresso'),
1879 1879
 
1880 1880
 	// Reference: packages/ui-components/src/Pagination/constants.ts:13
1881
-	__( 'Next Page', 'event_espresso' ),
1881
+	__('Next Page', 'event_espresso'),
1882 1882
 
1883 1883
 	// Reference: packages/ui-components/src/Pagination/constants.ts:14
1884
-	__( 'Previous Page', 'event_espresso' ),
1884
+	__('Previous Page', 'event_espresso'),
1885 1885
 
1886 1886
 	// Reference: packages/ui-components/src/PercentSign/index.tsx:10
1887
-	__( '%', 'event_espresso' ),
1887
+	__('%', 'event_espresso'),
1888 1888
 
1889 1889
 	// Reference: packages/ui-components/src/SimpleEntityList/EntityOptionsRow/index.tsx:23
1890
-	__( 'Select an existing one to use as a template.', 'event_espresso' ),
1890
+	__('Select an existing one to use as a template.', 'event_espresso'),
1891 1891
 
1892 1892
 	// Reference: packages/ui-components/src/SimpleEntityList/EntityOptionsRow/index.tsx:27
1893
-	__( 'or', 'event_espresso' ),
1893
+	__('or', 'event_espresso'),
1894 1894
 
1895 1895
 	// Reference: packages/ui-components/src/SimpleEntityList/EntityOptionsRow/index.tsx:30
1896
-	__( 'Add new and insert details manually', 'event_espresso' ),
1896
+	__('Add new and insert details manually', 'event_espresso'),
1897 1897
 
1898 1898
 	// Reference: packages/ui-components/src/SimpleEntityList/EntityOptionsRow/index.tsx:34
1899
-	__( 'Add New', 'event_espresso' ),
1899
+	__('Add New', 'event_espresso'),
1900 1900
 
1901 1901
 	// Reference: packages/ui-components/src/Stepper/buttons/Next.tsx:8
1902
-	__( 'Next', 'event_espresso' ),
1902
+	__('Next', 'event_espresso'),
1903 1903
 
1904 1904
 	// Reference: packages/ui-components/src/Stepper/buttons/Previous.tsx:8
1905
-	__( 'Previous', 'event_espresso' ),
1905
+	__('Previous', 'event_espresso'),
1906 1906
 
1907 1907
 	// Reference: packages/ui-components/src/Steps/Steps.tsx:31
1908
-	__( 'Steps', 'event_espresso' ),
1908
+	__('Steps', 'event_espresso'),
1909 1909
 
1910 1910
 	// Reference: packages/ui-components/src/TabbableText/index.tsx:19
1911
-	__( 'Click to edit…', 'event_espresso' ),
1911
+	__('Click to edit…', 'event_espresso'),
1912 1912
 
1913 1913
 	// Reference: packages/ui-components/src/TimezoneTimeInfo/Content.tsx:14
1914
-	__( 'The Website\'s Time Zone', 'event_espresso' ),
1914
+	__('The Website\'s Time Zone', 'event_espresso'),
1915 1915
 
1916 1916
 	// Reference: packages/ui-components/src/TimezoneTimeInfo/Content.tsx:19
1917
-	__( 'UTC (Greenwich Mean Time)', 'event_espresso' ),
1917
+	__('UTC (Greenwich Mean Time)', 'event_espresso'),
1918 1918
 
1919 1919
 	// Reference: packages/ui-components/src/TimezoneTimeInfo/Content.tsx:9
1920
-	__( 'Your Local Time Zone', 'event_espresso' ),
1920
+	__('Your Local Time Zone', 'event_espresso'),
1921 1921
 
1922 1922
 	// Reference: packages/ui-components/src/TimezoneTimeInfo/TimezoneTimeInfo.tsx:27
1923
-	__( 'click for timezone information', 'event_espresso' ),
1923
+	__('click for timezone information', 'event_espresso'),
1924 1924
 
1925 1925
 	// Reference: packages/ui-components/src/TimezoneTimeInfo/TimezoneTimeInfo.tsx:32
1926
-	__( 'This Date Converted To:', 'event_espresso' ),
1926
+	__('This Date Converted To:', 'event_espresso'),
1927 1927
 
1928 1928
 	// Reference: packages/ui-components/src/bulkEdit/BulkActions.tsx:51
1929
-	__( 'select all', 'event_espresso' ),
1929
+	__('select all', 'event_espresso'),
1930 1930
 
1931 1931
 	// Reference: packages/ui-components/src/bulkEdit/BulkActions.tsx:54
1932
-	__( 'apply', 'event_espresso' )
1932
+	__('apply', 'event_espresso')
1933 1933
 );
1934 1934
 /* THIS IS THE END OF THE GENERATED FILE */
Please login to merge, or discard this patch.
admin_pages/events/Events_Admin_Page.core.php 1 patch
Indentation   +2859 added lines, -2859 removed lines patch added patch discarded remove patch
@@ -20,2864 +20,2864 @@
 block discarded – undo
20 20
 class Events_Admin_Page extends EE_Admin_Page_CPT
21 21
 {
22 22
 
23
-    /**
24
-     * This will hold the event object for event_details screen.
25
-     *
26
-     * @access protected
27
-     * @var EE_Event $_event
28
-     */
29
-    protected $_event;
30
-
31
-
32
-    /**
33
-     * This will hold the category object for category_details screen.
34
-     *
35
-     * @var stdClass $_category
36
-     */
37
-    protected $_category;
38
-
39
-
40
-    /**
41
-     * This will hold the event model instance
42
-     *
43
-     * @var EEM_Event $_event_model
44
-     */
45
-    protected $_event_model;
46
-
47
-
48
-    /**
49
-     * @var EE_Event
50
-     */
51
-    protected $_cpt_model_obj = false;
52
-
53
-
54
-    /**
55
-     * @var NodeGroupDao
56
-     */
57
-    protected $model_obj_node_group_persister;
58
-
59
-    /**
60
-     * Initialize page props for this admin page group.
61
-     */
62
-    protected function _init_page_props()
63
-    {
64
-        $this->page_slug = EVENTS_PG_SLUG;
65
-        $this->page_label = EVENTS_LABEL;
66
-        $this->_admin_base_url = EVENTS_ADMIN_URL;
67
-        $this->_admin_base_path = EVENTS_ADMIN;
68
-        $this->_cpt_model_names = [
69
-            'create_new' => 'EEM_Event',
70
-            'edit'       => 'EEM_Event',
71
-        ];
72
-        $this->_cpt_edit_routes = [
73
-            'espresso_events' => 'edit',
74
-        ];
75
-        add_action(
76
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
77
-            [$this, 'verify_event_edit'],
78
-            10,
79
-            2
80
-        );
81
-    }
82
-
83
-
84
-    /**
85
-     * Sets the ajax hooks used for this admin page group.
86
-     */
87
-    protected function _ajax_hooks()
88
-    {
89
-        add_action('wp_ajax_ee_save_timezone_setting', [$this, 'save_timezonestring_setting']);
90
-    }
91
-
92
-
93
-    /**
94
-     * Sets the page properties for this admin page group.
95
-     */
96
-    protected function _define_page_props()
97
-    {
98
-        $this->_admin_page_title = EVENTS_LABEL;
99
-        $this->_labels = [
100
-            'buttons'      => [
101
-                'add'             => esc_html__('Add New Event', 'event_espresso'),
102
-                'edit'            => esc_html__('Edit Event', 'event_espresso'),
103
-                'delete'          => esc_html__('Delete Event', 'event_espresso'),
104
-                'add_category'    => esc_html__('Add New Category', 'event_espresso'),
105
-                'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
106
-                'delete_category' => esc_html__('Delete Category', 'event_espresso'),
107
-            ],
108
-            'editor_title' => [
109
-                'espresso_events' => esc_html__('Enter event title here', 'event_espresso'),
110
-            ],
111
-            'publishbox'   => [
112
-                'create_new'        => esc_html__('Save New Event', 'event_espresso'),
113
-                'edit'              => esc_html__('Update Event', 'event_espresso'),
114
-                'add_category'      => esc_html__('Save New Category', 'event_espresso'),
115
-                'edit_category'     => esc_html__('Update Category', 'event_espresso'),
116
-                'template_settings' => esc_html__('Update Settings', 'event_espresso'),
117
-            ],
118
-        ];
119
-    }
120
-
121
-
122
-    /**
123
-     * Sets the page routes property for this admin page group.
124
-     */
125
-    protected function _set_page_routes()
126
-    {
127
-        // load formatter helper
128
-        // load field generator helper
129
-        // is there a evt_id in the request?
130
-        $evt_id = ! empty($this->_req_data['EVT_ID']) && ! is_array($this->_req_data['EVT_ID'])
131
-            ? $this->_req_data['EVT_ID']
132
-            : 0;
133
-        $evt_id = ! empty($this->_req_data['post']) ? $this->_req_data['post'] : $evt_id;
134
-        $this->_page_routes = [
135
-            'default'                       => [
136
-                'func'       => '_events_overview_list_table',
137
-                'capability' => 'ee_read_events',
138
-            ],
139
-            'create_new'                    => [
140
-                'func'       => '_create_new_cpt_item',
141
-                'capability' => 'ee_edit_events',
142
-            ],
143
-            'edit'                          => [
144
-                'func'       => '_edit_cpt_item',
145
-                'capability' => 'ee_edit_event',
146
-                'obj_id'     => $evt_id,
147
-            ],
148
-            'copy_event'                    => [
149
-                'func'       => '_copy_events',
150
-                'capability' => 'ee_edit_event',
151
-                'obj_id'     => $evt_id,
152
-                'noheader'   => true,
153
-            ],
154
-            'trash_event'                   => [
155
-                'func'       => '_trash_or_restore_event',
156
-                'args'       => ['event_status' => 'trash'],
157
-                'capability' => 'ee_delete_event',
158
-                'obj_id'     => $evt_id,
159
-                'noheader'   => true,
160
-            ],
161
-            'trash_events'                  => [
162
-                'func'       => '_trash_or_restore_events',
163
-                'args'       => ['event_status' => 'trash'],
164
-                'capability' => 'ee_delete_events',
165
-                'noheader'   => true,
166
-            ],
167
-            'restore_event'                 => [
168
-                'func'       => '_trash_or_restore_event',
169
-                'args'       => ['event_status' => 'draft'],
170
-                'capability' => 'ee_delete_event',
171
-                'obj_id'     => $evt_id,
172
-                'noheader'   => true,
173
-            ],
174
-            'restore_events'                => [
175
-                'func'       => '_trash_or_restore_events',
176
-                'args'       => ['event_status' => 'draft'],
177
-                'capability' => 'ee_delete_events',
178
-                'noheader'   => true,
179
-            ],
180
-            'delete_event'                  => [
181
-                'func'       => '_delete_event',
182
-                'capability' => 'ee_delete_event',
183
-                'obj_id'     => $evt_id,
184
-                'noheader'   => true,
185
-            ],
186
-            'delete_events'                 => [
187
-                'func'       => '_delete_events',
188
-                'capability' => 'ee_delete_events',
189
-                'noheader'   => true,
190
-            ],
191
-            'view_report'                   => [
192
-                'func'       => '_view_report',
193
-                'capability' => 'ee_edit_events',
194
-            ],
195
-            'default_event_settings'        => [
196
-                'func'       => '_default_event_settings',
197
-                'capability' => 'manage_options',
198
-            ],
199
-            'update_default_event_settings' => [
200
-                'func'       => '_update_default_event_settings',
201
-                'capability' => 'manage_options',
202
-                'noheader'   => true,
203
-            ],
204
-            'template_settings'             => [
205
-                'func'       => '_template_settings',
206
-                'capability' => 'manage_options',
207
-            ],
208
-            // event category tab related
209
-            'add_category'                  => [
210
-                'func'       => '_category_details',
211
-                'capability' => 'ee_edit_event_category',
212
-                'args'       => ['add'],
213
-            ],
214
-            'edit_category'                 => [
215
-                'func'       => '_category_details',
216
-                'capability' => 'ee_edit_event_category',
217
-                'args'       => ['edit'],
218
-            ],
219
-            'delete_categories'             => [
220
-                'func'       => '_delete_categories',
221
-                'capability' => 'ee_delete_event_category',
222
-                'noheader'   => true,
223
-            ],
224
-            'delete_category'               => [
225
-                'func'       => '_delete_categories',
226
-                'capability' => 'ee_delete_event_category',
227
-                'noheader'   => true,
228
-            ],
229
-            'insert_category'               => [
230
-                'func'       => '_insert_or_update_category',
231
-                'args'       => ['new_category' => true],
232
-                'capability' => 'ee_edit_event_category',
233
-                'noheader'   => true,
234
-            ],
235
-            'update_category'               => [
236
-                'func'       => '_insert_or_update_category',
237
-                'args'       => ['new_category' => false],
238
-                'capability' => 'ee_edit_event_category',
239
-                'noheader'   => true,
240
-            ],
241
-            'category_list'                 => [
242
-                'func'       => '_category_list_table',
243
-                'capability' => 'ee_manage_event_categories',
244
-            ],
245
-            'preview_deletion' => [
246
-                'func' => 'previewDeletion',
247
-                'capability' => 'ee_delete_events',
248
-            ],
249
-            'confirm_deletion' => [
250
-                'func' => 'confirmDeletion',
251
-                'capability' => 'ee_delete_events',
252
-                'noheader' => true,
253
-            ]
254
-        ];
255
-    }
256
-
257
-
258
-    /**
259
-     * Set the _page_config property for this admin page group.
260
-     */
261
-    protected function _set_page_config()
262
-    {
263
-        $this->_page_config = [
264
-            'default'                => [
265
-                'nav'           => [
266
-                    'label' => esc_html__('Overview', 'event_espresso'),
267
-                    'order' => 10,
268
-                ],
269
-                'list_table'    => 'Events_Admin_List_Table',
270
-                'help_tabs'     => [
271
-                    'events_overview_help_tab'                       => [
272
-                        'title'    => esc_html__('Events Overview', 'event_espresso'),
273
-                        'filename' => 'events_overview',
274
-                    ],
275
-                    'events_overview_table_column_headings_help_tab' => [
276
-                        'title'    => esc_html__('Events Overview Table Column Headings', 'event_espresso'),
277
-                        'filename' => 'events_overview_table_column_headings',
278
-                    ],
279
-                    'events_overview_filters_help_tab'               => [
280
-                        'title'    => esc_html__('Events Overview Filters', 'event_espresso'),
281
-                        'filename' => 'events_overview_filters',
282
-                    ],
283
-                    'events_overview_view_help_tab'                  => [
284
-                        'title'    => esc_html__('Events Overview Views', 'event_espresso'),
285
-                        'filename' => 'events_overview_views',
286
-                    ],
287
-                    'events_overview_other_help_tab'                 => [
288
-                        'title'    => esc_html__('Events Overview Other', 'event_espresso'),
289
-                        'filename' => 'events_overview_other',
290
-                    ],
291
-                ],
292
-                // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
293
-                // 'help_tour'     => [
294
-                //     'Event_Overview_Help_Tour',
295
-                //     // 'New_Features_Test_Help_Tour' for testing multiple help tour
296
-                // ],
297
-                'qtips'         => [
298
-                    'EE_Event_List_Table_Tips',
23
+	/**
24
+	 * This will hold the event object for event_details screen.
25
+	 *
26
+	 * @access protected
27
+	 * @var EE_Event $_event
28
+	 */
29
+	protected $_event;
30
+
31
+
32
+	/**
33
+	 * This will hold the category object for category_details screen.
34
+	 *
35
+	 * @var stdClass $_category
36
+	 */
37
+	protected $_category;
38
+
39
+
40
+	/**
41
+	 * This will hold the event model instance
42
+	 *
43
+	 * @var EEM_Event $_event_model
44
+	 */
45
+	protected $_event_model;
46
+
47
+
48
+	/**
49
+	 * @var EE_Event
50
+	 */
51
+	protected $_cpt_model_obj = false;
52
+
53
+
54
+	/**
55
+	 * @var NodeGroupDao
56
+	 */
57
+	protected $model_obj_node_group_persister;
58
+
59
+	/**
60
+	 * Initialize page props for this admin page group.
61
+	 */
62
+	protected function _init_page_props()
63
+	{
64
+		$this->page_slug = EVENTS_PG_SLUG;
65
+		$this->page_label = EVENTS_LABEL;
66
+		$this->_admin_base_url = EVENTS_ADMIN_URL;
67
+		$this->_admin_base_path = EVENTS_ADMIN;
68
+		$this->_cpt_model_names = [
69
+			'create_new' => 'EEM_Event',
70
+			'edit'       => 'EEM_Event',
71
+		];
72
+		$this->_cpt_edit_routes = [
73
+			'espresso_events' => 'edit',
74
+		];
75
+		add_action(
76
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
77
+			[$this, 'verify_event_edit'],
78
+			10,
79
+			2
80
+		);
81
+	}
82
+
83
+
84
+	/**
85
+	 * Sets the ajax hooks used for this admin page group.
86
+	 */
87
+	protected function _ajax_hooks()
88
+	{
89
+		add_action('wp_ajax_ee_save_timezone_setting', [$this, 'save_timezonestring_setting']);
90
+	}
91
+
92
+
93
+	/**
94
+	 * Sets the page properties for this admin page group.
95
+	 */
96
+	protected function _define_page_props()
97
+	{
98
+		$this->_admin_page_title = EVENTS_LABEL;
99
+		$this->_labels = [
100
+			'buttons'      => [
101
+				'add'             => esc_html__('Add New Event', 'event_espresso'),
102
+				'edit'            => esc_html__('Edit Event', 'event_espresso'),
103
+				'delete'          => esc_html__('Delete Event', 'event_espresso'),
104
+				'add_category'    => esc_html__('Add New Category', 'event_espresso'),
105
+				'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
106
+				'delete_category' => esc_html__('Delete Category', 'event_espresso'),
107
+			],
108
+			'editor_title' => [
109
+				'espresso_events' => esc_html__('Enter event title here', 'event_espresso'),
110
+			],
111
+			'publishbox'   => [
112
+				'create_new'        => esc_html__('Save New Event', 'event_espresso'),
113
+				'edit'              => esc_html__('Update Event', 'event_espresso'),
114
+				'add_category'      => esc_html__('Save New Category', 'event_espresso'),
115
+				'edit_category'     => esc_html__('Update Category', 'event_espresso'),
116
+				'template_settings' => esc_html__('Update Settings', 'event_espresso'),
117
+			],
118
+		];
119
+	}
120
+
121
+
122
+	/**
123
+	 * Sets the page routes property for this admin page group.
124
+	 */
125
+	protected function _set_page_routes()
126
+	{
127
+		// load formatter helper
128
+		// load field generator helper
129
+		// is there a evt_id in the request?
130
+		$evt_id = ! empty($this->_req_data['EVT_ID']) && ! is_array($this->_req_data['EVT_ID'])
131
+			? $this->_req_data['EVT_ID']
132
+			: 0;
133
+		$evt_id = ! empty($this->_req_data['post']) ? $this->_req_data['post'] : $evt_id;
134
+		$this->_page_routes = [
135
+			'default'                       => [
136
+				'func'       => '_events_overview_list_table',
137
+				'capability' => 'ee_read_events',
138
+			],
139
+			'create_new'                    => [
140
+				'func'       => '_create_new_cpt_item',
141
+				'capability' => 'ee_edit_events',
142
+			],
143
+			'edit'                          => [
144
+				'func'       => '_edit_cpt_item',
145
+				'capability' => 'ee_edit_event',
146
+				'obj_id'     => $evt_id,
147
+			],
148
+			'copy_event'                    => [
149
+				'func'       => '_copy_events',
150
+				'capability' => 'ee_edit_event',
151
+				'obj_id'     => $evt_id,
152
+				'noheader'   => true,
153
+			],
154
+			'trash_event'                   => [
155
+				'func'       => '_trash_or_restore_event',
156
+				'args'       => ['event_status' => 'trash'],
157
+				'capability' => 'ee_delete_event',
158
+				'obj_id'     => $evt_id,
159
+				'noheader'   => true,
160
+			],
161
+			'trash_events'                  => [
162
+				'func'       => '_trash_or_restore_events',
163
+				'args'       => ['event_status' => 'trash'],
164
+				'capability' => 'ee_delete_events',
165
+				'noheader'   => true,
166
+			],
167
+			'restore_event'                 => [
168
+				'func'       => '_trash_or_restore_event',
169
+				'args'       => ['event_status' => 'draft'],
170
+				'capability' => 'ee_delete_event',
171
+				'obj_id'     => $evt_id,
172
+				'noheader'   => true,
173
+			],
174
+			'restore_events'                => [
175
+				'func'       => '_trash_or_restore_events',
176
+				'args'       => ['event_status' => 'draft'],
177
+				'capability' => 'ee_delete_events',
178
+				'noheader'   => true,
179
+			],
180
+			'delete_event'                  => [
181
+				'func'       => '_delete_event',
182
+				'capability' => 'ee_delete_event',
183
+				'obj_id'     => $evt_id,
184
+				'noheader'   => true,
185
+			],
186
+			'delete_events'                 => [
187
+				'func'       => '_delete_events',
188
+				'capability' => 'ee_delete_events',
189
+				'noheader'   => true,
190
+			],
191
+			'view_report'                   => [
192
+				'func'       => '_view_report',
193
+				'capability' => 'ee_edit_events',
194
+			],
195
+			'default_event_settings'        => [
196
+				'func'       => '_default_event_settings',
197
+				'capability' => 'manage_options',
198
+			],
199
+			'update_default_event_settings' => [
200
+				'func'       => '_update_default_event_settings',
201
+				'capability' => 'manage_options',
202
+				'noheader'   => true,
203
+			],
204
+			'template_settings'             => [
205
+				'func'       => '_template_settings',
206
+				'capability' => 'manage_options',
207
+			],
208
+			// event category tab related
209
+			'add_category'                  => [
210
+				'func'       => '_category_details',
211
+				'capability' => 'ee_edit_event_category',
212
+				'args'       => ['add'],
213
+			],
214
+			'edit_category'                 => [
215
+				'func'       => '_category_details',
216
+				'capability' => 'ee_edit_event_category',
217
+				'args'       => ['edit'],
218
+			],
219
+			'delete_categories'             => [
220
+				'func'       => '_delete_categories',
221
+				'capability' => 'ee_delete_event_category',
222
+				'noheader'   => true,
223
+			],
224
+			'delete_category'               => [
225
+				'func'       => '_delete_categories',
226
+				'capability' => 'ee_delete_event_category',
227
+				'noheader'   => true,
228
+			],
229
+			'insert_category'               => [
230
+				'func'       => '_insert_or_update_category',
231
+				'args'       => ['new_category' => true],
232
+				'capability' => 'ee_edit_event_category',
233
+				'noheader'   => true,
234
+			],
235
+			'update_category'               => [
236
+				'func'       => '_insert_or_update_category',
237
+				'args'       => ['new_category' => false],
238
+				'capability' => 'ee_edit_event_category',
239
+				'noheader'   => true,
240
+			],
241
+			'category_list'                 => [
242
+				'func'       => '_category_list_table',
243
+				'capability' => 'ee_manage_event_categories',
244
+			],
245
+			'preview_deletion' => [
246
+				'func' => 'previewDeletion',
247
+				'capability' => 'ee_delete_events',
248
+			],
249
+			'confirm_deletion' => [
250
+				'func' => 'confirmDeletion',
251
+				'capability' => 'ee_delete_events',
252
+				'noheader' => true,
253
+			]
254
+		];
255
+	}
256
+
257
+
258
+	/**
259
+	 * Set the _page_config property for this admin page group.
260
+	 */
261
+	protected function _set_page_config()
262
+	{
263
+		$this->_page_config = [
264
+			'default'                => [
265
+				'nav'           => [
266
+					'label' => esc_html__('Overview', 'event_espresso'),
267
+					'order' => 10,
299 268
 				],
300
-                'require_nonce' => false,
301
-                'qtips'         => ['EE_Event_List_Table_Tips'],
302
-            ],
303
-            'create_new'             => [
304
-                'nav'           => [
305
-                    'label'      => esc_html__('Add Event', 'event_espresso'),
306
-                    'order'      => 5,
307
-                    'persistent' => false,
308
-                ],
309
-                'metaboxes'     => ['_register_event_editor_meta_boxes'],
310
-                'help_tabs'     => [
311
-                    'event_editor_help_tab'                            => [
312
-                        'title'    => esc_html__('Event Editor', 'event_espresso'),
313
-                        'filename' => 'event_editor',
314
-                    ],
315
-                    'event_editor_title_richtexteditor_help_tab'       => [
316
-                        'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
317
-                        'filename' => 'event_editor_title_richtexteditor',
318
-                    ],
319
-                    'event_editor_venue_details_help_tab'              => [
320
-                        'title'    => esc_html__('Event Venue Details', 'event_espresso'),
321
-                        'filename' => 'event_editor_venue_details',
322
-                    ],
323
-                    'event_editor_event_datetimes_help_tab'            => [
324
-                        'title'    => esc_html__('Event Datetimes', 'event_espresso'),
325
-                        'filename' => 'event_editor_event_datetimes',
326
-                    ],
327
-                    'event_editor_event_tickets_help_tab'              => [
328
-                        'title'    => esc_html__('Event Tickets', 'event_espresso'),
329
-                        'filename' => 'event_editor_event_tickets',
330
-                    ],
331
-                    'event_editor_event_registration_options_help_tab' => [
332
-                        'title'    => esc_html__('Event Registration Options', 'event_espresso'),
333
-                        'filename' => 'event_editor_event_registration_options',
334
-                    ],
335
-                    'event_editor_tags_categories_help_tab'            => [
336
-                        'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
337
-                        'filename' => 'event_editor_tags_categories',
338
-                    ],
339
-                    'event_editor_questions_registrants_help_tab'      => [
340
-                        'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
341
-                        'filename' => 'event_editor_questions_registrants',
342
-                    ],
343
-                    'event_editor_save_new_event_help_tab'             => [
344
-                        'title'    => esc_html__('Save New Event', 'event_espresso'),
345
-                        'filename' => 'event_editor_save_new_event',
346
-                    ],
347
-                    'event_editor_other_help_tab'                      => [
348
-                        'title'    => esc_html__('Event Other', 'event_espresso'),
349
-                        'filename' => 'event_editor_other',
350
-                    ],
351
-                ],
352
-                // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
353
-                // 'help_tour'     => [
354
-                //     'Event_Editor_Help_Tour',
355
-                // ],
356
-                'qtips'         => ['EE_Event_Editor_Decaf_Tips'],
357
-                'require_nonce' => false,
358
-            ],
359
-            'edit'                   => [
360
-                'nav'           => [
361
-                    'label'      => esc_html__('Edit Event', 'event_espresso'),
362
-                    'order'      => 5,
363
-                    'persistent' => false,
364
-                    'url'        => isset($this->_req_data['post'])
365
-                        ? EE_Admin_Page::add_query_args_and_nonce(
366
-                            ['post' => $this->_req_data['post'], 'action' => 'edit'],
367
-                            $this->_current_page_view_url
368
-                        )
369
-                        : $this->_admin_base_url,
370
-                ],
371
-                'metaboxes'     => ['_register_event_editor_meta_boxes'],
372
-                'help_tabs'     => [
373
-                    'event_editor_help_tab'                            => [
374
-                        'title'    => esc_html__('Event Editor', 'event_espresso'),
375
-                        'filename' => 'event_editor',
376
-                    ],
377
-                    'event_editor_title_richtexteditor_help_tab'       => [
378
-                        'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
379
-                        'filename' => 'event_editor_title_richtexteditor',
380
-                    ],
381
-                    'event_editor_venue_details_help_tab'              => [
382
-                        'title'    => esc_html__('Event Venue Details', 'event_espresso'),
383
-                        'filename' => 'event_editor_venue_details',
384
-                    ],
385
-                    'event_editor_event_datetimes_help_tab'            => [
386
-                        'title'    => esc_html__('Event Datetimes', 'event_espresso'),
387
-                        'filename' => 'event_editor_event_datetimes',
388
-                    ],
389
-                    'event_editor_event_tickets_help_tab'              => [
390
-                        'title'    => esc_html__('Event Tickets', 'event_espresso'),
391
-                        'filename' => 'event_editor_event_tickets',
392
-                    ],
393
-                    'event_editor_event_registration_options_help_tab' => [
394
-                        'title'    => esc_html__('Event Registration Options', 'event_espresso'),
395
-                        'filename' => 'event_editor_event_registration_options',
396
-                    ],
397
-                    'event_editor_tags_categories_help_tab'            => [
398
-                        'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
399
-                        'filename' => 'event_editor_tags_categories',
400
-                    ],
401
-                    'event_editor_questions_registrants_help_tab'      => [
402
-                        'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
403
-                        'filename' => 'event_editor_questions_registrants',
404
-                    ],
405
-                    'event_editor_save_new_event_help_tab'             => [
406
-                        'title'    => esc_html__('Save New Event', 'event_espresso'),
407
-                        'filename' => 'event_editor_save_new_event',
408
-                    ],
409
-                    'event_editor_other_help_tab'                      => [
410
-                        'title'    => esc_html__('Event Other', 'event_espresso'),
411
-                        'filename' => 'event_editor_other',
412
-                    ],
413
-                ],
414
-                'require_nonce' => false,
415
-            ],
416
-            'default_event_settings' => [
417
-                'nav'           => [
418
-                    'label' => esc_html__('Default Settings', 'event_espresso'),
419
-                    'order' => 40,
420
-                ],
421
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, ['_publish_post_box']),
422
-                'labels'        => [
423
-                    'publishbox' => esc_html__('Update Settings', 'event_espresso'),
424
-                ],
425
-                'help_tabs'     => [
426
-                    'default_settings_help_tab'        => [
427
-                        'title'    => esc_html__('Default Event Settings', 'event_espresso'),
428
-                        'filename' => 'events_default_settings',
429
-                    ],
430
-                    'default_settings_status_help_tab' => [
431
-                        'title'    => esc_html__('Default Registration Status', 'event_espresso'),
432
-                        'filename' => 'events_default_settings_status',
433
-                    ],
434
-                    'default_maximum_tickets_help_tab' => [
435
-                        'title'    => esc_html__('Default Maximum Tickets Per Order', 'event_espresso'),
436
-                        'filename' => 'events_default_settings_max_tickets',
437
-                    ],
438
-                ],
439
-                // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
440
-                // 'help_tour'     => ['Event_Default_Settings_Help_Tour'],
441
-                'require_nonce' => false,
442
-            ],
443
-            // template settings
444
-            'template_settings'      => [
445
-                'nav'           => [
446
-                    'label' => esc_html__('Templates', 'event_espresso'),
447
-                    'order' => 30,
448
-                ],
449
-                'metaboxes'     => $this->_default_espresso_metaboxes,
450
-                'help_tabs'     => [
451
-                    'general_settings_templates_help_tab' => [
452
-                        'title'    => esc_html__('Templates', 'event_espresso'),
453
-                        'filename' => 'general_settings_templates',
454
-                    ],
455
-                ],
456
-                // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
457
-                // 'help_tour'     => ['Templates_Help_Tour'],
458
-                'require_nonce' => false,
459
-            ],
460
-            // event category stuff
461
-            'add_category'           => [
462
-                'nav'           => [
463
-                    'label'      => esc_html__('Add Category', 'event_espresso'),
464
-                    'order'      => 15,
465
-                    'persistent' => false,
466
-                ],
467
-                'help_tabs'     => [
468
-                    'add_category_help_tab' => [
469
-                        'title'    => esc_html__('Add New Event Category', 'event_espresso'),
470
-                        'filename' => 'events_add_category',
471
-                    ],
472
-                ],
473
-                // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
474
-                // 'help_tour'     => ['Event_Add_Category_Help_Tour'],
475
-                'metaboxes'     => ['_publish_post_box'],
476
-                'require_nonce' => false,
477
-            ],
478
-            'edit_category'          => [
479
-                'nav'           => [
480
-                    'label'      => esc_html__('Edit Category', 'event_espresso'),
481
-                    'order'      => 15,
482
-                    'persistent' => false,
483
-                    'url'        => isset($this->_req_data['EVT_CAT_ID'])
484
-                        ? add_query_arg(
485
-                            ['EVT_CAT_ID' => $this->_req_data['EVT_CAT_ID']],
486
-                            $this->_current_page_view_url
487
-                        )
488
-                        : $this->_admin_base_url,
489
-                ],
490
-                'help_tabs'     => [
491
-                    'edit_category_help_tab' => [
492
-                        'title'    => esc_html__('Edit Event Category', 'event_espresso'),
493
-                        'filename' => 'events_edit_category',
494
-                    ],
495
-                ],
496
-                /*'help_tour' => ['Event_Edit_Category_Help_Tour'],*/
497
-                'metaboxes'     => ['_publish_post_box'],
498
-                'require_nonce' => false,
499
-            ],
500
-            'category_list'          => [
501
-                'nav'           => [
502
-                    'label' => esc_html__('Categories', 'event_espresso'),
503
-                    'order' => 20,
504
-                ],
505
-                'list_table'    => 'Event_Categories_Admin_List_Table',
506
-                'help_tabs'     => [
507
-                    'events_categories_help_tab'                       => [
508
-                        'title'    => esc_html__('Event Categories', 'event_espresso'),
509
-                        'filename' => 'events_categories',
510
-                    ],
511
-                    'events_categories_table_column_headings_help_tab' => [
512
-                        'title'    => esc_html__('Event Categories Table Column Headings', 'event_espresso'),
513
-                        'filename' => 'events_categories_table_column_headings',
514
-                    ],
515
-                    'events_categories_view_help_tab'                  => [
516
-                        'title'    => esc_html__('Event Categories Views', 'event_espresso'),
517
-                        'filename' => 'events_categories_views',
518
-                    ],
519
-                    'events_categories_other_help_tab'                 => [
520
-                        'title'    => esc_html__('Event Categories Other', 'event_espresso'),
521
-                        'filename' => 'events_categories_other',
522
-                    ],
523
-                ],
524
-                // disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
525
-                // 'help_tour'     => [
526
-                //     'Event_Categories_Help_Tour',
527
-                // ],
528
-                'metaboxes'     => $this->_default_espresso_metaboxes,
529
-                'require_nonce' => false,
530
-            ],
531
-            'preview_deletion'       => [
532
-                'nav'           => [
533
-                    'label'      => esc_html__('Preview Deletion', 'event_espresso'),
534
-                    'order'      => 15,
535
-                    'persistent' => false,
536
-                    'url'        => '',
537
-                ],
538
-                'require_nonce' => false,
539
-            ],
540
-        ];
541
-        // only load EE_Event_Editor_Decaf_Tips if domain is not caffeinated
542
-        $domain = $this->loader->getShared('EventEspresso\core\domain\Domain');
543
-        if (! $domain->isCaffeinated()) {
544
-            $this->_page_config['create_new']['qtips'] = ['EE_Event_Editor_Decaf_Tips'];
545
-            $this->_page_config['edit']['qtips'] = ['EE_Event_Editor_Decaf_Tips'];
546
-        }
547
-    }
548
-
549
-
550
-    /**
551
-     * Used to register any global screen options if necessary for every route in this admin page group.
552
-     */
553
-    protected function _add_screen_options()
554
-    {
555
-    }
556
-
557
-
558
-    /**
559
-     * Implementing the screen options for the 'default' route.
560
-     *
561
-     * @throws InvalidArgumentException
562
-     * @throws InvalidDataTypeException
563
-     * @throws InvalidInterfaceException
564
-     */
565
-    protected function _add_screen_options_default()
566
-    {
567
-        $this->_per_page_screen_option();
568
-    }
569
-
570
-
571
-    /**
572
-     * Implementing screen options for the category list route.
573
-     *
574
-     * @throws InvalidArgumentException
575
-     * @throws InvalidDataTypeException
576
-     * @throws InvalidInterfaceException
577
-     */
578
-    protected function _add_screen_options_category_list()
579
-    {
580
-        $page_title = $this->_admin_page_title;
581
-        $this->_admin_page_title = esc_html__('Categories', 'event_espresso');
582
-        $this->_per_page_screen_option();
583
-        $this->_admin_page_title = $page_title;
584
-    }
585
-
586
-
587
-    /**
588
-     * Used to register any global feature pointers for the admin page group.
589
-     */
590
-    protected function _add_feature_pointers()
591
-    {
592
-    }
593
-
594
-
595
-    /**
596
-     * Registers and enqueues any global scripts and styles for the entire admin page group.
597
-     */
598
-    public function load_scripts_styles()
599
-    {
600
-        wp_register_style(
601
-            'events-admin-css',
602
-            EVENTS_ASSETS_URL . 'events-admin-page.css',
603
-            [],
604
-            EVENT_ESPRESSO_VERSION
605
-        );
606
-        wp_register_style(
607
-            'ee-cat-admin',
608
-            EVENTS_ASSETS_URL . 'ee-cat-admin.css',
609
-            [],
610
-            EVENT_ESPRESSO_VERSION
611
-        );
612
-        wp_enqueue_style('events-admin-css');
613
-        wp_enqueue_style('ee-cat-admin');
614
-        // scripts
615
-        wp_register_script(
616
-            'event_editor_js',
617
-            EVENTS_ASSETS_URL . 'event_editor.js',
618
-            ['ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'],
619
-            EVENT_ESPRESSO_VERSION,
620
-            true
621
-        );
622
-    }
623
-
624
-
625
-    /**
626
-     * Enqueuing scripts and styles specific to this view
627
-     */
628
-    public function load_scripts_styles_create_new()
629
-    {
630
-        $this->load_scripts_styles_edit();
631
-    }
632
-
633
-
634
-    /**
635
-     * Enqueuing scripts and styles specific to this view
636
-     */
637
-    public function load_scripts_styles_edit()
638
-    {
639
-        // styles
640
-        wp_enqueue_style('espresso-ui-theme');
641
-        wp_register_style(
642
-            'event-editor-css',
643
-            EVENTS_ASSETS_URL . 'event-editor.css',
644
-            ['ee-admin-css'],
645
-            EVENT_ESPRESSO_VERSION
646
-        );
647
-        wp_enqueue_style('event-editor-css');
648
-        // scripts
649
-        if (! $this->admin_config->useAdvancedEditor()) {
650
-            wp_register_script(
651
-                'event-datetime-metabox',
652
-                EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
653
-                ['event_editor_js', 'ee-datepicker'],
654
-                EVENT_ESPRESSO_VERSION
655
-            );
656
-            wp_enqueue_script('event-datetime-metabox');
657
-        }
658
-    }
659
-
660
-
661
-    /**
662
-     * Populating the _views property for the category list table view.
663
-     */
664
-    protected function _set_list_table_views_category_list()
665
-    {
666
-        $this->_views = [
667
-            'all' => [
668
-                'slug'        => 'all',
669
-                'label'       => esc_html__('All', 'event_espresso'),
670
-                'count'       => 0,
671
-                'bulk_action' => [
672
-                    'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
673
-                ],
674
-            ],
675
-        ];
676
-    }
677
-
678
-
679
-    /**
680
-     * For adding anything that fires on the admin_init hook for any route within this admin page group.
681
-     */
682
-    public function admin_init()
683
-    {
684
-        EE_Registry::$i18n_js_strings['image_confirm'] = esc_html__(
685
-            'Do you really want to delete this image? Please remember to update your event to complete the removal.',
686
-            'event_espresso'
687
-        );
688
-    }
689
-
690
-
691
-    /**
692
-     * For adding anything that should be triggered on the admin_notices hook for any route within this admin page
693
-     * group.
694
-     */
695
-    public function admin_notices()
696
-    {
697
-    }
698
-
699
-
700
-    /**
701
-     * For adding anything that should be triggered on the `admin_print_footer_scripts` hook for any route within
702
-     * this admin page group.
703
-     */
704
-    public function admin_footer_scripts()
705
-    {
706
-    }
707
-
708
-
709
-    /**
710
-     * Call this function to verify if an event is public and has tickets for sale.  If it does, then we need to show a
711
-     * warning (via EE_Error::add_error());
712
-     *
713
-     * @param EE_Event $event Event object
714
-     * @param string   $req_type
715
-     * @return void
716
-     * @throws EE_Error
717
-     * @access public
718
-     */
719
-    public function verify_event_edit($event = null, $req_type = '')
720
-    {
721
-        // don't need to do this when processing
722
-        if (! empty($req_type)) {
723
-            return;
724
-        }
725
-        // no event?
726
-        if (! $event instanceof EE_Event) {
727
-            $event = $this->_cpt_model_obj;
728
-        }
729
-        // STILL no event?
730
-        if (! $event instanceof EE_Event) {
731
-            return;
732
-        }
733
-        $orig_status = $event->status();
734
-        // first check if event is active.
735
-        if (
736
-            $orig_status === EEM_Event::cancelled
737
-            || $orig_status === EEM_Event::postponed
738
-            || $event->is_expired()
739
-            || $event->is_inactive()
740
-        ) {
741
-            return;
742
-        }
743
-        // made it here so it IS active... next check that any of the tickets are sold.
744
-        if ($event->is_sold_out(true)) {
745
-            if ($orig_status !== EEM_Event::sold_out && $event->status() !== $orig_status) {
746
-                EE_Error::add_attention(
747
-                    sprintf(
748
-                        esc_html__(
749
-                            'Please note that the Event Status has automatically been changed to %s because there are no more spaces available for this event.  However, this change is not permanent until you update the event.  You can change the status back to something else before updating if you wish.',
750
-                            'event_espresso'
751
-                        ),
752
-                        EEH_Template::pretty_status(EEM_Event::sold_out, false, 'sentence')
753
-                    )
754
-                );
755
-            }
756
-            return;
757
-        }
758
-        if ($orig_status === EEM_Event::sold_out) {
759
-            EE_Error::add_attention(
760
-                sprintf(
761
-                    esc_html__(
762
-                        'Please note that the Event Status has automatically been changed to %s because more spaces have become available for this event, most likely due to abandoned transactions freeing up reserved tickets.  However, this change is not permanent until you update the event. If you wish, you can change the status back to something else before updating.',
763
-                        'event_espresso'
764
-                    ),
765
-                    EEH_Template::pretty_status($event->status(), false, 'sentence')
766
-                )
767
-            );
768
-        }
769
-        // now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
770
-        if (! $event->tickets_on_sale()) {
771
-            return;
772
-        }
773
-        // made it here so show warning
774
-        $this->_edit_event_warning();
775
-    }
776
-
777
-
778
-    /**
779
-     * This is the text used for when an event is being edited that is public and has tickets for sale.
780
-     * When needed, hook this into a EE_Error::add_error() notice.
781
-     *
782
-     * @access protected
783
-     * @return void
784
-     */
785
-    protected function _edit_event_warning()
786
-    {
787
-        // we don't want to add warnings during these requests
788
-        if (isset($this->_req_data['action']) && $this->_req_data['action'] === 'editpost') {
789
-            return;
790
-        }
791
-        EE_Error::add_attention(
792
-            sprintf(
793
-                esc_html__(
794
-                    'Your event is open for registration. Making changes may disrupt any transactions in progress. %sLearn more%s',
795
-                    'event_espresso'
796
-                ),
797
-                '<a class="espresso-help-tab-lnk">',
798
-                '</a>'
799
-            )
800
-        );
801
-    }
802
-
803
-
804
-    /**
805
-     * When a user is creating a new event, notify them if they haven't set their timezone.
806
-     * Otherwise, do the normal logic
807
-     *
808
-     * @return string
809
-     * @throws EE_Error
810
-     * @throws InvalidArgumentException
811
-     * @throws InvalidDataTypeException
812
-     * @throws InvalidInterfaceException
813
-     */
814
-    protected function _create_new_cpt_item()
815
-    {
816
-        $has_timezone_string = get_option('timezone_string');
817
-        // only nag them about setting their timezone if it's their first event, and they haven't already done it
818
-        if (! $has_timezone_string && ! EEM_Event::instance()->exists([])) {
819
-            EE_Error::add_attention(
820
-                sprintf(
821
-                    __(
822
-                        'Your website\'s timezone is currently set to a UTC offset. We recommend updating your timezone to a city or region near you before you create an event. Change your timezone now:%1$s%2$s%3$sChange Timezone%4$s',
823
-                        'event_espresso'
824
-                    ),
825
-                    '<br>',
826
-                    '<select id="timezone_string" name="timezone_string" aria-describedby="timezone-description">'
827
-                    . EEH_DTT_Helper::wp_timezone_choice('', EEH_DTT_Helper::get_user_locale())
828
-                    . '</select>',
829
-                    '<button class="button button-secondary timezone-submit">',
830
-                    '</button><span class="spinner"></span>'
831
-                ),
832
-                __FILE__,
833
-                __FUNCTION__,
834
-                __LINE__
835
-            );
836
-        }
837
-        parent::_create_new_cpt_item();
838
-    }
839
-
840
-
841
-    /**
842
-     * Sets the _views property for the default route in this admin page group.
843
-     */
844
-    protected function _set_list_table_views_default()
845
-    {
846
-        $this->_views = [
847
-            'all'   => [
848
-                'slug'        => 'all',
849
-                'label'       => esc_html__('View All Events', 'event_espresso'),
850
-                'count'       => 0,
851
-                'bulk_action' => [
852
-                    'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
853
-                ],
854
-            ],
855
-            'draft' => [
856
-                'slug'        => 'draft',
857
-                'label'       => esc_html__('Draft', 'event_espresso'),
858
-                'count'       => 0,
859
-                'bulk_action' => [
860
-                    'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
861
-                ],
862
-            ],
863
-        ];
864
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_events', 'espresso_events_trash_events')) {
865
-            $this->_views['trash'] = [
866
-                'slug'        => 'trash',
867
-                'label'       => esc_html__('Trash', 'event_espresso'),
868
-                'count'       => 0,
869
-                'bulk_action' => [
870
-                    'restore_events' => esc_html__('Restore From Trash', 'event_espresso'),
871
-                    'delete_events'  => esc_html__('Delete Permanently', 'event_espresso'),
872
-                ],
873
-            ];
874
-        }
875
-    }
876
-
877
-
878
-    /**
879
-     * Provides the legend item array for the default list table view.
880
-     *
881
-     * @return array
882
-     */
883
-    protected function _event_legend_items()
884
-    {
885
-        $items = [
886
-            'view_details'   => [
887
-                'class' => 'dashicons dashicons-search',
888
-                'desc'  => esc_html__('View Event', 'event_espresso'),
889
-            ],
890
-            'edit_event'     => [
891
-                'class' => 'ee-icon ee-icon-calendar-edit',
892
-                'desc'  => esc_html__('Edit Event Details', 'event_espresso'),
893
-            ],
894
-            'view_attendees' => [
895
-                'class' => 'dashicons dashicons-groups',
896
-                'desc'  => esc_html__('View Registrations for Event', 'event_espresso'),
897
-            ],
898
-        ];
899
-        $items = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
900
-        $statuses = [
901
-            'sold_out_status'  => [
902
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
903
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
904
-            ],
905
-            'active_status'    => [
906
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
907
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
908
-            ],
909
-            'upcoming_status'  => [
910
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
911
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
912
-            ],
913
-            'postponed_status' => [
914
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
915
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
916
-            ],
917
-            'cancelled_status' => [
918
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
919
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
920
-            ],
921
-            'expired_status'   => [
922
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
923
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
924
-            ],
925
-            'inactive_status'  => [
926
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
927
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
928
-            ],
929
-        ];
930
-        $statuses = apply_filters('FHEE__Events_Admin_Page__event_legend_items__statuses', $statuses);
931
-        return array_merge($items, $statuses);
932
-    }
933
-
934
-
935
-    /**
936
-     * @return EEM_Event
937
-     * @throws EE_Error
938
-     * @throws InvalidArgumentException
939
-     * @throws InvalidDataTypeException
940
-     * @throws InvalidInterfaceException
941
-     * @throws ReflectionException
942
-     */
943
-    private function _event_model()
944
-    {
945
-        if (! $this->_event_model instanceof EEM_Event) {
946
-            $this->_event_model = EE_Registry::instance()->load_model('Event');
947
-        }
948
-        return $this->_event_model;
949
-    }
950
-
951
-
952
-    /**
953
-     * Adds extra buttons to the WP CPT permalink field row.
954
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
955
-     *
956
-     * @param string $return    the current html
957
-     * @param int    $id        the post id for the page
958
-     * @param string $new_title What the title is
959
-     * @param string $new_slug  what the slug is
960
-     * @return string            The new html string for the permalink area
961
-     */
962
-    public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
963
-    {
964
-        // make sure this is only when editing
965
-        if (! empty($id)) {
966
-            $post = get_post($id);
967
-            $return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
968
-                       . esc_html__('Shortcode', 'event_espresso')
969
-                       . '</a> ';
970
-            $return .= '<input id="shortcode" type="hidden" value="[ESPRESSO_TICKET_SELECTOR event_id='
971
-                       . $post->ID
972
-                       . ']">';
973
-        }
974
-        return $return;
975
-    }
976
-
977
-
978
-    /**
979
-     * _events_overview_list_table
980
-     * This contains the logic for showing the events_overview list
981
-     *
982
-     * @access protected
983
-     * @return void
984
-     * @throws DomainException
985
-     * @throws EE_Error
986
-     * @throws InvalidArgumentException
987
-     * @throws InvalidDataTypeException
988
-     * @throws InvalidInterfaceException
989
-     */
990
-    protected function _events_overview_list_table()
991
-    {
992
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
993
-        $this->_template_args['after_list_table'] = ! empty($this->_template_args['after_list_table'])
994
-            ? (array) $this->_template_args['after_list_table']
995
-            : [];
996
-        $this->_template_args['after_list_table']['view_event_list_button'] = EEH_HTML::br()
997
-                                                                              . EEH_Template::get_button_or_link(
998
-                                                                                  get_post_type_archive_link('espresso_events'),
999
-                                                                                  esc_html__('View Event Archive Page', 'event_espresso'),
1000
-                                                                                  'button'
1001
-                                                                              );
1002
-        $this->_template_args['after_list_table']['legend'] = $this->_display_legend($this->_event_legend_items());
1003
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
1004
-            'create_new',
1005
-            'add',
1006
-            [],
1007
-            'add-new-h2'
1008
-        );
1009
-        $this->display_admin_list_table_page_with_no_sidebar();
1010
-    }
1011
-
1012
-
1013
-    /**
1014
-     * this allows for extra misc actions in the default WP publish box
1015
-     *
1016
-     * @return void
1017
-     * @throws DomainException
1018
-     * @throws EE_Error
1019
-     * @throws InvalidArgumentException
1020
-     * @throws InvalidDataTypeException
1021
-     * @throws InvalidInterfaceException
1022
-     * @throws ReflectionException
1023
-     */
1024
-    public function extra_misc_actions_publish_box()
1025
-    {
1026
-        $this->_generate_publish_box_extra_content();
1027
-    }
1028
-
1029
-
1030
-    /**
1031
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
1032
-     * saved.
1033
-     * Typically you would use this to save any additional data.
1034
-     * Keep in mind also that "save_post" runs on EVERY post update to the database.
1035
-     * ALSO very important.  When a post transitions from scheduled to published,
1036
-     * the save_post action is fired but you will NOT have any _POST data containing any extra info you may have from
1037
-     * other meta saves. So MAKE sure that you handle this accordingly.
1038
-     *
1039
-     * @access protected
1040
-     * @abstract
1041
-     * @param string $post_id The ID of the cpt that was saved (so you can link relationally)
1042
-     * @param object $post    The post object of the cpt that was saved.
1043
-     * @return void
1044
-     * @throws EE_Error
1045
-     * @throws InvalidArgumentException
1046
-     * @throws InvalidDataTypeException
1047
-     * @throws InvalidInterfaceException
1048
-     * @throws ReflectionException
1049
-     */
1050
-    protected function _insert_update_cpt_item($post_id, $post)
1051
-    {
1052
-        if ($post instanceof WP_Post && $post->post_type !== 'espresso_events') {
1053
-            // get out we're not processing an event save.
1054
-            return;
1055
-        }
1056
-        $event_values = [
1057
-            'EVT_member_only'     => ! empty($this->_req_data['member_only']) ? 1 : 0,
1058
-            'EVT_allow_overflow'  => ! empty($this->_req_data['EVT_allow_overflow']) ? 1 : 0,
1059
-            'EVT_timezone_string' => ! empty($this->_req_data['timezone_string'])
1060
-                ? sanitize_text_field($this->_req_data['timezone_string'])
1061
-                : null,
1062
-        ];
1063
-        /** @var FeatureFlags $flags */
1064
-        $flags = $this->loader->getShared('EventEspresso\core\domain\services\capabilities\FeatureFlags');
1065
-        // check if the new EDTR reg options meta box is being used, and if so, don't run updates for legacy version
1066
-        if (! $this->admin_config->useAdvancedEditor() || ! $flags->featureAllowed('use_reg_options_meta_box')) {
1067
-            $event_values['EVT_display_ticket_selector'] =
1068
-                ! empty($this->_req_data['display_ticket_selector'])
1069
-                    ? 1
1070
-                    : 0;
1071
-            $event_values['EVT_additional_limit'] = min(
1072
-                apply_filters('FHEE__EE_Events_Admin__insert_update_cpt_item__EVT_additional_limit_max', 255),
1073
-                ! empty($this->_req_data['additional_limit'])
1074
-                    ? absint($this->_req_data['additional_limit'])
1075
-                    : null
1076
-            );
1077
-            $event_values['EVT_default_registration_status'] =
1078
-                ! empty($this->_req_data['EVT_default_registration_status'])
1079
-                    ? sanitize_text_field($this->_req_data['EVT_default_registration_status'])
1080
-                    : EE_Registry::instance()->CFG->registration->default_STS_ID;
1081
-            $event_values['EVT_external_URL'] = ! empty($this->_req_data['externalURL'])
1082
-                ? esc_url_raw($this->_req_data['externalURL'])
1083
-                : null;
1084
-            $event_values['EVT_phone'] = ! empty($this->_req_data['event_phone'])
1085
-                ? sanitize_text_field($this->_req_data['event_phone'])
1086
-                : null;
1087
-        }
1088
-        // update event
1089
-        $success = $this->_event_model()->update_by_ID($event_values, $post_id);
1090
-        // get event_object for other metaboxes... though it would seem to make sense to just use $this->_event_model()->get_one_by_ID( $post_id ).. i have to setup where conditions to override the filters in the model that filter out autodraft and inherit statuses so we GET the inherit id!
1091
-        $get_one_where = [
1092
-            $this->_event_model()->primary_key_name() => $post_id,
1093
-            'OR'                                      => [
1094
-                'status'   => $post->post_status,
1095
-                // if trying to "Publish" a sold out event, it's status will get switched back to "sold_out" in the db,
1096
-                // but the returned object here has a status of "publish", so use the original post status as well
1097
-                'status*1' => $this->_req_data['original_post_status'],
1098
-            ],
1099
-        ];
1100
-        $event = $this->_event_model()->get_one([$get_one_where]);
1101
-        // the following are default callbacks for event attachment updates that can be overridden by caffeinated functionality and/or addons.
1102
-        $event_update_callbacks = apply_filters(
1103
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
1104
-            [
1105
-                [$this, '_default_venue_update'],
1106
-                [$this, '_default_tickets_update'],
1107
-            ]
1108
-        );
1109
-        $att_success = true;
1110
-        foreach ($event_update_callbacks as $e_callback) {
1111
-            $_success = is_callable($e_callback)
1112
-                ? $e_callback($event, $this->_req_data)
1113
-                : false;
1114
-            // if ANY of these updates fail then we want the appropriate global error message
1115
-            $att_success = ! $att_success ? $att_success : $_success;
1116
-        }
1117
-        // any errors?
1118
-        if ($success && false === $att_success) {
1119
-            EE_Error::add_error(
1120
-                esc_html__(
1121
-                    'Event Details saved successfully but something went wrong with saving attachments.',
1122
-                    'event_espresso'
1123
-                ),
1124
-                __FILE__,
1125
-                __FUNCTION__,
1126
-                __LINE__
1127
-            );
1128
-        } elseif ($success === false) {
1129
-            EE_Error::add_error(
1130
-                esc_html__('Event Details did not save successfully.', 'event_espresso'),
1131
-                __FILE__,
1132
-                __FUNCTION__,
1133
-                __LINE__
1134
-            );
1135
-        }
1136
-    }
1137
-
1138
-
1139
-    /**
1140
-     * @param int $post_id
1141
-     * @param int $revision_id
1142
-     * @throws EE_Error
1143
-     * @throws InvalidArgumentException
1144
-     * @throws InvalidDataTypeException
1145
-     * @throws InvalidInterfaceException
1146
-     * @throws ReflectionException
1147
-     * @see parent::restore_item()
1148
-     */
1149
-    protected function _restore_cpt_item($post_id, $revision_id)
1150
-    {
1151
-        // copy existing event meta to new post
1152
-        $post_evt = $this->_event_model()->get_one_by_ID($post_id);
1153
-        if ($post_evt instanceof EE_Event) {
1154
-            // meta revision restore
1155
-            $post_evt->restore_revision($revision_id);
1156
-            // related objs restore
1157
-            $post_evt->restore_revision($revision_id, ['Venue', 'Datetime', 'Price']);
1158
-        }
1159
-    }
1160
-
1161
-
1162
-    /**
1163
-     * Attach the venue to the Event
1164
-     *
1165
-     * @param EE_Event $evtobj Event Object to add the venue to
1166
-     * @param array    $data   The request data from the form
1167
-     * @return bool           Success or fail.
1168
-     * @throws EE_Error
1169
-     * @throws InvalidArgumentException
1170
-     * @throws InvalidDataTypeException
1171
-     * @throws InvalidInterfaceException
1172
-     * @throws ReflectionException
1173
-     */
1174
-    protected function _default_venue_update(EE_Event $evtobj, $data)
1175
-    {
1176
-        require_once(EE_MODELS . 'EEM_Venue.model.php');
1177
-        $venue_model = EE_Registry::instance()->load_model('Venue');
1178
-        $rows_affected = null;
1179
-        $venue_id = ! empty($data['venue_id']) ? $data['venue_id'] : null;
1180
-        // very important.  If we don't have a venue name...
1181
-        // then we'll get out because not necessary to create empty venue
1182
-        if (empty($data['venue_title'])) {
1183
-            return false;
1184
-        }
1185
-        $venue_array = [
1186
-            'VNU_wp_user'         => $evtobj->get('EVT_wp_user'),
1187
-            'VNU_name'            => ! empty($data['venue_title']) ? $data['venue_title'] : null,
1188
-            'VNU_desc'            => ! empty($data['venue_description']) ? $data['venue_description'] : null,
1189
-            'VNU_identifier'      => ! empty($data['venue_identifier']) ? $data['venue_identifier'] : null,
1190
-            'VNU_short_desc'      => ! empty($data['venue_short_description']) ? $data['venue_short_description']
1191
-                : null,
1192
-            'VNU_address'         => ! empty($data['address']) ? $data['address'] : null,
1193
-            'VNU_address2'        => ! empty($data['address2']) ? $data['address2'] : null,
1194
-            'VNU_city'            => ! empty($data['city']) ? $data['city'] : null,
1195
-            'STA_ID'              => ! empty($data['state']) ? $data['state'] : null,
1196
-            'CNT_ISO'             => ! empty($data['countries']) ? $data['countries'] : null,
1197
-            'VNU_zip'             => ! empty($data['zip']) ? $data['zip'] : null,
1198
-            'VNU_phone'           => ! empty($data['venue_phone']) ? $data['venue_phone'] : null,
1199
-            'VNU_capacity'        => ! empty($data['venue_capacity']) ? $data['venue_capacity'] : null,
1200
-            'VNU_url'             => ! empty($data['venue_url']) ? $data['venue_url'] : null,
1201
-            'VNU_virtual_phone'   => ! empty($data['virtual_phone']) ? $data['virtual_phone'] : null,
1202
-            'VNU_virtual_url'     => ! empty($data['virtual_url']) ? $data['virtual_url'] : null,
1203
-            'VNU_enable_for_gmap' => isset($data['enable_for_gmap']) ? 1 : 0,
1204
-            'status'              => 'publish',
1205
-        ];
1206
-        // if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1207
-        if (! empty($venue_id)) {
1208
-            $update_where = [$venue_model->primary_key_name() => $venue_id];
1209
-            $rows_affected = $venue_model->update($venue_array, [$update_where]);
1210
-            // we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
1211
-            $evtobj->_add_relation_to($venue_id, 'Venue');
1212
-            return $rows_affected > 0;
1213
-        }
1214
-        // we insert the venue
1215
-        $venue_id = $venue_model->insert($venue_array);
1216
-        $evtobj->_add_relation_to($venue_id, 'Venue');
1217
-        return ! empty($venue_id) ? true : false;
1218
-        // when we have the ancestor come in it's already been handled by the revision save.
1219
-    }
1220
-
1221
-
1222
-    /**
1223
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
1224
-     *
1225
-     * @param EE_Event $evtobj The Event object we're attaching data to
1226
-     * @param array    $data   The request data from the form
1227
-     * @return array
1228
-     * @throws EE_Error
1229
-     * @throws InvalidArgumentException
1230
-     * @throws InvalidDataTypeException
1231
-     * @throws InvalidInterfaceException
1232
-     * @throws ReflectionException
1233
-     * @throws Exception
1234
-     */
1235
-    protected function _default_tickets_update(EE_Event $evtobj, $data)
1236
-    {
1237
-        if ($this->admin_config->useAdvancedEditor()) {
1238
-            return [];
1239
-        }
1240
-        $success = true;
1241
-        $saved_dtt = null;
1242
-        $saved_tickets = [];
1243
-        $incoming_date_formats = ['Y-m-d', 'h:i a'];
1244
-        foreach ($data['edit_event_datetimes'] as $row => $dtt) {
1245
-            // trim all values to ensure any excess whitespace is removed.
1246
-            $dtt = array_map('trim', $dtt);
1247
-            $dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && ! empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end']
1248
-                : $dtt['DTT_EVT_start'];
1249
-            $datetime_values = [
1250
-                'DTT_ID'        => ! empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : null,
1251
-                'DTT_EVT_start' => $dtt['DTT_EVT_start'],
1252
-                'DTT_EVT_end'   => $dtt['DTT_EVT_end'],
1253
-                'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? EE_INF : $dtt['DTT_reg_limit'],
1254
-                'DTT_order'     => $row,
1255
-            ];
1256
-            // if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
1257
-            if (! empty($dtt['DTT_ID'])) {
1258
-                $DTM = EE_Registry::instance()
1259
-                                  ->load_model('Datetime', [$evtobj->get_timezone()])
1260
-                                  ->get_one_by_ID($dtt['DTT_ID']);
1261
-                $DTM->set_date_format($incoming_date_formats[0]);
1262
-                $DTM->set_time_format($incoming_date_formats[1]);
1263
-                foreach ($datetime_values as $field => $value) {
1264
-                    $DTM->set($field, $value);
1265
-                }
1266
-                // make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
1267
-                $saved_dtts[ $DTM->ID() ] = $DTM;
1268
-            } else {
1269
-                $DTM = EE_Registry::instance()->load_class(
1270
-                    'Datetime',
1271
-                    [$datetime_values, $evtobj->get_timezone(), $incoming_date_formats],
1272
-                    false,
1273
-                    false
1274
-                );
1275
-                foreach ($datetime_values as $field => $value) {
1276
-                    $DTM->set($field, $value);
1277
-                }
1278
-            }
1279
-            $DTM->save();
1280
-            $DTT = $evtobj->_add_relation_to($DTM, 'Datetime');
1281
-            // load DTT helper
1282
-            // before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
1283
-            if ($DTT->get_raw('DTT_EVT_start') > $DTT->get_raw('DTT_EVT_end')) {
1284
-                $DTT->set('DTT_EVT_end', $DTT->get('DTT_EVT_start'));
1285
-                $DTT = EEH_DTT_Helper::date_time_add($DTT, 'DTT_EVT_end', 'days');
1286
-                $DTT->save();
1287
-            }
1288
-            // now we got to make sure we add the new DTT_ID to the $saved_dtts array  because it is possible there was a new one created for the autosave.
1289
-            $saved_dtt = $DTT;
1290
-            $success = ! $success ? $success : $DTT;
1291
-            // if ANY of these updates fail then we want the appropriate global error message.
1292
-            // //todo this is actually sucky we need a better error message but this is what it is for now.
1293
-        }
1294
-        // no dtts get deleted so we don't do any of that logic here.
1295
-        // update tickets next
1296
-        $old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
1297
-        foreach ($data['edit_tickets'] as $row => $tkt) {
1298
-            $incoming_date_formats = ['Y-m-d', 'h:i a'];
1299
-            $update_prices = false;
1300
-            $ticket_price = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1301
-                ? $data['edit_prices'][ $row ][1]['PRC_amount'] : 0;
1302
-            // trim inputs to ensure any excess whitespace is removed.
1303
-            $tkt = array_map('trim', $tkt);
1304
-            if (empty($tkt['TKT_start_date'])) {
1305
-                // let's use now in the set timezone.
1306
-                $now = new DateTime('now', new DateTimeZone($evtobj->get_timezone()));
1307
-                $tkt['TKT_start_date'] = $now->format($incoming_date_formats[0] . ' ' . $incoming_date_formats[1]);
1308
-            }
1309
-            if (empty($tkt['TKT_end_date'])) {
1310
-                // use the start date of the first datetime
1311
-                $dtt = $evtobj->first_datetime();
1312
-                $tkt['TKT_end_date'] = $dtt->start_date_and_time(
1313
-                    $incoming_date_formats[0],
1314
-                    $incoming_date_formats[1]
1315
-                );
1316
-            }
1317
-            $TKT_values = [
1318
-                'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
1319
-                'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
1320
-                'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
1321
-                'TKT_description' => ! empty($tkt['TKT_description']) ? $tkt['TKT_description'] : '',
1322
-                'TKT_start_date'  => $tkt['TKT_start_date'],
1323
-                'TKT_end_date'    => $tkt['TKT_end_date'],
1324
-                'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === '' ? EE_INF : $tkt['TKT_qty'],
1325
-                'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === '' ? EE_INF : $tkt['TKT_uses'],
1326
-                'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
1327
-                'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
1328
-                'TKT_row'         => $row,
1329
-                'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : $row,
1330
-                'TKT_price'       => $ticket_price,
1331
-            ];
1332
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly, which means in turn that the prices will become new prices as well.
1333
-            if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
1334
-                $TKT_values['TKT_ID'] = 0;
1335
-                $TKT_values['TKT_is_default'] = 0;
1336
-                $TKT_values['TKT_price'] = $ticket_price;
1337
-                $update_prices = true;
1338
-            }
1339
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
1340
-            // we actually do our saves a head of doing any add_relations to because its entirely possible that this ticket didn't removed or added to any datetime in the session but DID have it's items modified.
1341
-            // keep in mind that if the TKT has been sold (and we have changed pricing information), then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1342
-            if (! empty($tkt['TKT_ID'])) {
1343
-                $TKT = EE_Registry::instance()
1344
-                                  ->load_model('Ticket', [$evtobj->get_timezone()])
1345
-                                  ->get_one_by_ID($tkt['TKT_ID']);
1346
-                if ($TKT instanceof EE_Ticket) {
1347
-                    $ticket_sold = $TKT->count_related(
1348
-                        'Registration',
1349
-                        [
1350
-                                [
1351
-                                    'STS_ID' => [
1352
-                                        'NOT IN',
1353
-                                        [EEM_Registration::status_id_incomplete],
1354
-                                    ],
1355
-                                ],
1356
-                            ]
1357
-                    ) > 0;
1358
-                    // let's just check the total price for the existing ticket and determine if it matches the new
1359
-                    // total price.  if they are different then we create a new ticket (if tickets sold)
1360
-                    // if they aren't different then we go ahead and modify existing ticket.
1361
-                    $create_new_TKT = $ticket_sold && ! $TKT->deleted()
1362
-                                      && EEH_Money::compare_floats(
1363
-                                          $ticket_price,
1364
-                                          $TKT->get('TKT_price'),
1365
-                                          '!=='
1366
-                                      );
1367
-                    $TKT->set_date_format($incoming_date_formats[0]);
1368
-                    $TKT->set_time_format($incoming_date_formats[1]);
1369
-                    // set new values
1370
-                    foreach ($TKT_values as $field => $value) {
1371
-                        if ($field === 'TKT_qty') {
1372
-                            $TKT->set_qty($value);
1373
-                        } else {
1374
-                            $TKT->set($field, $value);
1375
-                        }
1376
-                    }
1377
-                    // if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
1378
-                    if ($create_new_TKT) {
1379
-                        // archive the old ticket first
1380
-                        $TKT->set('TKT_deleted', 1);
1381
-                        $TKT->save();
1382
-                        // make sure this ticket is still recorded in our saved_tkts so we don't run it through the regular trash routine.
1383
-                        $saved_tickets[ $TKT->ID() ] = $TKT;
1384
-                        // create new ticket that's a copy of the existing except a new id of course (and not archived) AND has the new TKT_price associated with it.
1385
-                        $TKT = clone $TKT;
1386
-                        $TKT->set('TKT_ID', 0);
1387
-                        $TKT->set('TKT_deleted', 0);
1388
-                        $TKT->set('TKT_price', $ticket_price);
1389
-                        $TKT->set('TKT_sold', 0);
1390
-                        // now we need to make sure that $new prices are created as well and attached to new ticket.
1391
-                        $update_prices = true;
1392
-                    }
1393
-                    // make sure price is set if it hasn't been already
1394
-                    $TKT->set('TKT_price', $ticket_price);
1395
-                }
1396
-            } else {
1397
-                // no TKT_id so a new TKT
1398
-                $TKT_values['TKT_price'] = $ticket_price;
1399
-                $TKT = EE_Registry::instance()->load_class('Ticket', [$TKT_values], false, false);
1400
-                if ($TKT instanceof EE_Ticket) {
1401
-                    // need to reset values to properly account for the date formats
1402
-                    $TKT->set_date_format($incoming_date_formats[0]);
1403
-                    $TKT->set_time_format($incoming_date_formats[1]);
1404
-                    $TKT->set_timezone($evtobj->get_timezone());
1405
-                    // set new values
1406
-                    foreach ($TKT_values as $field => $value) {
1407
-                        if ($field === 'TKT_qty') {
1408
-                            $TKT->set_qty($value);
1409
-                        } else {
1410
-                            $TKT->set($field, $value);
1411
-                        }
1412
-                    }
1413
-                    $update_prices = true;
1414
-                }
1415
-            }
1416
-            // cap ticket qty by datetime reg limits
1417
-            $TKT->set_qty(min($TKT->qty(), $TKT->qty('reg_limit')));
1418
-            // update ticket.
1419
-            $TKT->save();
1420
-            // before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
1421
-            if ($TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date')) {
1422
-                $TKT->set('TKT_end_date', $TKT->get('TKT_start_date'));
1423
-                $TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
1424
-                $TKT->save();
1425
-            }
1426
-            // initially let's add the ticket to the dtt
1427
-            $saved_dtt->_add_relation_to($TKT, 'Ticket');
1428
-            $saved_tickets[ $TKT->ID() ] = $TKT;
1429
-            // add prices to ticket
1430
-            $this->_add_prices_to_ticket($data['edit_prices'][ $row ], $TKT, $update_prices);
1431
-        }
1432
-        // however now we need to handle permanently deleting tickets via the ui.  Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.  However, it does allow for deleting tickets that have no tickets sold, in which case we want to get rid of permanently because there is no need to save in db.
1433
-        $old_tickets = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
1434
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
1435
-        foreach ($tickets_removed as $id) {
1436
-            $id = absint($id);
1437
-            // get the ticket for this id
1438
-            $tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
1439
-            // need to get all the related datetimes on this ticket and remove from every single one of them (remember this process can ONLY kick off if there are NO tkts_sold)
1440
-            $dtts = $tkt_to_remove->get_many_related('Datetime');
1441
-            foreach ($dtts as $dtt) {
1442
-                $tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
1443
-            }
1444
-            // need to do the same for prices (except these prices can also be deleted because again, tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
1445
-            $tkt_to_remove->delete_related_permanently('Price');
1446
-            // finally let's delete this ticket (which should not be blocked at this point b/c we've removed all our relationships)
1447
-            $tkt_to_remove->delete_permanently();
1448
-        }
1449
-        return [$saved_dtt, $saved_tickets];
1450
-    }
1451
-
1452
-
1453
-    /**
1454
-     * This attaches a list of given prices to a ticket.
1455
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
1456
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
1457
-     * price info and prices are automatically "archived" via the ticket.
1458
-     *
1459
-     * @access  private
1460
-     * @param array     $prices     Array of prices from the form.
1461
-     * @param EE_Ticket $ticket     EE_Ticket object that prices are being attached to.
1462
-     * @param bool      $new_prices Whether attach existing incoming prices or create new ones.
1463
-     * @return  void
1464
-     * @throws EE_Error
1465
-     * @throws InvalidArgumentException
1466
-     * @throws InvalidDataTypeException
1467
-     * @throws InvalidInterfaceException
1468
-     * @throws ReflectionException
1469
-     */
1470
-    private function _add_prices_to_ticket($prices, EE_Ticket $ticket, $new_prices = false)
1471
-    {
1472
-        foreach ($prices as $row => $prc) {
1473
-            $PRC_values = [
1474
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
1475
-                'PRT_ID'         => ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null,
1476
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
1477
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
1478
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
1479
-                'PRC_is_default' => 0, // make sure prices are NOT set as default from this context
1480
-                'PRC_order'      => $row,
1481
-            ];
1482
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
1483
-                $PRC_values['PRC_ID'] = 0;
1484
-                $PRC = EE_Registry::instance()->load_class('Price', [$PRC_values], false, false);
1485
-            } else {
1486
-                $PRC = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
1487
-                // update this price with new values
1488
-                foreach ($PRC_values as $field => $newprc) {
1489
-                    $PRC->set($field, $newprc);
1490
-                }
1491
-                $PRC->save();
1492
-            }
1493
-            $ticket->_add_relation_to($PRC, 'Price');
1494
-        }
1495
-    }
1496
-
1497
-
1498
-    /**
1499
-     * Add in our autosave ajax handlers
1500
-     *
1501
-     */
1502
-    protected function _ee_autosave_create_new()
1503
-    {
1504
-    }
1505
-
1506
-
1507
-    /**
1508
-     * More autosave handlers.
1509
-     */
1510
-    protected function _ee_autosave_edit()
1511
-    {
1512
-    }
1513
-
1514
-
1515
-    /**
1516
-     *    _generate_publish_box_extra_content
1517
-     *
1518
-     * @throws DomainException
1519
-     * @throws EE_Error
1520
-     * @throws InvalidArgumentException
1521
-     * @throws InvalidDataTypeException
1522
-     * @throws InvalidInterfaceException
1523
-     * @throws ReflectionException
1524
-     */
1525
-    private function _generate_publish_box_extra_content()
1526
-    {
1527
-        // load formatter helper
1528
-        // args for getting related registrations
1529
-        $approved_query_args = [
1530
-            [
1531
-                'REG_deleted' => 0,
1532
-                'STS_ID'      => EEM_Registration::status_id_approved,
1533
-            ],
1534
-        ];
1535
-        $not_approved_query_args = [
1536
-            [
1537
-                'REG_deleted' => 0,
1538
-                'STS_ID'      => EEM_Registration::status_id_not_approved,
1539
-            ],
1540
-        ];
1541
-        $pending_payment_query_args = [
1542
-            [
1543
-                'REG_deleted' => 0,
1544
-                'STS_ID'      => EEM_Registration::status_id_pending_payment,
1545
-            ],
1546
-        ];
1547
-        // publish box
1548
-        $publish_box_extra_args = [
1549
-            'view_approved_reg_url'        => add_query_arg(
1550
-                [
1551
-                    'action'      => 'default',
1552
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1553
-                    '_reg_status' => EEM_Registration::status_id_approved,
1554
-                ],
1555
-                REG_ADMIN_URL
1556
-            ),
1557
-            'view_not_approved_reg_url'    => add_query_arg(
1558
-                [
1559
-                    'action'      => 'default',
1560
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1561
-                    '_reg_status' => EEM_Registration::status_id_not_approved,
1562
-                ],
1563
-                REG_ADMIN_URL
1564
-            ),
1565
-            'view_pending_payment_reg_url' => add_query_arg(
1566
-                [
1567
-                    'action'      => 'default',
1568
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1569
-                    '_reg_status' => EEM_Registration::status_id_pending_payment,
1570
-                ],
1571
-                REG_ADMIN_URL
1572
-            ),
1573
-            'approved_regs'                => $this->_cpt_model_obj->count_related(
1574
-                'Registration',
1575
-                $approved_query_args
1576
-            ),
1577
-            'not_approved_regs'            => $this->_cpt_model_obj->count_related(
1578
-                'Registration',
1579
-                $not_approved_query_args
1580
-            ),
1581
-            'pending_payment_regs'         => $this->_cpt_model_obj->count_related(
1582
-                'Registration',
1583
-                $pending_payment_query_args
1584
-            ),
1585
-            'misc_pub_section_class'       => apply_filters(
1586
-                'FHEE_Events_Admin_Page___generate_publish_box_extra_content__misc_pub_section_class',
1587
-                'misc-pub-section'
1588
-            ),
1589
-        ];
1590
-        ob_start();
1591
-        do_action(
1592
-            'AHEE__Events_Admin_Page___generate_publish_box_extra_content__event_editor_overview_add',
1593
-            $this->_cpt_model_obj
1594
-        );
1595
-        $publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1596
-        // load template
1597
-        EEH_Template::display_template(
1598
-            EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1599
-            $publish_box_extra_args
1600
-        );
1601
-    }
1602
-
1603
-
1604
-    /**
1605
-     * @return EE_Event
1606
-     */
1607
-    public function get_event_object()
1608
-    {
1609
-        return $this->_cpt_model_obj;
1610
-    }
1611
-
1612
-
1613
-
1614
-
1615
-    /** METABOXES * */
1616
-    /**
1617
-     * _register_event_editor_meta_boxes
1618
-     * add all metaboxes related to the event_editor
1619
-     *
1620
-     * @return void
1621
-     * @throws EE_Error
1622
-     * @throws InvalidArgumentException
1623
-     * @throws InvalidDataTypeException
1624
-     * @throws InvalidInterfaceException
1625
-     * @throws ReflectionException
1626
-     */
1627
-    protected function _register_event_editor_meta_boxes()
1628
-    {
1629
-        $this->verify_cpt_object();
1630
-        $use_advanced_editor = $this->admin_config->useAdvancedEditor();
1631
-        /** @var FeatureFlags $flags */
1632
-        $flags = $this->loader->getShared('EventEspresso\core\domain\services\capabilities\FeatureFlags');
1633
-        // check if the new EDTR reg options meta box is being used, and if so, don't load the legacy version
1634
-        if (! $use_advanced_editor || ! $flags->featureAllowed('use_reg_options_meta_box')) {
1635
-            add_meta_box(
1636
-                'espresso_event_editor_event_options',
1637
-                esc_html__('Event Registration Options', 'event_espresso'),
1638
-                [$this, 'registration_options_meta_box'],
1639
-                $this->page_slug,
1640
-                'side'
1641
-            );
1642
-        }
1643
-        if (! $use_advanced_editor) {
1644
-            add_meta_box(
1645
-                'espresso_event_editor_tickets',
1646
-                esc_html__('Event Datetime & Ticket', 'event_espresso'),
1647
-                [$this, 'ticket_metabox'],
1648
-                $this->page_slug,
1649
-                'normal',
1650
-                'high'
1651
-            );
1652
-        } else {
1653
-            if ($flags->featureAllowed('use_reg_options_meta_box')) {
1654
-                add_action(
1655
-                    'add_meta_boxes_espresso_events',
1656
-                    function () {
1657
-                        global $current_screen;
1658
-                        remove_meta_box('authordiv', $current_screen, 'normal');
1659
-                    },
1660
-                    99
1661
-                );
1662
-            }
1663
-        }
1664
-        // NOTE: if you're looking for other metaboxes in here,
1665
-        // where a metabox has a related management page in the admin
1666
-        // you will find it setup in the related management page's "_Hooks" file.
1667
-        // i.e. messages metabox is found in "espresso_events_Messages_Hooks.class.php".
1668
-    }
1669
-
1670
-
1671
-    /**
1672
-     * @throws DomainException
1673
-     * @throws EE_Error
1674
-     * @throws InvalidArgumentException
1675
-     * @throws InvalidDataTypeException
1676
-     * @throws InvalidInterfaceException
1677
-     * @throws ReflectionException
1678
-     */
1679
-    public function ticket_metabox()
1680
-    {
1681
-        $existing_datetime_ids = $existing_ticket_ids = [];
1682
-        // defaults for template args
1683
-        $template_args = [
1684
-            'existing_datetime_ids'    => '',
1685
-            'event_datetime_help_link' => '',
1686
-            'ticket_options_help_link' => '',
1687
-            'time'                     => null,
1688
-            'ticket_rows'              => '',
1689
-            'existing_ticket_ids'      => '',
1690
-            'total_ticket_rows'        => 1,
1691
-            'ticket_js_structure'      => '',
1692
-            'trash_icon'               => 'ee-lock-icon',
1693
-            'disabled'                 => '',
1694
-        ];
1695
-        $event_id = is_object($this->_cpt_model_obj) ? $this->_cpt_model_obj->ID() : null;
1696
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1697
-        /**
1698
-         * 1. Start with retrieving Datetimes
1699
-         * 2. Fore each datetime get related tickets
1700
-         * 3. For each ticket get related prices
1701
-         */
1702
-        $times = EE_Registry::instance()->load_model('Datetime')->get_all_event_dates($event_id);
1703
-        /** @type EE_Datetime $first_datetime */
1704
-        $first_datetime = reset($times);
1705
-        // do we get related tickets?
1706
-        if (
1707
-            $first_datetime instanceof EE_Datetime
1708
-            && $first_datetime->ID() !== 0
1709
-        ) {
1710
-            $existing_datetime_ids[] = $first_datetime->get('DTT_ID');
1711
-            $template_args['time'] = $first_datetime;
1712
-            $related_tickets = $first_datetime->tickets(
1713
-                [
1714
-                    ['OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0]],
1715
-                    'default_where_conditions' => 'none',
1716
-                ]
1717
-            );
1718
-            if (! empty($related_tickets)) {
1719
-                $template_args['total_ticket_rows'] = count($related_tickets);
1720
-                $row = 0;
1721
-                foreach ($related_tickets as $ticket) {
1722
-                    $existing_ticket_ids[] = $ticket->get('TKT_ID');
1723
-                    $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket, false, $row);
1724
-                    $row++;
1725
-                }
1726
-            } else {
1727
-                $template_args['total_ticket_rows'] = 1;
1728
-                /** @type EE_Ticket $ticket */
1729
-                $ticket = EE_Registry::instance()->load_model('Ticket')->create_default_object();
1730
-                $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket);
1731
-            }
1732
-        } else {
1733
-            $template_args['time'] = $times[0];
1734
-            /** @type EE_Ticket $ticket */
1735
-            $ticket = EE_Registry::instance()->load_model('Ticket')->get_all_default_tickets();
1736
-            $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket[1]);
1737
-            // NOTE: we're just sending the first default row
1738
-            // (decaf can't manage default tickets so this should be sufficient);
1739
-        }
1740
-        $template_args['event_datetime_help_link'] = $this->_get_help_tab_link(
1741
-            'event_editor_event_datetimes_help_tab'
1742
-        );
1743
-        $template_args['ticket_options_help_link'] = $this->_get_help_tab_link('ticket_options_info');
1744
-        $template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1745
-        $template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1746
-        $template_args['ticket_js_structure'] = $this->_get_ticket_row(
1747
-            EE_Registry::instance()->load_model('Ticket')->create_default_object(),
1748
-            true
1749
-        );
1750
-        $template = apply_filters(
1751
-            'FHEE__Events_Admin_Page__ticket_metabox__template',
1752
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1753
-        );
1754
-        EEH_Template::display_template($template, $template_args);
1755
-    }
1756
-
1757
-
1758
-    /**
1759
-     * Setup an individual ticket form for the decaf event editor page
1760
-     *
1761
-     * @access private
1762
-     * @param EE_Ticket $ticket   the ticket object
1763
-     * @param boolean   $skeleton whether we're generating a skeleton for js manipulation
1764
-     * @param int       $row
1765
-     * @return string generated html for the ticket row.
1766
-     * @throws DomainException
1767
-     * @throws EE_Error
1768
-     * @throws InvalidArgumentException
1769
-     * @throws InvalidDataTypeException
1770
-     * @throws InvalidInterfaceException
1771
-     * @throws ReflectionException
1772
-     */
1773
-    private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1774
-    {
1775
-        $template_args = [
1776
-            'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1777
-            'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1778
-                : '',
1779
-            'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
1780
-            'TKT_ID'              => $ticket->get('TKT_ID'),
1781
-            'TKT_name'            => $ticket->get('TKT_name'),
1782
-            'TKT_start_date'      => $skeleton ? '' : $ticket->get_date('TKT_start_date', 'Y-m-d h:i a'),
1783
-            'TKT_end_date'        => $skeleton ? '' : $ticket->get_date('TKT_end_date', 'Y-m-d h:i a'),
1784
-            'TKT_is_default'      => $ticket->get('TKT_is_default'),
1785
-            'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1786
-            'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1787
-            'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1788
-            'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1789
-                                     && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1790
-                ? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1791
-            'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1792
-                : ' disabled=disabled',
1793
-        ];
1794
-        $price = $ticket->ID() !== 0
1795
-            ? $ticket->get_first_related('Price', ['default_where_conditions' => 'none'])
1796
-            : EE_Registry::instance()->load_model('Price')->create_default_object();
1797
-        $price_args = [
1798
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1799
-            'PRC_amount'            => $price->get('PRC_amount'),
1800
-            'PRT_ID'                => $price->get('PRT_ID'),
1801
-            'PRC_ID'                => $price->get('PRC_ID'),
1802
-            'PRC_is_default'        => $price->get('PRC_is_default'),
1803
-        ];
1804
-        // make sure we have default start and end dates if skeleton
1805
-        // handle rows that should NOT be empty
1806
-        if (empty($template_args['TKT_start_date'])) {
1807
-            // if empty then the start date will be now.
1808
-            $template_args['TKT_start_date'] = date('Y-m-d h:i a', current_time('timestamp'));
1809
-        }
1810
-        if (empty($template_args['TKT_end_date'])) {
1811
-            // get the earliest datetime (if present);
1812
-            $earliest_dtt = $this->_cpt_model_obj->ID() > 0
1813
-                ? $this->_cpt_model_obj->get_first_related(
1814
-                    'Datetime',
1815
-                    ['order_by' => ['DTT_EVT_start' => 'ASC']]
1816
-                )
1817
-                : null;
1818
-            if (! empty($earliest_dtt)) {
1819
-                $template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a');
1820
-            } else {
1821
-                $template_args['TKT_end_date'] = date(
1822
-                    'Y-m-d h:i a',
1823
-                    mktime(0, 0, 0, date('m'), date('d') + 7, date('Y'))
1824
-                );
1825
-            }
1826
-        }
1827
-        $template_args = array_merge($template_args, $price_args);
1828
-        $template = apply_filters(
1829
-            'FHEE__Events_Admin_Page__get_ticket_row__template',
1830
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1831
-            $ticket
1832
-        );
1833
-        return EEH_Template::display_template($template, $template_args, true);
1834
-    }
1835
-
1836
-
1837
-    /**
1838
-     * @throws DomainException
1839
-     * @throws EE_Error
1840
-     */
1841
-    public function registration_options_meta_box()
1842
-    {
1843
-        $yes_no_values = [
1844
-            ['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
1845
-            ['id' => false, 'text' => esc_html__('No', 'event_espresso')],
1846
-        ];
1847
-        $default_reg_status_values = EEM_Registration::reg_status_array(
1848
-            [
1849
-                EEM_Registration::status_id_cancelled,
1850
-                EEM_Registration::status_id_declined,
1851
-                EEM_Registration::status_id_incomplete,
1852
-            ],
1853
-            true
1854
-        );
1855
-        // $template_args['is_active_select'] = EEH_Form_Fields::select_input('is_active', $yes_no_values, $this->_cpt_model_obj->is_active());
1856
-        $template_args['_event'] = $this->_cpt_model_obj;
1857
-        $template_args['active_status'] = $this->_cpt_model_obj->pretty_active_status(false);
1858
-        $template_args['additional_limit'] = $this->_cpt_model_obj->additional_limit();
1859
-        $template_args['default_registration_status'] = EEH_Form_Fields::select_input(
1860
-            'default_reg_status',
1861
-            $default_reg_status_values,
1862
-            $this->_cpt_model_obj->default_registration_status()
1863
-        );
1864
-        $template_args['display_description'] = EEH_Form_Fields::select_input(
1865
-            'display_desc',
1866
-            $yes_no_values,
1867
-            $this->_cpt_model_obj->display_description()
1868
-        );
1869
-        $template_args['display_ticket_selector'] = EEH_Form_Fields::select_input(
1870
-            'display_ticket_selector',
1871
-            $yes_no_values,
1872
-            $this->_cpt_model_obj->display_ticket_selector(),
1873
-            '',
1874
-            '',
1875
-            false
1876
-        );
1877
-        $template_args['additional_registration_options'] = apply_filters(
1878
-            'FHEE__Events_Admin_Page__registration_options_meta_box__additional_registration_options',
1879
-            '',
1880
-            $template_args,
1881
-            $yes_no_values,
1882
-            $default_reg_status_values
1883
-        );
1884
-        EEH_Template::display_template(
1885
-            EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1886
-            $template_args
1887
-        );
1888
-    }
1889
-
1890
-
1891
-    /**
1892
-     * _get_events()
1893
-     * This method simply returns all the events (for the given _view and paging)
1894
-     *
1895
-     * @access public
1896
-     * @param int  $per_page     count of items per page (20 default);
1897
-     * @param int  $current_page what is the current page being viewed.
1898
-     * @param bool $count        if TRUE then we just return a count of ALL events matching the given _view.
1899
-     *                           If FALSE then we return an array of event objects
1900
-     *                           that match the given _view and paging parameters.
1901
-     * @return array an array of event objects.
1902
-     * @throws EE_Error
1903
-     * @throws InvalidArgumentException
1904
-     * @throws InvalidDataTypeException
1905
-     * @throws InvalidInterfaceException
1906
-     * @throws ReflectionException
1907
-     * @throws Exception
1908
-     * @throws Exception
1909
-     * @throws Exception
1910
-     */
1911
-    public function get_events($per_page = 10, $current_page = 1, $count = false)
1912
-    {
1913
-        $EEME = $this->_event_model();
1914
-        $offset = ($current_page - 1) * $per_page;
1915
-        $limit = $count ? null : $offset . ',' . $per_page;
1916
-        $orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'EVT_ID';
1917
-        $order = isset($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
1918
-        if (isset($this->_req_data['month_range'])) {
1919
-            $pieces = explode(' ', $this->_req_data['month_range'], 3);
1920
-            // simulate the FIRST day of the month, that fixes issues for months like February
1921
-            // where PHP doesn't know what to assume for date.
1922
-            // @see https://events.codebasehq.com/projects/event-espresso/tickets/10437
1923
-            $month_r = ! empty($pieces[0]) ? date('m', EEH_DTT_Helper::first_of_month_timestamp($pieces[0])) : '';
1924
-            $year_r = ! empty($pieces[1]) ? $pieces[1] : '';
1925
-        }
1926
-        $where = [];
1927
-        $status = isset($this->_req_data['status']) ? $this->_req_data['status'] : null;
1928
-        // determine what post_status our condition will have for the query.
1929
-        switch ($status) {
1930
-            case 'month':
1931
-            case 'today':
1932
-            case null:
1933
-            case 'all':
1934
-                break;
1935
-            case 'draft':
1936
-                $where['status'] = ['IN', ['draft', 'auto-draft']];
1937
-                break;
1938
-            default:
1939
-                $where['status'] = $status;
1940
-        }
1941
-        // categories?
1942
-        $category = isset($this->_req_data['EVT_CAT']) && $this->_req_data['EVT_CAT'] > 0
1943
-            ? $this->_req_data['EVT_CAT'] : null;
1944
-        if (! empty($category)) {
1945
-            $where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1946
-            $where['Term_Taxonomy.term_id'] = $category;
1947
-        }
1948
-        // date where conditions
1949
-        $start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1950
-        if (isset($this->_req_data['month_range']) && $this->_req_data['month_range'] !== '') {
1951
-            $DateTime = new DateTime(
1952
-                $year_r . '-' . $month_r . '-01 00:00:00',
1953
-                new DateTimeZone('UTC')
1954
-            );
1955
-            $start = $DateTime->getTimestamp();
1956
-            // set the datetime to be the end of the month
1957
-            $DateTime->setDate(
1958
-                $year_r,
1959
-                $month_r,
1960
-                $DateTime->format('t')
1961
-            )->setTime(23, 59, 59);
1962
-            $end = $DateTime->getTimestamp();
1963
-            $where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1964
-        } elseif (isset($this->_req_data['status']) && $this->_req_data['status'] === 'today') {
1965
-            $DateTime = new DateTime('now', new DateTimeZone(EEM_Event::instance()->get_timezone()));
1966
-            $start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1967
-            $end = $DateTime->setTime(23, 59, 59)->format(implode(' ', $start_formats));
1968
-            $where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1969
-        } elseif (isset($this->_req_data['status']) && $this->_req_data['status'] === 'month') {
1970
-            $now = date('Y-m-01');
1971
-            $DateTime = new DateTime($now, new DateTimeZone(EEM_Event::instance()->get_timezone()));
1972
-            $start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1973
-            $end = $DateTime->setDate(date('Y'), date('m'), $DateTime->format('t'))
1974
-                            ->setTime(23, 59, 59)
1975
-                            ->format(implode(' ', $start_formats));
1976
-            $where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1977
-        }
1978
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1979
-            $where['EVT_wp_user'] = get_current_user_id();
1980
-        } elseif (
1981
-            ! isset($where['status'])
1982
-                  && ! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')
1983
-        ) {
1984
-            $where['OR'] = [
1985
-                'status*restrict_private' => ['!=', 'private'],
1986
-                'AND'                     => [
1987
-                    'status*inclusive' => ['=', 'private'],
1988
-                    'EVT_wp_user'      => get_current_user_id(),
1989
-                ],
1990
-            ];
1991
-        }
1992
-
1993
-        if (
1994
-            isset($this->_req_data['EVT_wp_user'])
1995
-            && (int) $this->_req_data['EVT_wp_user'] !== (int) get_current_user_id()
1996
-            && EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')
1997
-        ) {
1998
-            $where['EVT_wp_user'] = $this->_req_data['EVT_wp_user'];
1999
-        }
2000
-        // search query handling
2001
-        if (isset($this->_req_data['s'])) {
2002
-            $search_string = '%' . $this->_req_data['s'] . '%';
2003
-            $where['OR'] = [
2004
-                'EVT_name'       => ['LIKE', $search_string],
2005
-                'EVT_desc'       => ['LIKE', $search_string],
2006
-                'EVT_short_desc' => ['LIKE', $search_string],
2007
-            ];
2008
-        }
2009
-        // filter events by venue.
2010
-        if (isset($this->_req_data['venue']) && ! empty($this->_req_data['venue'])) {
2011
-            $where['Venue.VNU_ID'] = absint($this->_req_data['venue']);
2012
-        }
2013
-        $where = apply_filters('FHEE__Events_Admin_Page__get_events__where', $where, $this->_req_data);
2014
-        $query_params = apply_filters(
2015
-            'FHEE__Events_Admin_Page__get_events__query_params',
2016
-            [
2017
-                $where,
2018
-                'limit'    => $limit,
2019
-                'order_by' => $orderby,
2020
-                'order'    => $order,
2021
-                'group_by' => 'EVT_ID',
2022
-            ],
2023
-            $this->_req_data
2024
-        );
2025
-
2026
-        // let's first check if we have special requests coming in.
2027
-        if (isset($this->_req_data['active_status'])) {
2028
-            switch ($this->_req_data['active_status']) {
2029
-                case 'upcoming':
2030
-                    return $EEME->get_upcoming_events($query_params, $count);
2031
-                    break;
2032
-                case 'expired':
2033
-                    return $EEME->get_expired_events($query_params, $count);
2034
-                    break;
2035
-                case 'active':
2036
-                    return $EEME->get_active_events($query_params, $count);
2037
-                    break;
2038
-                case 'inactive':
2039
-                    return $EEME->get_inactive_events($query_params, $count);
2040
-                    break;
2041
-            }
2042
-        }
2043
-
2044
-        $events = $count ? $EEME->count([$where], 'EVT_ID', true) : $EEME->get_all($query_params);
2045
-        return $events;
2046
-    }
2047
-
2048
-
2049
-    /**
2050
-     * handling for WordPress CPT actions (trash, restore, delete)
2051
-     *
2052
-     * @param string $post_id
2053
-     * @throws EE_Error
2054
-     * @throws InvalidArgumentException
2055
-     * @throws InvalidDataTypeException
2056
-     * @throws InvalidInterfaceException
2057
-     * @throws ReflectionException
2058
-     */
2059
-    public function trash_cpt_item($post_id)
2060
-    {
2061
-        $this->_req_data['EVT_ID'] = $post_id;
2062
-        $this->_trash_or_restore_event('trash', false);
2063
-    }
2064
-
2065
-
2066
-    /**
2067
-     * @param string $post_id
2068
-     * @throws EE_Error
2069
-     * @throws InvalidArgumentException
2070
-     * @throws InvalidDataTypeException
2071
-     * @throws InvalidInterfaceException
2072
-     * @throws ReflectionException
2073
-     */
2074
-    public function restore_cpt_item($post_id)
2075
-    {
2076
-        $this->_req_data['EVT_ID'] = $post_id;
2077
-        $this->_trash_or_restore_event('draft', false);
2078
-    }
2079
-
2080
-
2081
-    /**
2082
-     * @param string $post_id
2083
-     * @throws EE_Error
2084
-     * @throws InvalidArgumentException
2085
-     * @throws InvalidDataTypeException
2086
-     * @throws InvalidInterfaceException
2087
-     * @throws ReflectionException
2088
-     */
2089
-    public function delete_cpt_item($post_id)
2090
-    {
2091
-        throw new EE_Error(esc_html__('Please contact Event Espresso support with the details of the steps taken to produce this error.', 'event_espresso'));
2092
-        $this->_req_data['EVT_ID'] = $post_id;
2093
-        $this->_delete_event();
2094
-    }
2095
-
2096
-
2097
-    /**
2098
-     * _trash_or_restore_event
2099
-     *
2100
-     * @access protected
2101
-     * @param string $event_status
2102
-     * @param bool   $redirect_after
2103
-     * @throws EE_Error
2104
-     * @throws InvalidArgumentException
2105
-     * @throws InvalidDataTypeException
2106
-     * @throws InvalidInterfaceException
2107
-     * @throws ReflectionException
2108
-     */
2109
-    protected function _trash_or_restore_event($event_status = 'trash', $redirect_after = true)
2110
-    {
2111
-        // determine the event id and set to array.
2112
-        $EVT_ID = isset($this->_req_data['EVT_ID']) ? absint($this->_req_data['EVT_ID']) : false;
2113
-        // loop thru events
2114
-        if ($EVT_ID) {
2115
-            // clean status
2116
-            $event_status = sanitize_key($event_status);
2117
-            // grab status
2118
-            if (! empty($event_status)) {
2119
-                $success = $this->_change_event_status($EVT_ID, $event_status);
2120
-            } else {
2121
-                $success = false;
2122
-                $msg = esc_html__(
2123
-                    'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2124
-                    'event_espresso'
2125
-                );
2126
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2127
-            }
2128
-        } else {
2129
-            $success = false;
2130
-            $msg = esc_html__(
2131
-                'An error occurred. The event could not be moved to the trash because a valid event ID was not not supplied.',
2132
-                'event_espresso'
2133
-            );
2134
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2135
-        }
2136
-        $action = $event_status === 'trash' ? 'moved to the trash' : 'restored from the trash';
2137
-        if ($redirect_after) {
2138
-            $this->_redirect_after_action($success, 'Event', $action, ['action' => 'default']);
2139
-        }
2140
-    }
2141
-
2142
-
2143
-    /**
2144
-     * _trash_or_restore_events
2145
-     *
2146
-     * @access protected
2147
-     * @param string $event_status
2148
-     * @return void
2149
-     * @throws EE_Error
2150
-     * @throws InvalidArgumentException
2151
-     * @throws InvalidDataTypeException
2152
-     * @throws InvalidInterfaceException
2153
-     * @throws ReflectionException
2154
-     */
2155
-    protected function _trash_or_restore_events($event_status = 'trash')
2156
-    {
2157
-        // clean status
2158
-        $event_status = sanitize_key($event_status);
2159
-        // grab status
2160
-        if (! empty($event_status)) {
2161
-            $success = true;
2162
-            // determine the event id and set to array.
2163
-            $EVT_IDs = isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : [];
2164
-            // loop thru events
2165
-            foreach ($EVT_IDs as $EVT_ID) {
2166
-                if ($EVT_ID = absint($EVT_ID)) {
2167
-                    $results = $this->_change_event_status($EVT_ID, $event_status);
2168
-                    $success = $results !== false ? $success : false;
2169
-                } else {
2170
-                    $msg = sprintf(
2171
-                        esc_html__(
2172
-                            'An error occurred. Event #%d could not be moved to the trash because a valid event ID was not not supplied.',
2173
-                            'event_espresso'
2174
-                        ),
2175
-                        $EVT_ID
2176
-                    );
2177
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2178
-                    $success = false;
2179
-                }
2180
-            }
2181
-        } else {
2182
-            $success = false;
2183
-            $msg = esc_html__(
2184
-                'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2185
-                'event_espresso'
2186
-            );
2187
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2188
-        }
2189
-        // in order to force a pluralized result message we need to send back a success status greater than 1
2190
-        $success = $success ? 2 : false;
2191
-        $action = $event_status === 'trash' ? 'moved to the trash' : 'restored from the trash';
2192
-        $this->_redirect_after_action($success, 'Events', $action, ['action' => 'default']);
2193
-    }
2194
-
2195
-
2196
-    /**
2197
-     * _trash_or_restore_events
2198
-     *
2199
-     * @access  private
2200
-     * @param int    $EVT_ID
2201
-     * @param string $event_status
2202
-     * @return bool
2203
-     * @throws EE_Error
2204
-     * @throws InvalidArgumentException
2205
-     * @throws InvalidDataTypeException
2206
-     * @throws InvalidInterfaceException
2207
-     * @throws ReflectionException
2208
-     */
2209
-    private function _change_event_status($EVT_ID = 0, $event_status = '')
2210
-    {
2211
-        // grab event id
2212
-        if (! $EVT_ID) {
2213
-            $msg = esc_html__(
2214
-                'An error occurred. No Event ID or an invalid Event ID was received.',
2215
-                'event_espresso'
2216
-            );
2217
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2218
-            return false;
2219
-        }
2220
-        $this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2221
-        // clean status
2222
-        $event_status = sanitize_key($event_status);
2223
-        // grab status
2224
-        if (empty($event_status)) {
2225
-            $msg = esc_html__(
2226
-                'An error occurred. No Event Status or an invalid Event Status was received.',
2227
-                'event_espresso'
2228
-            );
2229
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2230
-            return false;
2231
-        }
2232
-        // was event trashed or restored ?
2233
-        switch ($event_status) {
2234
-            case 'draft':
2235
-                $action = 'restored from the trash';
2236
-                $hook = 'AHEE_event_restored_from_trash';
2237
-                break;
2238
-            case 'trash':
2239
-                $action = 'moved to the trash';
2240
-                $hook = 'AHEE_event_moved_to_trash';
2241
-                break;
2242
-            default:
2243
-                $action = 'updated';
2244
-                $hook = false;
2245
-        }
2246
-        // use class to change status
2247
-        $this->_cpt_model_obj->set_status($event_status);
2248
-        $success = $this->_cpt_model_obj->save();
2249
-        if ($success === false) {
2250
-            $msg = sprintf(esc_html__('An error occurred. The event could not be %s.', 'event_espresso'), $action);
2251
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2252
-            return false;
2253
-        }
2254
-        if ($hook) {
2255
-            do_action($hook);
2256
-        }
2257
-        return true;
2258
-    }
2259
-
2260
-
2261
-    /**
2262
-     * _delete_event
2263
-     *
2264
-     * @access protected
2265
-     * @param bool $redirect_after
2266
-     * @throws EE_Error
2267
-     * @throws InvalidArgumentException
2268
-     * @throws InvalidDataTypeException
2269
-     * @throws InvalidInterfaceException
2270
-     * @throws ReflectionException
2271
-     */
2272
-    protected function _delete_event()
2273
-    {
2274
-        $this->generateDeletionPreview(isset($this->_req_data['EVT_ID']) ? $this->_req_data['EVT_ID'] : []);
2275
-    }
2276
-
2277
-    /**
2278
-     * Gets the tree traversal batch persister.
2279
-     * @since 4.10.12.p
2280
-     * @return NodeGroupDao
2281
-     * @throws InvalidArgumentException
2282
-     * @throws InvalidDataTypeException
2283
-     * @throws InvalidInterfaceException
2284
-     */
2285
-    protected function getModelObjNodeGroupPersister()
2286
-    {
2287
-        if (! $this->model_obj_node_group_persister instanceof NodeGroupDao) {
2288
-            $this->model_obj_node_group_persister = $this->getLoader()->load('\EventEspresso\core\services\orm\tree_traversal\NodeGroupDao');
2289
-        }
2290
-        return $this->model_obj_node_group_persister;
2291
-    }
2292
-
2293
-    /**
2294
-     * _delete_events
2295
-     *
2296
-     * @access protected
2297
-     * @return void
2298
-     * @throws EE_Error
2299
-     * @throws InvalidArgumentException
2300
-     * @throws InvalidDataTypeException
2301
-     * @throws InvalidInterfaceException
2302
-     * @throws ReflectionException
2303
-     */
2304
-    protected function _delete_events()
2305
-    {
2306
-        $this->generateDeletionPreview(isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : []);
2307
-    }
2308
-
2309
-    protected function generateDeletionPreview($event_ids)
2310
-    {
2311
-        $event_ids = (array) $event_ids;
2312
-        // Set a code we can use to reference this deletion task in the batch jobs and preview page.
2313
-        $deletion_job_code = $this->getModelObjNodeGroupPersister()->generateGroupCode();
2314
-        $return_url = EE_Admin_Page::add_query_args_and_nonce(
2315
-            [
2316
-                'action' => 'preview_deletion',
2317
-                'deletion_job_code' => $deletion_job_code,
2318
-            ],
2319
-            $this->_admin_base_url
2320
-        );
2321
-        $event_ids = array_map(
2322
-            'intval',
2323
-            $event_ids
2324
-        );
2325
-
2326
-        EEH_URL::safeRedirectAndExit(
2327
-            EE_Admin_Page::add_query_args_and_nonce(
2328
-                [
2329
-                    'page'              => 'espresso_batch',
2330
-                    'batch'             => EED_Batch::batch_job,
2331
-                    'EVT_IDs'           => $event_ids,
2332
-                    'deletion_job_code' => $deletion_job_code,
2333
-                    'job_handler'       => urlencode('EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'),
2334
-                    'return_url'        => urlencode($return_url),
2335
-                ],
2336
-                admin_url()
2337
-            )
2338
-        );
2339
-    }
2340
-
2341
-    /**
2342
-     * Checks for a POST submission
2343
-     * @since 4.10.12.p
2344
-     */
2345
-    protected function confirmDeletion()
2346
-    {
2347
-        $deletion_redirect_logic = $this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion');
2348
-        $deletion_redirect_logic->handle($this->get_request_data(), $this->admin_base_url());
2349
-    }
2350
-
2351
-    /**
2352
-     * A page for users to preview what exactly will be deleted, and confirm they want to delete it.
2353
-     * @since 4.10.12.p
2354
-     * @throws EE_Error
2355
-     */
2356
-    protected function previewDeletion()
2357
-    {
2358
-        $preview_deletion_logic = $this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\PreviewDeletion');
2359
-        $this->set_template_args($preview_deletion_logic->handle($this->get_request_data(), $this->admin_base_url()));
2360
-        $this->display_admin_page_with_no_sidebar();
2361
-    }
2362
-
2363
-    /**
2364
-     * get total number of events
2365
-     *
2366
-     * @access public
2367
-     * @return int
2368
-     * @throws EE_Error
2369
-     * @throws InvalidArgumentException
2370
-     * @throws InvalidDataTypeException
2371
-     * @throws InvalidInterfaceException
2372
-     */
2373
-    public function total_events()
2374
-    {
2375
-        $count = EEM_Event::instance()->count(['caps' => 'read_admin'], 'EVT_ID', true);
2376
-        return $count;
2377
-    }
2378
-
2379
-
2380
-    /**
2381
-     * get total number of draft events
2382
-     *
2383
-     * @access public
2384
-     * @return int
2385
-     * @throws EE_Error
2386
-     * @throws InvalidArgumentException
2387
-     * @throws InvalidDataTypeException
2388
-     * @throws InvalidInterfaceException
2389
-     */
2390
-    public function total_events_draft()
2391
-    {
2392
-        $where = [
2393
-            'status' => ['IN', ['draft', 'auto-draft']],
2394
-        ];
2395
-        $count = EEM_Event::instance()->count([$where, 'caps' => 'read_admin'], 'EVT_ID', true);
2396
-        return $count;
2397
-    }
2398
-
2399
-
2400
-    /**
2401
-     * get total number of trashed events
2402
-     *
2403
-     * @access public
2404
-     * @return int
2405
-     * @throws EE_Error
2406
-     * @throws InvalidArgumentException
2407
-     * @throws InvalidDataTypeException
2408
-     * @throws InvalidInterfaceException
2409
-     */
2410
-    public function total_trashed_events()
2411
-    {
2412
-        $where = [
2413
-            'status' => 'trash',
2414
-        ];
2415
-        $count = EEM_Event::instance()->count([$where, 'caps' => 'read_admin'], 'EVT_ID', true);
2416
-        return $count;
2417
-    }
2418
-
2419
-
2420
-    /**
2421
-     *    _default_event_settings
2422
-     *    This generates the Default Settings Tab
2423
-     *
2424
-     * @return void
2425
-     * @throws DomainException
2426
-     * @throws EE_Error
2427
-     * @throws InvalidArgumentException
2428
-     * @throws InvalidDataTypeException
2429
-     * @throws InvalidInterfaceException
2430
-     */
2431
-    protected function _default_event_settings()
2432
-    {
2433
-        $this->_set_add_edit_form_tags('update_default_event_settings');
2434
-        $this->_set_publish_post_box_vars(null, false, false, null, false);
2435
-        $this->_template_args['admin_page_content'] = $this->_default_event_settings_form()->get_html();
2436
-        $this->display_admin_page_with_sidebar();
2437
-    }
2438
-
2439
-
2440
-    /**
2441
-     * Return the form for event settings.
2442
-     *
2443
-     * @return EE_Form_Section_Proper
2444
-     * @throws EE_Error
2445
-     */
2446
-    protected function _default_event_settings_form()
2447
-    {
2448
-        $registration_config = EE_Registry::instance()->CFG->registration;
2449
-        $registration_stati_for_selection = EEM_Registration::reg_status_array(
2450
-        // exclude
2451
-            [
2452
-                EEM_Registration::status_id_cancelled,
2453
-                EEM_Registration::status_id_declined,
2454
-                EEM_Registration::status_id_incomplete,
2455
-                EEM_Registration::status_id_wait_list,
2456
-            ],
2457
-            true
2458
-        );
2459
-        return new EE_Form_Section_Proper(
2460
-            [
2461
-                'name'            => 'update_default_event_settings',
2462
-                'html_id'         => 'update_default_event_settings',
2463
-                'html_class'      => 'form-table',
2464
-                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
2465
-                'subsections'     => apply_filters(
2466
-                    'FHEE__Events_Admin_Page___default_event_settings_form__form_subsections',
2467
-                    [
2468
-                        'default_reg_status'  => new EE_Select_Input(
2469
-                            $registration_stati_for_selection,
2470
-                            [
2471
-                                'default'         => isset($registration_config->default_STS_ID)
2472
-                                                     && array_key_exists(
2473
-                                                         $registration_config->default_STS_ID,
2474
-                                                         $registration_stati_for_selection
2475
-                                                     )
2476
-                                    ? sanitize_text_field($registration_config->default_STS_ID)
2477
-                                    : EEM_Registration::status_id_pending_payment,
2478
-                                'html_label_text' => esc_html__('Default Registration Status', 'event_espresso')
2479
-                                                     . EEH_Template::get_help_tab_link(
2480
-                                                         'default_settings_status_help_tab'
2481
-                                                     ),
2482
-                                'html_help_text'  => esc_html__(
2483
-                                    'This setting allows you to preselect what the default registration status setting is when creating an event.  Note that changing this setting does NOT retroactively apply it to existing events.',
2484
-                                    'event_espresso'
2485
-                                ),
2486
-                            ]
2487
-                        ),
2488
-                        'default_max_tickets' => new EE_Integer_Input(
2489
-                            [
2490
-                                'default'         => isset($registration_config->default_maximum_number_of_tickets)
2491
-                                    ? $registration_config->default_maximum_number_of_tickets
2492
-                                    : EEM_Event::get_default_additional_limit(),
2493
-                                'html_label_text' => esc_html__(
2494
-                                    'Default Maximum Tickets Allowed Per Order:',
2495
-                                    'event_espresso'
2496
-                                )
2497
-                                                     . EEH_Template::get_help_tab_link(
2498
-                                                         'default_maximum_tickets_help_tab"'
2499
-                                                     ),
2500
-                                'html_help_text'  => esc_html__(
2501
-                                    'This setting allows you to indicate what will be the default for the maximum number of tickets per order when creating new events.',
2502
-                                    'event_espresso'
2503
-                                ),
2504
-                            ]
2505
-                        ),
2506
-                    ]
2507
-                ),
2508
-            ]
2509
-        );
2510
-    }
2511
-
2512
-
2513
-    /**
2514
-     * @return void
2515
-     * @throws EE_Error
2516
-     * @throws InvalidArgumentException
2517
-     * @throws InvalidDataTypeException
2518
-     * @throws InvalidInterfaceException
2519
-     */
2520
-    protected function _update_default_event_settings()
2521
-    {
2522
-        $form = $this->_default_event_settings_form();
2523
-        if ($form->was_submitted()) {
2524
-            $form->receive_form_submission();
2525
-            if ($form->is_valid()) {
2526
-                $registration_config = EE_Registry::instance()->CFG->registration;
2527
-                $valid_data = $form->valid_data();
2528
-                if (isset($valid_data['default_reg_status'])) {
2529
-                    $registration_config->default_STS_ID = $valid_data['default_reg_status'];
2530
-                }
2531
-                if (isset($valid_data['default_max_tickets'])) {
2532
-                    $registration_config->default_maximum_number_of_tickets = $valid_data['default_max_tickets'];
2533
-                }
2534
-                do_action(
2535
-                    'AHEE__Events_Admin_Page___update_default_event_settings',
2536
-                    $valid_data,
2537
-                    EE_Registry::instance()->CFG,
2538
-                    $this
2539
-                );
2540
-                // update because data was valid!
2541
-                EE_Registry::instance()->CFG->update_espresso_config();
2542
-                EE_Error::overwrite_success();
2543
-                EE_Error::add_success(
2544
-                    __('Default Event Settings were updated', 'event_espresso')
2545
-                );
2546
-            }
2547
-        }
2548
-        $this->_redirect_after_action(0, '', '', ['action' => 'default_event_settings'], true);
2549
-    }
2550
-
2551
-
2552
-    /*************        Templates        *************/
2553
-    protected function _template_settings()
2554
-    {
2555
-        $this->_admin_page_title = esc_html__('Template Settings (Preview)', 'event_espresso');
2556
-        $this->_template_args['preview_img'] = '<img src="'
2557
-                                               . EVENTS_ASSETS_URL
2558
-                                               . '/images/'
2559
-                                               . 'caffeinated_template_features.jpg" alt="'
2560
-                                               . esc_attr__('Template Settings Preview screenshot', 'event_espresso')
2561
-                                               . '" />';
2562
-        $this->_template_args['preview_text'] = '<strong>'
2563
-                                                . esc_html__(
2564
-                                                    'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2565
-                                                    'event_espresso'
2566
-                                                ) . '</strong>';
2567
-        $this->display_admin_caf_preview_page('template_settings_tab');
2568
-    }
2569
-
2570
-
2571
-    /** Event Category Stuff **/
2572
-    /**
2573
-     * set the _category property with the category object for the loaded page.
2574
-     *
2575
-     * @access private
2576
-     * @return void
2577
-     */
2578
-    private function _set_category_object()
2579
-    {
2580
-        if (isset($this->_category->id) && ! empty($this->_category->id)) {
2581
-            return;
2582
-        } //already have the category object so get out.
2583
-        // set default category object
2584
-        $this->_set_empty_category_object();
2585
-        // only set if we've got an id
2586
-        if (! isset($this->_req_data['EVT_CAT_ID'])) {
2587
-            return;
2588
-        }
2589
-        $category_id = absint($this->_req_data['EVT_CAT_ID']);
2590
-        $term = get_term($category_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2591
-        if (! empty($term)) {
2592
-            $this->_category->category_name = $term->name;
2593
-            $this->_category->category_identifier = $term->slug;
2594
-            $this->_category->category_desc = $term->description;
2595
-            $this->_category->id = $term->term_id;
2596
-            $this->_category->parent = $term->parent;
2597
-        }
2598
-    }
2599
-
2600
-
2601
-    /**
2602
-     * Clears out category properties.
2603
-     */
2604
-    private function _set_empty_category_object()
2605
-    {
2606
-        $this->_category = new stdClass();
2607
-        $this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
2608
-        $this->_category->id = $this->_category->parent = 0;
2609
-    }
2610
-
2611
-
2612
-    /**
2613
-     * @throws DomainException
2614
-     * @throws EE_Error
2615
-     * @throws InvalidArgumentException
2616
-     * @throws InvalidDataTypeException
2617
-     * @throws InvalidInterfaceException
2618
-     */
2619
-    protected function _category_list_table()
2620
-    {
2621
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2622
-        $this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2623
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2624
-            'add_category',
2625
-            'add_category',
2626
-            [],
2627
-            'add-new-h2'
2628
-        );
2629
-        $this->display_admin_list_table_page_with_sidebar();
2630
-    }
2631
-
2632
-
2633
-    /**
2634
-     * Output category details view.
2635
-     *
2636
-     * @param string $view
2637
-     * @throws DomainException
2638
-     * @throws EE_Error
2639
-     * @throws InvalidArgumentException
2640
-     * @throws InvalidDataTypeException
2641
-     * @throws InvalidInterfaceException
2642
-     */
2643
-    protected function _category_details($view)
2644
-    {
2645
-        // load formatter helper
2646
-        // load field generator helper
2647
-        $route = $view === 'edit' ? 'update_category' : 'insert_category';
2648
-        $this->_set_add_edit_form_tags($route);
2649
-        $this->_set_category_object();
2650
-        $id = ! empty($this->_category->id) ? $this->_category->id : '';
2651
-        $delete_action = 'delete_category';
2652
-        // custom redirect
2653
-        $redirect = EE_Admin_Page::add_query_args_and_nonce(
2654
-            ['action' => 'category_list'],
2655
-            $this->_admin_base_url
2656
-        );
2657
-        $this->_set_publish_post_box_vars('EVT_CAT_ID', $id, $delete_action, $redirect);
2658
-        // take care of contents
2659
-        $this->_template_args['admin_page_content'] = $this->_category_details_content();
2660
-        $this->display_admin_page_with_sidebar();
2661
-    }
2662
-
2663
-
2664
-    /**
2665
-     * Output category details content.
2666
-     *
2667
-     * @throws DomainException
2668
-     */
2669
-    protected function _category_details_content()
2670
-    {
2671
-        $editor_args['category_desc'] = [
2672
-            'type'          => 'wp_editor',
2673
-            'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
2674
-            'class'         => 'my_editor_custom',
2675
-            'wpeditor_args' => ['media_buttons' => false],
2676
-        ];
2677
-        $_wp_editor = $this->_generate_admin_form_fields($editor_args, 'array');
2678
-        $all_terms = get_terms(
2679
-            [EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY],
2680
-            ['hide_empty' => 0, 'exclude' => [$this->_category->id]]
2681
-        );
2682
-        // setup category select for term parents.
2683
-        $category_select_values[] = [
2684
-            'text' => esc_html__('No Parent', 'event_espresso'),
2685
-            'id'   => 0,
2686
-        ];
2687
-        foreach ($all_terms as $term) {
2688
-            $category_select_values[] = [
2689
-                'text' => $term->name,
2690
-                'id'   => $term->term_id,
2691
-            ];
2692
-        }
2693
-        $category_select = EEH_Form_Fields::select_input(
2694
-            'category_parent',
2695
-            $category_select_values,
2696
-            $this->_category->parent
2697
-        );
2698
-        $template_args = [
2699
-            'category'                 => $this->_category,
2700
-            'category_select'          => $category_select,
2701
-            'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
2702
-            'category_desc_editor'     => $_wp_editor['category_desc']['field'],
2703
-            'disable'                  => '',
2704
-            'disabled_message'         => false,
2705
-        ];
2706
-        $template = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2707
-        return EEH_Template::display_template($template, $template_args, true);
2708
-    }
2709
-
2710
-
2711
-    /**
2712
-     * Handles deleting categories.
2713
-     */
2714
-    protected function _delete_categories()
2715
-    {
2716
-        $cat_ids = isset($this->_req_data['EVT_CAT_ID']) ? (array) $this->_req_data['EVT_CAT_ID']
2717
-            : (array) $this->_req_data['category_id'];
2718
-        foreach ($cat_ids as $cat_id) {
2719
-            $this->_delete_category($cat_id);
2720
-        }
2721
-        // doesn't matter what page we're coming from... we're going to the same place after delete.
2722
-        $query_args = [
2723
-            'action' => 'category_list',
2724
-        ];
2725
-        $this->_redirect_after_action(0, '', '', $query_args);
2726
-    }
2727
-
2728
-
2729
-    /**
2730
-     * Handles deleting specific category.
2731
-     *
2732
-     * @param int $cat_id
2733
-     */
2734
-    protected function _delete_category($cat_id)
2735
-    {
2736
-        $cat_id = absint($cat_id);
2737
-        wp_delete_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2738
-    }
2739
-
2740
-
2741
-    /**
2742
-     * Handles triggering the update or insertion of a new category.
2743
-     *
2744
-     * @param bool $new_category true means we're triggering the insert of a new category.
2745
-     * @throws EE_Error
2746
-     * @throws InvalidArgumentException
2747
-     * @throws InvalidDataTypeException
2748
-     * @throws InvalidInterfaceException
2749
-     */
2750
-    protected function _insert_or_update_category($new_category)
2751
-    {
2752
-        $cat_id = $new_category ? $this->_insert_category() : $this->_insert_category(true);
2753
-        $success = 0; // we already have a success message so lets not send another.
2754
-        if ($cat_id) {
2755
-            $query_args = [
2756
-                'action'     => 'edit_category',
2757
-                'EVT_CAT_ID' => $cat_id,
2758
-            ];
2759
-        } else {
2760
-            $query_args = ['action' => 'add_category'];
2761
-        }
2762
-        $this->_redirect_after_action($success, '', '', $query_args, true);
2763
-    }
2764
-
2765
-
2766
-    /**
2767
-     * Inserts or updates category
2768
-     *
2769
-     * @param bool $update (true indicates we're updating a category).
2770
-     * @return bool|mixed|string
2771
-     */
2772
-    private function _insert_category($update = false)
2773
-    {
2774
-        $cat_id = $update ? $this->_req_data['EVT_CAT_ID'] : '';
2775
-        $category_name = isset($this->_req_data['category_name']) ? $this->_req_data['category_name'] : '';
2776
-        $category_desc = isset($this->_req_data['category_desc']) ? $this->_req_data['category_desc'] : '';
2777
-        $category_parent = isset($this->_req_data['category_parent']) ? $this->_req_data['category_parent'] : 0;
2778
-        if (empty($category_name)) {
2779
-            $msg = esc_html__('You must add a name for the category.', 'event_espresso');
2780
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2781
-            return false;
2782
-        }
2783
-        $term_args = [
2784
-            'name'        => $category_name,
2785
-            'description' => $category_desc,
2786
-            'parent'      => $category_parent,
2787
-        ];
2788
-        // was the category_identifier input disabled?
2789
-        if (isset($this->_req_data['category_identifier'])) {
2790
-            $term_args['slug'] = $this->_req_data['category_identifier'];
2791
-        }
2792
-        $insert_ids = $update
2793
-            ? wp_update_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2794
-            : wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2795
-        if (! is_array($insert_ids)) {
2796
-            $msg = esc_html__(
2797
-                'An error occurred and the category has not been saved to the database.',
2798
-                'event_espresso'
2799
-            );
2800
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2801
-        } else {
2802
-            $cat_id = $insert_ids['term_id'];
2803
-            $msg = sprintf(esc_html__('The category %s was successfully saved', 'event_espresso'), $category_name);
2804
-            EE_Error::add_success($msg);
2805
-        }
2806
-        return $cat_id;
2807
-    }
2808
-
2809
-
2810
-    /**
2811
-     * Gets categories or count of categories matching the arguments in the request.
2812
-     *
2813
-     * @param int  $per_page
2814
-     * @param int  $current_page
2815
-     * @param bool $count
2816
-     * @return EE_Base_Class[]|EE_Term_Taxonomy[]|int
2817
-     * @throws EE_Error
2818
-     * @throws InvalidArgumentException
2819
-     * @throws InvalidDataTypeException
2820
-     * @throws InvalidInterfaceException
2821
-     */
2822
-    public function get_categories($per_page = 10, $current_page = 1, $count = false)
2823
-    {
2824
-        // testing term stuff
2825
-        $orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'Term.term_id';
2826
-        $order = isset($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
2827
-        $limit = ($current_page - 1) * $per_page;
2828
-        $where = ['taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY];
2829
-        if (isset($this->_req_data['s'])) {
2830
-            $sstr = '%' . $this->_req_data['s'] . '%';
2831
-            $where['OR'] = [
2832
-                'Term.name'   => ['LIKE', $sstr],
2833
-                'description' => ['LIKE', $sstr],
2834
-            ];
2835
-        }
2836
-        $query_params = [
2837
-            $where,
2838
-            'order_by'   => [$orderby => $order],
2839
-            'limit'      => $limit . ',' . $per_page,
2840
-            'force_join' => ['Term'],
2841
-        ];
2842
-        $categories = $count
2843
-            ? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
2844
-            : EEM_Term_Taxonomy::instance()->get_all($query_params);
2845
-        return $categories;
2846
-    }
2847
-
2848
-    /* end category stuff */
2849
-    /**************/
2850
-
2851
-
2852
-    /**
2853
-     * Callback for the `ee_save_timezone_setting` ajax action.
2854
-     *
2855
-     * @throws EE_Error
2856
-     * @throws InvalidArgumentException
2857
-     * @throws InvalidDataTypeException
2858
-     * @throws InvalidInterfaceException
2859
-     */
2860
-    public function save_timezonestring_setting()
2861
-    {
2862
-        $timezone_string = isset($this->_req_data['timezone_selected'])
2863
-            ? $this->_req_data['timezone_selected']
2864
-            : '';
2865
-        if (empty($timezone_string) || ! EEH_DTT_Helper::validate_timezone($timezone_string, false)) {
2866
-            EE_Error::add_error(
2867
-                esc_html__('An invalid timezone string submitted.', 'event_espresso'),
2868
-                __FILE__,
2869
-                __FUNCTION__,
2870
-                __LINE__
2871
-            );
2872
-            $this->_template_args['error'] = true;
2873
-            $this->_return_json();
2874
-        }
2875
-
2876
-        update_option('timezone_string', $timezone_string);
2877
-        EE_Error::add_success(
2878
-            esc_html__('Your timezone string was updated.', 'event_espresso')
2879
-        );
2880
-        $this->_template_args['success'] = true;
2881
-        $this->_return_json(true, ['action' => 'create_new']);
2882
-    }
269
+				'list_table'    => 'Events_Admin_List_Table',
270
+				'help_tabs'     => [
271
+					'events_overview_help_tab'                       => [
272
+						'title'    => esc_html__('Events Overview', 'event_espresso'),
273
+						'filename' => 'events_overview',
274
+					],
275
+					'events_overview_table_column_headings_help_tab' => [
276
+						'title'    => esc_html__('Events Overview Table Column Headings', 'event_espresso'),
277
+						'filename' => 'events_overview_table_column_headings',
278
+					],
279
+					'events_overview_filters_help_tab'               => [
280
+						'title'    => esc_html__('Events Overview Filters', 'event_espresso'),
281
+						'filename' => 'events_overview_filters',
282
+					],
283
+					'events_overview_view_help_tab'                  => [
284
+						'title'    => esc_html__('Events Overview Views', 'event_espresso'),
285
+						'filename' => 'events_overview_views',
286
+					],
287
+					'events_overview_other_help_tab'                 => [
288
+						'title'    => esc_html__('Events Overview Other', 'event_espresso'),
289
+						'filename' => 'events_overview_other',
290
+					],
291
+				],
292
+				// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
293
+				// 'help_tour'     => [
294
+				//     'Event_Overview_Help_Tour',
295
+				//     // 'New_Features_Test_Help_Tour' for testing multiple help tour
296
+				// ],
297
+				'qtips'         => [
298
+					'EE_Event_List_Table_Tips',
299
+				],
300
+				'require_nonce' => false,
301
+				'qtips'         => ['EE_Event_List_Table_Tips'],
302
+			],
303
+			'create_new'             => [
304
+				'nav'           => [
305
+					'label'      => esc_html__('Add Event', 'event_espresso'),
306
+					'order'      => 5,
307
+					'persistent' => false,
308
+				],
309
+				'metaboxes'     => ['_register_event_editor_meta_boxes'],
310
+				'help_tabs'     => [
311
+					'event_editor_help_tab'                            => [
312
+						'title'    => esc_html__('Event Editor', 'event_espresso'),
313
+						'filename' => 'event_editor',
314
+					],
315
+					'event_editor_title_richtexteditor_help_tab'       => [
316
+						'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
317
+						'filename' => 'event_editor_title_richtexteditor',
318
+					],
319
+					'event_editor_venue_details_help_tab'              => [
320
+						'title'    => esc_html__('Event Venue Details', 'event_espresso'),
321
+						'filename' => 'event_editor_venue_details',
322
+					],
323
+					'event_editor_event_datetimes_help_tab'            => [
324
+						'title'    => esc_html__('Event Datetimes', 'event_espresso'),
325
+						'filename' => 'event_editor_event_datetimes',
326
+					],
327
+					'event_editor_event_tickets_help_tab'              => [
328
+						'title'    => esc_html__('Event Tickets', 'event_espresso'),
329
+						'filename' => 'event_editor_event_tickets',
330
+					],
331
+					'event_editor_event_registration_options_help_tab' => [
332
+						'title'    => esc_html__('Event Registration Options', 'event_espresso'),
333
+						'filename' => 'event_editor_event_registration_options',
334
+					],
335
+					'event_editor_tags_categories_help_tab'            => [
336
+						'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
337
+						'filename' => 'event_editor_tags_categories',
338
+					],
339
+					'event_editor_questions_registrants_help_tab'      => [
340
+						'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
341
+						'filename' => 'event_editor_questions_registrants',
342
+					],
343
+					'event_editor_save_new_event_help_tab'             => [
344
+						'title'    => esc_html__('Save New Event', 'event_espresso'),
345
+						'filename' => 'event_editor_save_new_event',
346
+					],
347
+					'event_editor_other_help_tab'                      => [
348
+						'title'    => esc_html__('Event Other', 'event_espresso'),
349
+						'filename' => 'event_editor_other',
350
+					],
351
+				],
352
+				// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
353
+				// 'help_tour'     => [
354
+				//     'Event_Editor_Help_Tour',
355
+				// ],
356
+				'qtips'         => ['EE_Event_Editor_Decaf_Tips'],
357
+				'require_nonce' => false,
358
+			],
359
+			'edit'                   => [
360
+				'nav'           => [
361
+					'label'      => esc_html__('Edit Event', 'event_espresso'),
362
+					'order'      => 5,
363
+					'persistent' => false,
364
+					'url'        => isset($this->_req_data['post'])
365
+						? EE_Admin_Page::add_query_args_and_nonce(
366
+							['post' => $this->_req_data['post'], 'action' => 'edit'],
367
+							$this->_current_page_view_url
368
+						)
369
+						: $this->_admin_base_url,
370
+				],
371
+				'metaboxes'     => ['_register_event_editor_meta_boxes'],
372
+				'help_tabs'     => [
373
+					'event_editor_help_tab'                            => [
374
+						'title'    => esc_html__('Event Editor', 'event_espresso'),
375
+						'filename' => 'event_editor',
376
+					],
377
+					'event_editor_title_richtexteditor_help_tab'       => [
378
+						'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
379
+						'filename' => 'event_editor_title_richtexteditor',
380
+					],
381
+					'event_editor_venue_details_help_tab'              => [
382
+						'title'    => esc_html__('Event Venue Details', 'event_espresso'),
383
+						'filename' => 'event_editor_venue_details',
384
+					],
385
+					'event_editor_event_datetimes_help_tab'            => [
386
+						'title'    => esc_html__('Event Datetimes', 'event_espresso'),
387
+						'filename' => 'event_editor_event_datetimes',
388
+					],
389
+					'event_editor_event_tickets_help_tab'              => [
390
+						'title'    => esc_html__('Event Tickets', 'event_espresso'),
391
+						'filename' => 'event_editor_event_tickets',
392
+					],
393
+					'event_editor_event_registration_options_help_tab' => [
394
+						'title'    => esc_html__('Event Registration Options', 'event_espresso'),
395
+						'filename' => 'event_editor_event_registration_options',
396
+					],
397
+					'event_editor_tags_categories_help_tab'            => [
398
+						'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
399
+						'filename' => 'event_editor_tags_categories',
400
+					],
401
+					'event_editor_questions_registrants_help_tab'      => [
402
+						'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
403
+						'filename' => 'event_editor_questions_registrants',
404
+					],
405
+					'event_editor_save_new_event_help_tab'             => [
406
+						'title'    => esc_html__('Save New Event', 'event_espresso'),
407
+						'filename' => 'event_editor_save_new_event',
408
+					],
409
+					'event_editor_other_help_tab'                      => [
410
+						'title'    => esc_html__('Event Other', 'event_espresso'),
411
+						'filename' => 'event_editor_other',
412
+					],
413
+				],
414
+				'require_nonce' => false,
415
+			],
416
+			'default_event_settings' => [
417
+				'nav'           => [
418
+					'label' => esc_html__('Default Settings', 'event_espresso'),
419
+					'order' => 40,
420
+				],
421
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, ['_publish_post_box']),
422
+				'labels'        => [
423
+					'publishbox' => esc_html__('Update Settings', 'event_espresso'),
424
+				],
425
+				'help_tabs'     => [
426
+					'default_settings_help_tab'        => [
427
+						'title'    => esc_html__('Default Event Settings', 'event_espresso'),
428
+						'filename' => 'events_default_settings',
429
+					],
430
+					'default_settings_status_help_tab' => [
431
+						'title'    => esc_html__('Default Registration Status', 'event_espresso'),
432
+						'filename' => 'events_default_settings_status',
433
+					],
434
+					'default_maximum_tickets_help_tab' => [
435
+						'title'    => esc_html__('Default Maximum Tickets Per Order', 'event_espresso'),
436
+						'filename' => 'events_default_settings_max_tickets',
437
+					],
438
+				],
439
+				// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
440
+				// 'help_tour'     => ['Event_Default_Settings_Help_Tour'],
441
+				'require_nonce' => false,
442
+			],
443
+			// template settings
444
+			'template_settings'      => [
445
+				'nav'           => [
446
+					'label' => esc_html__('Templates', 'event_espresso'),
447
+					'order' => 30,
448
+				],
449
+				'metaboxes'     => $this->_default_espresso_metaboxes,
450
+				'help_tabs'     => [
451
+					'general_settings_templates_help_tab' => [
452
+						'title'    => esc_html__('Templates', 'event_espresso'),
453
+						'filename' => 'general_settings_templates',
454
+					],
455
+				],
456
+				// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
457
+				// 'help_tour'     => ['Templates_Help_Tour'],
458
+				'require_nonce' => false,
459
+			],
460
+			// event category stuff
461
+			'add_category'           => [
462
+				'nav'           => [
463
+					'label'      => esc_html__('Add Category', 'event_espresso'),
464
+					'order'      => 15,
465
+					'persistent' => false,
466
+				],
467
+				'help_tabs'     => [
468
+					'add_category_help_tab' => [
469
+						'title'    => esc_html__('Add New Event Category', 'event_espresso'),
470
+						'filename' => 'events_add_category',
471
+					],
472
+				],
473
+				// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
474
+				// 'help_tour'     => ['Event_Add_Category_Help_Tour'],
475
+				'metaboxes'     => ['_publish_post_box'],
476
+				'require_nonce' => false,
477
+			],
478
+			'edit_category'          => [
479
+				'nav'           => [
480
+					'label'      => esc_html__('Edit Category', 'event_espresso'),
481
+					'order'      => 15,
482
+					'persistent' => false,
483
+					'url'        => isset($this->_req_data['EVT_CAT_ID'])
484
+						? add_query_arg(
485
+							['EVT_CAT_ID' => $this->_req_data['EVT_CAT_ID']],
486
+							$this->_current_page_view_url
487
+						)
488
+						: $this->_admin_base_url,
489
+				],
490
+				'help_tabs'     => [
491
+					'edit_category_help_tab' => [
492
+						'title'    => esc_html__('Edit Event Category', 'event_espresso'),
493
+						'filename' => 'events_edit_category',
494
+					],
495
+				],
496
+				/*'help_tour' => ['Event_Edit_Category_Help_Tour'],*/
497
+				'metaboxes'     => ['_publish_post_box'],
498
+				'require_nonce' => false,
499
+			],
500
+			'category_list'          => [
501
+				'nav'           => [
502
+					'label' => esc_html__('Categories', 'event_espresso'),
503
+					'order' => 20,
504
+				],
505
+				'list_table'    => 'Event_Categories_Admin_List_Table',
506
+				'help_tabs'     => [
507
+					'events_categories_help_tab'                       => [
508
+						'title'    => esc_html__('Event Categories', 'event_espresso'),
509
+						'filename' => 'events_categories',
510
+					],
511
+					'events_categories_table_column_headings_help_tab' => [
512
+						'title'    => esc_html__('Event Categories Table Column Headings', 'event_espresso'),
513
+						'filename' => 'events_categories_table_column_headings',
514
+					],
515
+					'events_categories_view_help_tab'                  => [
516
+						'title'    => esc_html__('Event Categories Views', 'event_espresso'),
517
+						'filename' => 'events_categories_views',
518
+					],
519
+					'events_categories_other_help_tab'                 => [
520
+						'title'    => esc_html__('Event Categories Other', 'event_espresso'),
521
+						'filename' => 'events_categories_other',
522
+					],
523
+				],
524
+				// disabled temporarily. see: https://github.com/eventespresso/eventsmart.com-website/issues/836
525
+				// 'help_tour'     => [
526
+				//     'Event_Categories_Help_Tour',
527
+				// ],
528
+				'metaboxes'     => $this->_default_espresso_metaboxes,
529
+				'require_nonce' => false,
530
+			],
531
+			'preview_deletion'       => [
532
+				'nav'           => [
533
+					'label'      => esc_html__('Preview Deletion', 'event_espresso'),
534
+					'order'      => 15,
535
+					'persistent' => false,
536
+					'url'        => '',
537
+				],
538
+				'require_nonce' => false,
539
+			],
540
+		];
541
+		// only load EE_Event_Editor_Decaf_Tips if domain is not caffeinated
542
+		$domain = $this->loader->getShared('EventEspresso\core\domain\Domain');
543
+		if (! $domain->isCaffeinated()) {
544
+			$this->_page_config['create_new']['qtips'] = ['EE_Event_Editor_Decaf_Tips'];
545
+			$this->_page_config['edit']['qtips'] = ['EE_Event_Editor_Decaf_Tips'];
546
+		}
547
+	}
548
+
549
+
550
+	/**
551
+	 * Used to register any global screen options if necessary for every route in this admin page group.
552
+	 */
553
+	protected function _add_screen_options()
554
+	{
555
+	}
556
+
557
+
558
+	/**
559
+	 * Implementing the screen options for the 'default' route.
560
+	 *
561
+	 * @throws InvalidArgumentException
562
+	 * @throws InvalidDataTypeException
563
+	 * @throws InvalidInterfaceException
564
+	 */
565
+	protected function _add_screen_options_default()
566
+	{
567
+		$this->_per_page_screen_option();
568
+	}
569
+
570
+
571
+	/**
572
+	 * Implementing screen options for the category list route.
573
+	 *
574
+	 * @throws InvalidArgumentException
575
+	 * @throws InvalidDataTypeException
576
+	 * @throws InvalidInterfaceException
577
+	 */
578
+	protected function _add_screen_options_category_list()
579
+	{
580
+		$page_title = $this->_admin_page_title;
581
+		$this->_admin_page_title = esc_html__('Categories', 'event_espresso');
582
+		$this->_per_page_screen_option();
583
+		$this->_admin_page_title = $page_title;
584
+	}
585
+
586
+
587
+	/**
588
+	 * Used to register any global feature pointers for the admin page group.
589
+	 */
590
+	protected function _add_feature_pointers()
591
+	{
592
+	}
593
+
594
+
595
+	/**
596
+	 * Registers and enqueues any global scripts and styles for the entire admin page group.
597
+	 */
598
+	public function load_scripts_styles()
599
+	{
600
+		wp_register_style(
601
+			'events-admin-css',
602
+			EVENTS_ASSETS_URL . 'events-admin-page.css',
603
+			[],
604
+			EVENT_ESPRESSO_VERSION
605
+		);
606
+		wp_register_style(
607
+			'ee-cat-admin',
608
+			EVENTS_ASSETS_URL . 'ee-cat-admin.css',
609
+			[],
610
+			EVENT_ESPRESSO_VERSION
611
+		);
612
+		wp_enqueue_style('events-admin-css');
613
+		wp_enqueue_style('ee-cat-admin');
614
+		// scripts
615
+		wp_register_script(
616
+			'event_editor_js',
617
+			EVENTS_ASSETS_URL . 'event_editor.js',
618
+			['ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'],
619
+			EVENT_ESPRESSO_VERSION,
620
+			true
621
+		);
622
+	}
623
+
624
+
625
+	/**
626
+	 * Enqueuing scripts and styles specific to this view
627
+	 */
628
+	public function load_scripts_styles_create_new()
629
+	{
630
+		$this->load_scripts_styles_edit();
631
+	}
632
+
633
+
634
+	/**
635
+	 * Enqueuing scripts and styles specific to this view
636
+	 */
637
+	public function load_scripts_styles_edit()
638
+	{
639
+		// styles
640
+		wp_enqueue_style('espresso-ui-theme');
641
+		wp_register_style(
642
+			'event-editor-css',
643
+			EVENTS_ASSETS_URL . 'event-editor.css',
644
+			['ee-admin-css'],
645
+			EVENT_ESPRESSO_VERSION
646
+		);
647
+		wp_enqueue_style('event-editor-css');
648
+		// scripts
649
+		if (! $this->admin_config->useAdvancedEditor()) {
650
+			wp_register_script(
651
+				'event-datetime-metabox',
652
+				EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
653
+				['event_editor_js', 'ee-datepicker'],
654
+				EVENT_ESPRESSO_VERSION
655
+			);
656
+			wp_enqueue_script('event-datetime-metabox');
657
+		}
658
+	}
659
+
660
+
661
+	/**
662
+	 * Populating the _views property for the category list table view.
663
+	 */
664
+	protected function _set_list_table_views_category_list()
665
+	{
666
+		$this->_views = [
667
+			'all' => [
668
+				'slug'        => 'all',
669
+				'label'       => esc_html__('All', 'event_espresso'),
670
+				'count'       => 0,
671
+				'bulk_action' => [
672
+					'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
673
+				],
674
+			],
675
+		];
676
+	}
677
+
678
+
679
+	/**
680
+	 * For adding anything that fires on the admin_init hook for any route within this admin page group.
681
+	 */
682
+	public function admin_init()
683
+	{
684
+		EE_Registry::$i18n_js_strings['image_confirm'] = esc_html__(
685
+			'Do you really want to delete this image? Please remember to update your event to complete the removal.',
686
+			'event_espresso'
687
+		);
688
+	}
689
+
690
+
691
+	/**
692
+	 * For adding anything that should be triggered on the admin_notices hook for any route within this admin page
693
+	 * group.
694
+	 */
695
+	public function admin_notices()
696
+	{
697
+	}
698
+
699
+
700
+	/**
701
+	 * For adding anything that should be triggered on the `admin_print_footer_scripts` hook for any route within
702
+	 * this admin page group.
703
+	 */
704
+	public function admin_footer_scripts()
705
+	{
706
+	}
707
+
708
+
709
+	/**
710
+	 * Call this function to verify if an event is public and has tickets for sale.  If it does, then we need to show a
711
+	 * warning (via EE_Error::add_error());
712
+	 *
713
+	 * @param EE_Event $event Event object
714
+	 * @param string   $req_type
715
+	 * @return void
716
+	 * @throws EE_Error
717
+	 * @access public
718
+	 */
719
+	public function verify_event_edit($event = null, $req_type = '')
720
+	{
721
+		// don't need to do this when processing
722
+		if (! empty($req_type)) {
723
+			return;
724
+		}
725
+		// no event?
726
+		if (! $event instanceof EE_Event) {
727
+			$event = $this->_cpt_model_obj;
728
+		}
729
+		// STILL no event?
730
+		if (! $event instanceof EE_Event) {
731
+			return;
732
+		}
733
+		$orig_status = $event->status();
734
+		// first check if event is active.
735
+		if (
736
+			$orig_status === EEM_Event::cancelled
737
+			|| $orig_status === EEM_Event::postponed
738
+			|| $event->is_expired()
739
+			|| $event->is_inactive()
740
+		) {
741
+			return;
742
+		}
743
+		// made it here so it IS active... next check that any of the tickets are sold.
744
+		if ($event->is_sold_out(true)) {
745
+			if ($orig_status !== EEM_Event::sold_out && $event->status() !== $orig_status) {
746
+				EE_Error::add_attention(
747
+					sprintf(
748
+						esc_html__(
749
+							'Please note that the Event Status has automatically been changed to %s because there are no more spaces available for this event.  However, this change is not permanent until you update the event.  You can change the status back to something else before updating if you wish.',
750
+							'event_espresso'
751
+						),
752
+						EEH_Template::pretty_status(EEM_Event::sold_out, false, 'sentence')
753
+					)
754
+				);
755
+			}
756
+			return;
757
+		}
758
+		if ($orig_status === EEM_Event::sold_out) {
759
+			EE_Error::add_attention(
760
+				sprintf(
761
+					esc_html__(
762
+						'Please note that the Event Status has automatically been changed to %s because more spaces have become available for this event, most likely due to abandoned transactions freeing up reserved tickets.  However, this change is not permanent until you update the event. If you wish, you can change the status back to something else before updating.',
763
+						'event_espresso'
764
+					),
765
+					EEH_Template::pretty_status($event->status(), false, 'sentence')
766
+				)
767
+			);
768
+		}
769
+		// now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
770
+		if (! $event->tickets_on_sale()) {
771
+			return;
772
+		}
773
+		// made it here so show warning
774
+		$this->_edit_event_warning();
775
+	}
776
+
777
+
778
+	/**
779
+	 * This is the text used for when an event is being edited that is public and has tickets for sale.
780
+	 * When needed, hook this into a EE_Error::add_error() notice.
781
+	 *
782
+	 * @access protected
783
+	 * @return void
784
+	 */
785
+	protected function _edit_event_warning()
786
+	{
787
+		// we don't want to add warnings during these requests
788
+		if (isset($this->_req_data['action']) && $this->_req_data['action'] === 'editpost') {
789
+			return;
790
+		}
791
+		EE_Error::add_attention(
792
+			sprintf(
793
+				esc_html__(
794
+					'Your event is open for registration. Making changes may disrupt any transactions in progress. %sLearn more%s',
795
+					'event_espresso'
796
+				),
797
+				'<a class="espresso-help-tab-lnk">',
798
+				'</a>'
799
+			)
800
+		);
801
+	}
802
+
803
+
804
+	/**
805
+	 * When a user is creating a new event, notify them if they haven't set their timezone.
806
+	 * Otherwise, do the normal logic
807
+	 *
808
+	 * @return string
809
+	 * @throws EE_Error
810
+	 * @throws InvalidArgumentException
811
+	 * @throws InvalidDataTypeException
812
+	 * @throws InvalidInterfaceException
813
+	 */
814
+	protected function _create_new_cpt_item()
815
+	{
816
+		$has_timezone_string = get_option('timezone_string');
817
+		// only nag them about setting their timezone if it's their first event, and they haven't already done it
818
+		if (! $has_timezone_string && ! EEM_Event::instance()->exists([])) {
819
+			EE_Error::add_attention(
820
+				sprintf(
821
+					__(
822
+						'Your website\'s timezone is currently set to a UTC offset. We recommend updating your timezone to a city or region near you before you create an event. Change your timezone now:%1$s%2$s%3$sChange Timezone%4$s',
823
+						'event_espresso'
824
+					),
825
+					'<br>',
826
+					'<select id="timezone_string" name="timezone_string" aria-describedby="timezone-description">'
827
+					. EEH_DTT_Helper::wp_timezone_choice('', EEH_DTT_Helper::get_user_locale())
828
+					. '</select>',
829
+					'<button class="button button-secondary timezone-submit">',
830
+					'</button><span class="spinner"></span>'
831
+				),
832
+				__FILE__,
833
+				__FUNCTION__,
834
+				__LINE__
835
+			);
836
+		}
837
+		parent::_create_new_cpt_item();
838
+	}
839
+
840
+
841
+	/**
842
+	 * Sets the _views property for the default route in this admin page group.
843
+	 */
844
+	protected function _set_list_table_views_default()
845
+	{
846
+		$this->_views = [
847
+			'all'   => [
848
+				'slug'        => 'all',
849
+				'label'       => esc_html__('View All Events', 'event_espresso'),
850
+				'count'       => 0,
851
+				'bulk_action' => [
852
+					'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
853
+				],
854
+			],
855
+			'draft' => [
856
+				'slug'        => 'draft',
857
+				'label'       => esc_html__('Draft', 'event_espresso'),
858
+				'count'       => 0,
859
+				'bulk_action' => [
860
+					'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
861
+				],
862
+			],
863
+		];
864
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_events', 'espresso_events_trash_events')) {
865
+			$this->_views['trash'] = [
866
+				'slug'        => 'trash',
867
+				'label'       => esc_html__('Trash', 'event_espresso'),
868
+				'count'       => 0,
869
+				'bulk_action' => [
870
+					'restore_events' => esc_html__('Restore From Trash', 'event_espresso'),
871
+					'delete_events'  => esc_html__('Delete Permanently', 'event_espresso'),
872
+				],
873
+			];
874
+		}
875
+	}
876
+
877
+
878
+	/**
879
+	 * Provides the legend item array for the default list table view.
880
+	 *
881
+	 * @return array
882
+	 */
883
+	protected function _event_legend_items()
884
+	{
885
+		$items = [
886
+			'view_details'   => [
887
+				'class' => 'dashicons dashicons-search',
888
+				'desc'  => esc_html__('View Event', 'event_espresso'),
889
+			],
890
+			'edit_event'     => [
891
+				'class' => 'ee-icon ee-icon-calendar-edit',
892
+				'desc'  => esc_html__('Edit Event Details', 'event_espresso'),
893
+			],
894
+			'view_attendees' => [
895
+				'class' => 'dashicons dashicons-groups',
896
+				'desc'  => esc_html__('View Registrations for Event', 'event_espresso'),
897
+			],
898
+		];
899
+		$items = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
900
+		$statuses = [
901
+			'sold_out_status'  => [
902
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
903
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
904
+			],
905
+			'active_status'    => [
906
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
907
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
908
+			],
909
+			'upcoming_status'  => [
910
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
911
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
912
+			],
913
+			'postponed_status' => [
914
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
915
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
916
+			],
917
+			'cancelled_status' => [
918
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
919
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
920
+			],
921
+			'expired_status'   => [
922
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
923
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
924
+			],
925
+			'inactive_status'  => [
926
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
927
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
928
+			],
929
+		];
930
+		$statuses = apply_filters('FHEE__Events_Admin_Page__event_legend_items__statuses', $statuses);
931
+		return array_merge($items, $statuses);
932
+	}
933
+
934
+
935
+	/**
936
+	 * @return EEM_Event
937
+	 * @throws EE_Error
938
+	 * @throws InvalidArgumentException
939
+	 * @throws InvalidDataTypeException
940
+	 * @throws InvalidInterfaceException
941
+	 * @throws ReflectionException
942
+	 */
943
+	private function _event_model()
944
+	{
945
+		if (! $this->_event_model instanceof EEM_Event) {
946
+			$this->_event_model = EE_Registry::instance()->load_model('Event');
947
+		}
948
+		return $this->_event_model;
949
+	}
950
+
951
+
952
+	/**
953
+	 * Adds extra buttons to the WP CPT permalink field row.
954
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
955
+	 *
956
+	 * @param string $return    the current html
957
+	 * @param int    $id        the post id for the page
958
+	 * @param string $new_title What the title is
959
+	 * @param string $new_slug  what the slug is
960
+	 * @return string            The new html string for the permalink area
961
+	 */
962
+	public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
963
+	{
964
+		// make sure this is only when editing
965
+		if (! empty($id)) {
966
+			$post = get_post($id);
967
+			$return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
968
+					   . esc_html__('Shortcode', 'event_espresso')
969
+					   . '</a> ';
970
+			$return .= '<input id="shortcode" type="hidden" value="[ESPRESSO_TICKET_SELECTOR event_id='
971
+					   . $post->ID
972
+					   . ']">';
973
+		}
974
+		return $return;
975
+	}
976
+
977
+
978
+	/**
979
+	 * _events_overview_list_table
980
+	 * This contains the logic for showing the events_overview list
981
+	 *
982
+	 * @access protected
983
+	 * @return void
984
+	 * @throws DomainException
985
+	 * @throws EE_Error
986
+	 * @throws InvalidArgumentException
987
+	 * @throws InvalidDataTypeException
988
+	 * @throws InvalidInterfaceException
989
+	 */
990
+	protected function _events_overview_list_table()
991
+	{
992
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
993
+		$this->_template_args['after_list_table'] = ! empty($this->_template_args['after_list_table'])
994
+			? (array) $this->_template_args['after_list_table']
995
+			: [];
996
+		$this->_template_args['after_list_table']['view_event_list_button'] = EEH_HTML::br()
997
+																			  . EEH_Template::get_button_or_link(
998
+																				  get_post_type_archive_link('espresso_events'),
999
+																				  esc_html__('View Event Archive Page', 'event_espresso'),
1000
+																				  'button'
1001
+																			  );
1002
+		$this->_template_args['after_list_table']['legend'] = $this->_display_legend($this->_event_legend_items());
1003
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
1004
+			'create_new',
1005
+			'add',
1006
+			[],
1007
+			'add-new-h2'
1008
+		);
1009
+		$this->display_admin_list_table_page_with_no_sidebar();
1010
+	}
1011
+
1012
+
1013
+	/**
1014
+	 * this allows for extra misc actions in the default WP publish box
1015
+	 *
1016
+	 * @return void
1017
+	 * @throws DomainException
1018
+	 * @throws EE_Error
1019
+	 * @throws InvalidArgumentException
1020
+	 * @throws InvalidDataTypeException
1021
+	 * @throws InvalidInterfaceException
1022
+	 * @throws ReflectionException
1023
+	 */
1024
+	public function extra_misc_actions_publish_box()
1025
+	{
1026
+		$this->_generate_publish_box_extra_content();
1027
+	}
1028
+
1029
+
1030
+	/**
1031
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
1032
+	 * saved.
1033
+	 * Typically you would use this to save any additional data.
1034
+	 * Keep in mind also that "save_post" runs on EVERY post update to the database.
1035
+	 * ALSO very important.  When a post transitions from scheduled to published,
1036
+	 * the save_post action is fired but you will NOT have any _POST data containing any extra info you may have from
1037
+	 * other meta saves. So MAKE sure that you handle this accordingly.
1038
+	 *
1039
+	 * @access protected
1040
+	 * @abstract
1041
+	 * @param string $post_id The ID of the cpt that was saved (so you can link relationally)
1042
+	 * @param object $post    The post object of the cpt that was saved.
1043
+	 * @return void
1044
+	 * @throws EE_Error
1045
+	 * @throws InvalidArgumentException
1046
+	 * @throws InvalidDataTypeException
1047
+	 * @throws InvalidInterfaceException
1048
+	 * @throws ReflectionException
1049
+	 */
1050
+	protected function _insert_update_cpt_item($post_id, $post)
1051
+	{
1052
+		if ($post instanceof WP_Post && $post->post_type !== 'espresso_events') {
1053
+			// get out we're not processing an event save.
1054
+			return;
1055
+		}
1056
+		$event_values = [
1057
+			'EVT_member_only'     => ! empty($this->_req_data['member_only']) ? 1 : 0,
1058
+			'EVT_allow_overflow'  => ! empty($this->_req_data['EVT_allow_overflow']) ? 1 : 0,
1059
+			'EVT_timezone_string' => ! empty($this->_req_data['timezone_string'])
1060
+				? sanitize_text_field($this->_req_data['timezone_string'])
1061
+				: null,
1062
+		];
1063
+		/** @var FeatureFlags $flags */
1064
+		$flags = $this->loader->getShared('EventEspresso\core\domain\services\capabilities\FeatureFlags');
1065
+		// check if the new EDTR reg options meta box is being used, and if so, don't run updates for legacy version
1066
+		if (! $this->admin_config->useAdvancedEditor() || ! $flags->featureAllowed('use_reg_options_meta_box')) {
1067
+			$event_values['EVT_display_ticket_selector'] =
1068
+				! empty($this->_req_data['display_ticket_selector'])
1069
+					? 1
1070
+					: 0;
1071
+			$event_values['EVT_additional_limit'] = min(
1072
+				apply_filters('FHEE__EE_Events_Admin__insert_update_cpt_item__EVT_additional_limit_max', 255),
1073
+				! empty($this->_req_data['additional_limit'])
1074
+					? absint($this->_req_data['additional_limit'])
1075
+					: null
1076
+			);
1077
+			$event_values['EVT_default_registration_status'] =
1078
+				! empty($this->_req_data['EVT_default_registration_status'])
1079
+					? sanitize_text_field($this->_req_data['EVT_default_registration_status'])
1080
+					: EE_Registry::instance()->CFG->registration->default_STS_ID;
1081
+			$event_values['EVT_external_URL'] = ! empty($this->_req_data['externalURL'])
1082
+				? esc_url_raw($this->_req_data['externalURL'])
1083
+				: null;
1084
+			$event_values['EVT_phone'] = ! empty($this->_req_data['event_phone'])
1085
+				? sanitize_text_field($this->_req_data['event_phone'])
1086
+				: null;
1087
+		}
1088
+		// update event
1089
+		$success = $this->_event_model()->update_by_ID($event_values, $post_id);
1090
+		// get event_object for other metaboxes... though it would seem to make sense to just use $this->_event_model()->get_one_by_ID( $post_id ).. i have to setup where conditions to override the filters in the model that filter out autodraft and inherit statuses so we GET the inherit id!
1091
+		$get_one_where = [
1092
+			$this->_event_model()->primary_key_name() => $post_id,
1093
+			'OR'                                      => [
1094
+				'status'   => $post->post_status,
1095
+				// if trying to "Publish" a sold out event, it's status will get switched back to "sold_out" in the db,
1096
+				// but the returned object here has a status of "publish", so use the original post status as well
1097
+				'status*1' => $this->_req_data['original_post_status'],
1098
+			],
1099
+		];
1100
+		$event = $this->_event_model()->get_one([$get_one_where]);
1101
+		// the following are default callbacks for event attachment updates that can be overridden by caffeinated functionality and/or addons.
1102
+		$event_update_callbacks = apply_filters(
1103
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
1104
+			[
1105
+				[$this, '_default_venue_update'],
1106
+				[$this, '_default_tickets_update'],
1107
+			]
1108
+		);
1109
+		$att_success = true;
1110
+		foreach ($event_update_callbacks as $e_callback) {
1111
+			$_success = is_callable($e_callback)
1112
+				? $e_callback($event, $this->_req_data)
1113
+				: false;
1114
+			// if ANY of these updates fail then we want the appropriate global error message
1115
+			$att_success = ! $att_success ? $att_success : $_success;
1116
+		}
1117
+		// any errors?
1118
+		if ($success && false === $att_success) {
1119
+			EE_Error::add_error(
1120
+				esc_html__(
1121
+					'Event Details saved successfully but something went wrong with saving attachments.',
1122
+					'event_espresso'
1123
+				),
1124
+				__FILE__,
1125
+				__FUNCTION__,
1126
+				__LINE__
1127
+			);
1128
+		} elseif ($success === false) {
1129
+			EE_Error::add_error(
1130
+				esc_html__('Event Details did not save successfully.', 'event_espresso'),
1131
+				__FILE__,
1132
+				__FUNCTION__,
1133
+				__LINE__
1134
+			);
1135
+		}
1136
+	}
1137
+
1138
+
1139
+	/**
1140
+	 * @param int $post_id
1141
+	 * @param int $revision_id
1142
+	 * @throws EE_Error
1143
+	 * @throws InvalidArgumentException
1144
+	 * @throws InvalidDataTypeException
1145
+	 * @throws InvalidInterfaceException
1146
+	 * @throws ReflectionException
1147
+	 * @see parent::restore_item()
1148
+	 */
1149
+	protected function _restore_cpt_item($post_id, $revision_id)
1150
+	{
1151
+		// copy existing event meta to new post
1152
+		$post_evt = $this->_event_model()->get_one_by_ID($post_id);
1153
+		if ($post_evt instanceof EE_Event) {
1154
+			// meta revision restore
1155
+			$post_evt->restore_revision($revision_id);
1156
+			// related objs restore
1157
+			$post_evt->restore_revision($revision_id, ['Venue', 'Datetime', 'Price']);
1158
+		}
1159
+	}
1160
+
1161
+
1162
+	/**
1163
+	 * Attach the venue to the Event
1164
+	 *
1165
+	 * @param EE_Event $evtobj Event Object to add the venue to
1166
+	 * @param array    $data   The request data from the form
1167
+	 * @return bool           Success or fail.
1168
+	 * @throws EE_Error
1169
+	 * @throws InvalidArgumentException
1170
+	 * @throws InvalidDataTypeException
1171
+	 * @throws InvalidInterfaceException
1172
+	 * @throws ReflectionException
1173
+	 */
1174
+	protected function _default_venue_update(EE_Event $evtobj, $data)
1175
+	{
1176
+		require_once(EE_MODELS . 'EEM_Venue.model.php');
1177
+		$venue_model = EE_Registry::instance()->load_model('Venue');
1178
+		$rows_affected = null;
1179
+		$venue_id = ! empty($data['venue_id']) ? $data['venue_id'] : null;
1180
+		// very important.  If we don't have a venue name...
1181
+		// then we'll get out because not necessary to create empty venue
1182
+		if (empty($data['venue_title'])) {
1183
+			return false;
1184
+		}
1185
+		$venue_array = [
1186
+			'VNU_wp_user'         => $evtobj->get('EVT_wp_user'),
1187
+			'VNU_name'            => ! empty($data['venue_title']) ? $data['venue_title'] : null,
1188
+			'VNU_desc'            => ! empty($data['venue_description']) ? $data['venue_description'] : null,
1189
+			'VNU_identifier'      => ! empty($data['venue_identifier']) ? $data['venue_identifier'] : null,
1190
+			'VNU_short_desc'      => ! empty($data['venue_short_description']) ? $data['venue_short_description']
1191
+				: null,
1192
+			'VNU_address'         => ! empty($data['address']) ? $data['address'] : null,
1193
+			'VNU_address2'        => ! empty($data['address2']) ? $data['address2'] : null,
1194
+			'VNU_city'            => ! empty($data['city']) ? $data['city'] : null,
1195
+			'STA_ID'              => ! empty($data['state']) ? $data['state'] : null,
1196
+			'CNT_ISO'             => ! empty($data['countries']) ? $data['countries'] : null,
1197
+			'VNU_zip'             => ! empty($data['zip']) ? $data['zip'] : null,
1198
+			'VNU_phone'           => ! empty($data['venue_phone']) ? $data['venue_phone'] : null,
1199
+			'VNU_capacity'        => ! empty($data['venue_capacity']) ? $data['venue_capacity'] : null,
1200
+			'VNU_url'             => ! empty($data['venue_url']) ? $data['venue_url'] : null,
1201
+			'VNU_virtual_phone'   => ! empty($data['virtual_phone']) ? $data['virtual_phone'] : null,
1202
+			'VNU_virtual_url'     => ! empty($data['virtual_url']) ? $data['virtual_url'] : null,
1203
+			'VNU_enable_for_gmap' => isset($data['enable_for_gmap']) ? 1 : 0,
1204
+			'status'              => 'publish',
1205
+		];
1206
+		// if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1207
+		if (! empty($venue_id)) {
1208
+			$update_where = [$venue_model->primary_key_name() => $venue_id];
1209
+			$rows_affected = $venue_model->update($venue_array, [$update_where]);
1210
+			// we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
1211
+			$evtobj->_add_relation_to($venue_id, 'Venue');
1212
+			return $rows_affected > 0;
1213
+		}
1214
+		// we insert the venue
1215
+		$venue_id = $venue_model->insert($venue_array);
1216
+		$evtobj->_add_relation_to($venue_id, 'Venue');
1217
+		return ! empty($venue_id) ? true : false;
1218
+		// when we have the ancestor come in it's already been handled by the revision save.
1219
+	}
1220
+
1221
+
1222
+	/**
1223
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
1224
+	 *
1225
+	 * @param EE_Event $evtobj The Event object we're attaching data to
1226
+	 * @param array    $data   The request data from the form
1227
+	 * @return array
1228
+	 * @throws EE_Error
1229
+	 * @throws InvalidArgumentException
1230
+	 * @throws InvalidDataTypeException
1231
+	 * @throws InvalidInterfaceException
1232
+	 * @throws ReflectionException
1233
+	 * @throws Exception
1234
+	 */
1235
+	protected function _default_tickets_update(EE_Event $evtobj, $data)
1236
+	{
1237
+		if ($this->admin_config->useAdvancedEditor()) {
1238
+			return [];
1239
+		}
1240
+		$success = true;
1241
+		$saved_dtt = null;
1242
+		$saved_tickets = [];
1243
+		$incoming_date_formats = ['Y-m-d', 'h:i a'];
1244
+		foreach ($data['edit_event_datetimes'] as $row => $dtt) {
1245
+			// trim all values to ensure any excess whitespace is removed.
1246
+			$dtt = array_map('trim', $dtt);
1247
+			$dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && ! empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end']
1248
+				: $dtt['DTT_EVT_start'];
1249
+			$datetime_values = [
1250
+				'DTT_ID'        => ! empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : null,
1251
+				'DTT_EVT_start' => $dtt['DTT_EVT_start'],
1252
+				'DTT_EVT_end'   => $dtt['DTT_EVT_end'],
1253
+				'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? EE_INF : $dtt['DTT_reg_limit'],
1254
+				'DTT_order'     => $row,
1255
+			];
1256
+			// if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
1257
+			if (! empty($dtt['DTT_ID'])) {
1258
+				$DTM = EE_Registry::instance()
1259
+								  ->load_model('Datetime', [$evtobj->get_timezone()])
1260
+								  ->get_one_by_ID($dtt['DTT_ID']);
1261
+				$DTM->set_date_format($incoming_date_formats[0]);
1262
+				$DTM->set_time_format($incoming_date_formats[1]);
1263
+				foreach ($datetime_values as $field => $value) {
1264
+					$DTM->set($field, $value);
1265
+				}
1266
+				// make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
1267
+				$saved_dtts[ $DTM->ID() ] = $DTM;
1268
+			} else {
1269
+				$DTM = EE_Registry::instance()->load_class(
1270
+					'Datetime',
1271
+					[$datetime_values, $evtobj->get_timezone(), $incoming_date_formats],
1272
+					false,
1273
+					false
1274
+				);
1275
+				foreach ($datetime_values as $field => $value) {
1276
+					$DTM->set($field, $value);
1277
+				}
1278
+			}
1279
+			$DTM->save();
1280
+			$DTT = $evtobj->_add_relation_to($DTM, 'Datetime');
1281
+			// load DTT helper
1282
+			// before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
1283
+			if ($DTT->get_raw('DTT_EVT_start') > $DTT->get_raw('DTT_EVT_end')) {
1284
+				$DTT->set('DTT_EVT_end', $DTT->get('DTT_EVT_start'));
1285
+				$DTT = EEH_DTT_Helper::date_time_add($DTT, 'DTT_EVT_end', 'days');
1286
+				$DTT->save();
1287
+			}
1288
+			// now we got to make sure we add the new DTT_ID to the $saved_dtts array  because it is possible there was a new one created for the autosave.
1289
+			$saved_dtt = $DTT;
1290
+			$success = ! $success ? $success : $DTT;
1291
+			// if ANY of these updates fail then we want the appropriate global error message.
1292
+			// //todo this is actually sucky we need a better error message but this is what it is for now.
1293
+		}
1294
+		// no dtts get deleted so we don't do any of that logic here.
1295
+		// update tickets next
1296
+		$old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
1297
+		foreach ($data['edit_tickets'] as $row => $tkt) {
1298
+			$incoming_date_formats = ['Y-m-d', 'h:i a'];
1299
+			$update_prices = false;
1300
+			$ticket_price = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1301
+				? $data['edit_prices'][ $row ][1]['PRC_amount'] : 0;
1302
+			// trim inputs to ensure any excess whitespace is removed.
1303
+			$tkt = array_map('trim', $tkt);
1304
+			if (empty($tkt['TKT_start_date'])) {
1305
+				// let's use now in the set timezone.
1306
+				$now = new DateTime('now', new DateTimeZone($evtobj->get_timezone()));
1307
+				$tkt['TKT_start_date'] = $now->format($incoming_date_formats[0] . ' ' . $incoming_date_formats[1]);
1308
+			}
1309
+			if (empty($tkt['TKT_end_date'])) {
1310
+				// use the start date of the first datetime
1311
+				$dtt = $evtobj->first_datetime();
1312
+				$tkt['TKT_end_date'] = $dtt->start_date_and_time(
1313
+					$incoming_date_formats[0],
1314
+					$incoming_date_formats[1]
1315
+				);
1316
+			}
1317
+			$TKT_values = [
1318
+				'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
1319
+				'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
1320
+				'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
1321
+				'TKT_description' => ! empty($tkt['TKT_description']) ? $tkt['TKT_description'] : '',
1322
+				'TKT_start_date'  => $tkt['TKT_start_date'],
1323
+				'TKT_end_date'    => $tkt['TKT_end_date'],
1324
+				'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === '' ? EE_INF : $tkt['TKT_qty'],
1325
+				'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === '' ? EE_INF : $tkt['TKT_uses'],
1326
+				'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
1327
+				'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
1328
+				'TKT_row'         => $row,
1329
+				'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : $row,
1330
+				'TKT_price'       => $ticket_price,
1331
+			];
1332
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly, which means in turn that the prices will become new prices as well.
1333
+			if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
1334
+				$TKT_values['TKT_ID'] = 0;
1335
+				$TKT_values['TKT_is_default'] = 0;
1336
+				$TKT_values['TKT_price'] = $ticket_price;
1337
+				$update_prices = true;
1338
+			}
1339
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
1340
+			// we actually do our saves a head of doing any add_relations to because its entirely possible that this ticket didn't removed or added to any datetime in the session but DID have it's items modified.
1341
+			// keep in mind that if the TKT has been sold (and we have changed pricing information), then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1342
+			if (! empty($tkt['TKT_ID'])) {
1343
+				$TKT = EE_Registry::instance()
1344
+								  ->load_model('Ticket', [$evtobj->get_timezone()])
1345
+								  ->get_one_by_ID($tkt['TKT_ID']);
1346
+				if ($TKT instanceof EE_Ticket) {
1347
+					$ticket_sold = $TKT->count_related(
1348
+						'Registration',
1349
+						[
1350
+								[
1351
+									'STS_ID' => [
1352
+										'NOT IN',
1353
+										[EEM_Registration::status_id_incomplete],
1354
+									],
1355
+								],
1356
+							]
1357
+					) > 0;
1358
+					// let's just check the total price for the existing ticket and determine if it matches the new
1359
+					// total price.  if they are different then we create a new ticket (if tickets sold)
1360
+					// if they aren't different then we go ahead and modify existing ticket.
1361
+					$create_new_TKT = $ticket_sold && ! $TKT->deleted()
1362
+									  && EEH_Money::compare_floats(
1363
+										  $ticket_price,
1364
+										  $TKT->get('TKT_price'),
1365
+										  '!=='
1366
+									  );
1367
+					$TKT->set_date_format($incoming_date_formats[0]);
1368
+					$TKT->set_time_format($incoming_date_formats[1]);
1369
+					// set new values
1370
+					foreach ($TKT_values as $field => $value) {
1371
+						if ($field === 'TKT_qty') {
1372
+							$TKT->set_qty($value);
1373
+						} else {
1374
+							$TKT->set($field, $value);
1375
+						}
1376
+					}
1377
+					// if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
1378
+					if ($create_new_TKT) {
1379
+						// archive the old ticket first
1380
+						$TKT->set('TKT_deleted', 1);
1381
+						$TKT->save();
1382
+						// make sure this ticket is still recorded in our saved_tkts so we don't run it through the regular trash routine.
1383
+						$saved_tickets[ $TKT->ID() ] = $TKT;
1384
+						// create new ticket that's a copy of the existing except a new id of course (and not archived) AND has the new TKT_price associated with it.
1385
+						$TKT = clone $TKT;
1386
+						$TKT->set('TKT_ID', 0);
1387
+						$TKT->set('TKT_deleted', 0);
1388
+						$TKT->set('TKT_price', $ticket_price);
1389
+						$TKT->set('TKT_sold', 0);
1390
+						// now we need to make sure that $new prices are created as well and attached to new ticket.
1391
+						$update_prices = true;
1392
+					}
1393
+					// make sure price is set if it hasn't been already
1394
+					$TKT->set('TKT_price', $ticket_price);
1395
+				}
1396
+			} else {
1397
+				// no TKT_id so a new TKT
1398
+				$TKT_values['TKT_price'] = $ticket_price;
1399
+				$TKT = EE_Registry::instance()->load_class('Ticket', [$TKT_values], false, false);
1400
+				if ($TKT instanceof EE_Ticket) {
1401
+					// need to reset values to properly account for the date formats
1402
+					$TKT->set_date_format($incoming_date_formats[0]);
1403
+					$TKT->set_time_format($incoming_date_formats[1]);
1404
+					$TKT->set_timezone($evtobj->get_timezone());
1405
+					// set new values
1406
+					foreach ($TKT_values as $field => $value) {
1407
+						if ($field === 'TKT_qty') {
1408
+							$TKT->set_qty($value);
1409
+						} else {
1410
+							$TKT->set($field, $value);
1411
+						}
1412
+					}
1413
+					$update_prices = true;
1414
+				}
1415
+			}
1416
+			// cap ticket qty by datetime reg limits
1417
+			$TKT->set_qty(min($TKT->qty(), $TKT->qty('reg_limit')));
1418
+			// update ticket.
1419
+			$TKT->save();
1420
+			// before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
1421
+			if ($TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date')) {
1422
+				$TKT->set('TKT_end_date', $TKT->get('TKT_start_date'));
1423
+				$TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
1424
+				$TKT->save();
1425
+			}
1426
+			// initially let's add the ticket to the dtt
1427
+			$saved_dtt->_add_relation_to($TKT, 'Ticket');
1428
+			$saved_tickets[ $TKT->ID() ] = $TKT;
1429
+			// add prices to ticket
1430
+			$this->_add_prices_to_ticket($data['edit_prices'][ $row ], $TKT, $update_prices);
1431
+		}
1432
+		// however now we need to handle permanently deleting tickets via the ui.  Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.  However, it does allow for deleting tickets that have no tickets sold, in which case we want to get rid of permanently because there is no need to save in db.
1433
+		$old_tickets = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
1434
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
1435
+		foreach ($tickets_removed as $id) {
1436
+			$id = absint($id);
1437
+			// get the ticket for this id
1438
+			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
1439
+			// need to get all the related datetimes on this ticket and remove from every single one of them (remember this process can ONLY kick off if there are NO tkts_sold)
1440
+			$dtts = $tkt_to_remove->get_many_related('Datetime');
1441
+			foreach ($dtts as $dtt) {
1442
+				$tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
1443
+			}
1444
+			// need to do the same for prices (except these prices can also be deleted because again, tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
1445
+			$tkt_to_remove->delete_related_permanently('Price');
1446
+			// finally let's delete this ticket (which should not be blocked at this point b/c we've removed all our relationships)
1447
+			$tkt_to_remove->delete_permanently();
1448
+		}
1449
+		return [$saved_dtt, $saved_tickets];
1450
+	}
1451
+
1452
+
1453
+	/**
1454
+	 * This attaches a list of given prices to a ticket.
1455
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
1456
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
1457
+	 * price info and prices are automatically "archived" via the ticket.
1458
+	 *
1459
+	 * @access  private
1460
+	 * @param array     $prices     Array of prices from the form.
1461
+	 * @param EE_Ticket $ticket     EE_Ticket object that prices are being attached to.
1462
+	 * @param bool      $new_prices Whether attach existing incoming prices or create new ones.
1463
+	 * @return  void
1464
+	 * @throws EE_Error
1465
+	 * @throws InvalidArgumentException
1466
+	 * @throws InvalidDataTypeException
1467
+	 * @throws InvalidInterfaceException
1468
+	 * @throws ReflectionException
1469
+	 */
1470
+	private function _add_prices_to_ticket($prices, EE_Ticket $ticket, $new_prices = false)
1471
+	{
1472
+		foreach ($prices as $row => $prc) {
1473
+			$PRC_values = [
1474
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
1475
+				'PRT_ID'         => ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null,
1476
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
1477
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
1478
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
1479
+				'PRC_is_default' => 0, // make sure prices are NOT set as default from this context
1480
+				'PRC_order'      => $row,
1481
+			];
1482
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
1483
+				$PRC_values['PRC_ID'] = 0;
1484
+				$PRC = EE_Registry::instance()->load_class('Price', [$PRC_values], false, false);
1485
+			} else {
1486
+				$PRC = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
1487
+				// update this price with new values
1488
+				foreach ($PRC_values as $field => $newprc) {
1489
+					$PRC->set($field, $newprc);
1490
+				}
1491
+				$PRC->save();
1492
+			}
1493
+			$ticket->_add_relation_to($PRC, 'Price');
1494
+		}
1495
+	}
1496
+
1497
+
1498
+	/**
1499
+	 * Add in our autosave ajax handlers
1500
+	 *
1501
+	 */
1502
+	protected function _ee_autosave_create_new()
1503
+	{
1504
+	}
1505
+
1506
+
1507
+	/**
1508
+	 * More autosave handlers.
1509
+	 */
1510
+	protected function _ee_autosave_edit()
1511
+	{
1512
+	}
1513
+
1514
+
1515
+	/**
1516
+	 *    _generate_publish_box_extra_content
1517
+	 *
1518
+	 * @throws DomainException
1519
+	 * @throws EE_Error
1520
+	 * @throws InvalidArgumentException
1521
+	 * @throws InvalidDataTypeException
1522
+	 * @throws InvalidInterfaceException
1523
+	 * @throws ReflectionException
1524
+	 */
1525
+	private function _generate_publish_box_extra_content()
1526
+	{
1527
+		// load formatter helper
1528
+		// args for getting related registrations
1529
+		$approved_query_args = [
1530
+			[
1531
+				'REG_deleted' => 0,
1532
+				'STS_ID'      => EEM_Registration::status_id_approved,
1533
+			],
1534
+		];
1535
+		$not_approved_query_args = [
1536
+			[
1537
+				'REG_deleted' => 0,
1538
+				'STS_ID'      => EEM_Registration::status_id_not_approved,
1539
+			],
1540
+		];
1541
+		$pending_payment_query_args = [
1542
+			[
1543
+				'REG_deleted' => 0,
1544
+				'STS_ID'      => EEM_Registration::status_id_pending_payment,
1545
+			],
1546
+		];
1547
+		// publish box
1548
+		$publish_box_extra_args = [
1549
+			'view_approved_reg_url'        => add_query_arg(
1550
+				[
1551
+					'action'      => 'default',
1552
+					'event_id'    => $this->_cpt_model_obj->ID(),
1553
+					'_reg_status' => EEM_Registration::status_id_approved,
1554
+				],
1555
+				REG_ADMIN_URL
1556
+			),
1557
+			'view_not_approved_reg_url'    => add_query_arg(
1558
+				[
1559
+					'action'      => 'default',
1560
+					'event_id'    => $this->_cpt_model_obj->ID(),
1561
+					'_reg_status' => EEM_Registration::status_id_not_approved,
1562
+				],
1563
+				REG_ADMIN_URL
1564
+			),
1565
+			'view_pending_payment_reg_url' => add_query_arg(
1566
+				[
1567
+					'action'      => 'default',
1568
+					'event_id'    => $this->_cpt_model_obj->ID(),
1569
+					'_reg_status' => EEM_Registration::status_id_pending_payment,
1570
+				],
1571
+				REG_ADMIN_URL
1572
+			),
1573
+			'approved_regs'                => $this->_cpt_model_obj->count_related(
1574
+				'Registration',
1575
+				$approved_query_args
1576
+			),
1577
+			'not_approved_regs'            => $this->_cpt_model_obj->count_related(
1578
+				'Registration',
1579
+				$not_approved_query_args
1580
+			),
1581
+			'pending_payment_regs'         => $this->_cpt_model_obj->count_related(
1582
+				'Registration',
1583
+				$pending_payment_query_args
1584
+			),
1585
+			'misc_pub_section_class'       => apply_filters(
1586
+				'FHEE_Events_Admin_Page___generate_publish_box_extra_content__misc_pub_section_class',
1587
+				'misc-pub-section'
1588
+			),
1589
+		];
1590
+		ob_start();
1591
+		do_action(
1592
+			'AHEE__Events_Admin_Page___generate_publish_box_extra_content__event_editor_overview_add',
1593
+			$this->_cpt_model_obj
1594
+		);
1595
+		$publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1596
+		// load template
1597
+		EEH_Template::display_template(
1598
+			EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1599
+			$publish_box_extra_args
1600
+		);
1601
+	}
1602
+
1603
+
1604
+	/**
1605
+	 * @return EE_Event
1606
+	 */
1607
+	public function get_event_object()
1608
+	{
1609
+		return $this->_cpt_model_obj;
1610
+	}
1611
+
1612
+
1613
+
1614
+
1615
+	/** METABOXES * */
1616
+	/**
1617
+	 * _register_event_editor_meta_boxes
1618
+	 * add all metaboxes related to the event_editor
1619
+	 *
1620
+	 * @return void
1621
+	 * @throws EE_Error
1622
+	 * @throws InvalidArgumentException
1623
+	 * @throws InvalidDataTypeException
1624
+	 * @throws InvalidInterfaceException
1625
+	 * @throws ReflectionException
1626
+	 */
1627
+	protected function _register_event_editor_meta_boxes()
1628
+	{
1629
+		$this->verify_cpt_object();
1630
+		$use_advanced_editor = $this->admin_config->useAdvancedEditor();
1631
+		/** @var FeatureFlags $flags */
1632
+		$flags = $this->loader->getShared('EventEspresso\core\domain\services\capabilities\FeatureFlags');
1633
+		// check if the new EDTR reg options meta box is being used, and if so, don't load the legacy version
1634
+		if (! $use_advanced_editor || ! $flags->featureAllowed('use_reg_options_meta_box')) {
1635
+			add_meta_box(
1636
+				'espresso_event_editor_event_options',
1637
+				esc_html__('Event Registration Options', 'event_espresso'),
1638
+				[$this, 'registration_options_meta_box'],
1639
+				$this->page_slug,
1640
+				'side'
1641
+			);
1642
+		}
1643
+		if (! $use_advanced_editor) {
1644
+			add_meta_box(
1645
+				'espresso_event_editor_tickets',
1646
+				esc_html__('Event Datetime & Ticket', 'event_espresso'),
1647
+				[$this, 'ticket_metabox'],
1648
+				$this->page_slug,
1649
+				'normal',
1650
+				'high'
1651
+			);
1652
+		} else {
1653
+			if ($flags->featureAllowed('use_reg_options_meta_box')) {
1654
+				add_action(
1655
+					'add_meta_boxes_espresso_events',
1656
+					function () {
1657
+						global $current_screen;
1658
+						remove_meta_box('authordiv', $current_screen, 'normal');
1659
+					},
1660
+					99
1661
+				);
1662
+			}
1663
+		}
1664
+		// NOTE: if you're looking for other metaboxes in here,
1665
+		// where a metabox has a related management page in the admin
1666
+		// you will find it setup in the related management page's "_Hooks" file.
1667
+		// i.e. messages metabox is found in "espresso_events_Messages_Hooks.class.php".
1668
+	}
1669
+
1670
+
1671
+	/**
1672
+	 * @throws DomainException
1673
+	 * @throws EE_Error
1674
+	 * @throws InvalidArgumentException
1675
+	 * @throws InvalidDataTypeException
1676
+	 * @throws InvalidInterfaceException
1677
+	 * @throws ReflectionException
1678
+	 */
1679
+	public function ticket_metabox()
1680
+	{
1681
+		$existing_datetime_ids = $existing_ticket_ids = [];
1682
+		// defaults for template args
1683
+		$template_args = [
1684
+			'existing_datetime_ids'    => '',
1685
+			'event_datetime_help_link' => '',
1686
+			'ticket_options_help_link' => '',
1687
+			'time'                     => null,
1688
+			'ticket_rows'              => '',
1689
+			'existing_ticket_ids'      => '',
1690
+			'total_ticket_rows'        => 1,
1691
+			'ticket_js_structure'      => '',
1692
+			'trash_icon'               => 'ee-lock-icon',
1693
+			'disabled'                 => '',
1694
+		];
1695
+		$event_id = is_object($this->_cpt_model_obj) ? $this->_cpt_model_obj->ID() : null;
1696
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1697
+		/**
1698
+		 * 1. Start with retrieving Datetimes
1699
+		 * 2. Fore each datetime get related tickets
1700
+		 * 3. For each ticket get related prices
1701
+		 */
1702
+		$times = EE_Registry::instance()->load_model('Datetime')->get_all_event_dates($event_id);
1703
+		/** @type EE_Datetime $first_datetime */
1704
+		$first_datetime = reset($times);
1705
+		// do we get related tickets?
1706
+		if (
1707
+			$first_datetime instanceof EE_Datetime
1708
+			&& $first_datetime->ID() !== 0
1709
+		) {
1710
+			$existing_datetime_ids[] = $first_datetime->get('DTT_ID');
1711
+			$template_args['time'] = $first_datetime;
1712
+			$related_tickets = $first_datetime->tickets(
1713
+				[
1714
+					['OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0]],
1715
+					'default_where_conditions' => 'none',
1716
+				]
1717
+			);
1718
+			if (! empty($related_tickets)) {
1719
+				$template_args['total_ticket_rows'] = count($related_tickets);
1720
+				$row = 0;
1721
+				foreach ($related_tickets as $ticket) {
1722
+					$existing_ticket_ids[] = $ticket->get('TKT_ID');
1723
+					$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket, false, $row);
1724
+					$row++;
1725
+				}
1726
+			} else {
1727
+				$template_args['total_ticket_rows'] = 1;
1728
+				/** @type EE_Ticket $ticket */
1729
+				$ticket = EE_Registry::instance()->load_model('Ticket')->create_default_object();
1730
+				$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket);
1731
+			}
1732
+		} else {
1733
+			$template_args['time'] = $times[0];
1734
+			/** @type EE_Ticket $ticket */
1735
+			$ticket = EE_Registry::instance()->load_model('Ticket')->get_all_default_tickets();
1736
+			$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket[1]);
1737
+			// NOTE: we're just sending the first default row
1738
+			// (decaf can't manage default tickets so this should be sufficient);
1739
+		}
1740
+		$template_args['event_datetime_help_link'] = $this->_get_help_tab_link(
1741
+			'event_editor_event_datetimes_help_tab'
1742
+		);
1743
+		$template_args['ticket_options_help_link'] = $this->_get_help_tab_link('ticket_options_info');
1744
+		$template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1745
+		$template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1746
+		$template_args['ticket_js_structure'] = $this->_get_ticket_row(
1747
+			EE_Registry::instance()->load_model('Ticket')->create_default_object(),
1748
+			true
1749
+		);
1750
+		$template = apply_filters(
1751
+			'FHEE__Events_Admin_Page__ticket_metabox__template',
1752
+			EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1753
+		);
1754
+		EEH_Template::display_template($template, $template_args);
1755
+	}
1756
+
1757
+
1758
+	/**
1759
+	 * Setup an individual ticket form for the decaf event editor page
1760
+	 *
1761
+	 * @access private
1762
+	 * @param EE_Ticket $ticket   the ticket object
1763
+	 * @param boolean   $skeleton whether we're generating a skeleton for js manipulation
1764
+	 * @param int       $row
1765
+	 * @return string generated html for the ticket row.
1766
+	 * @throws DomainException
1767
+	 * @throws EE_Error
1768
+	 * @throws InvalidArgumentException
1769
+	 * @throws InvalidDataTypeException
1770
+	 * @throws InvalidInterfaceException
1771
+	 * @throws ReflectionException
1772
+	 */
1773
+	private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1774
+	{
1775
+		$template_args = [
1776
+			'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1777
+			'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1778
+				: '',
1779
+			'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
1780
+			'TKT_ID'              => $ticket->get('TKT_ID'),
1781
+			'TKT_name'            => $ticket->get('TKT_name'),
1782
+			'TKT_start_date'      => $skeleton ? '' : $ticket->get_date('TKT_start_date', 'Y-m-d h:i a'),
1783
+			'TKT_end_date'        => $skeleton ? '' : $ticket->get_date('TKT_end_date', 'Y-m-d h:i a'),
1784
+			'TKT_is_default'      => $ticket->get('TKT_is_default'),
1785
+			'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1786
+			'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1787
+			'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1788
+			'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1789
+									 && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1790
+				? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1791
+			'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1792
+				: ' disabled=disabled',
1793
+		];
1794
+		$price = $ticket->ID() !== 0
1795
+			? $ticket->get_first_related('Price', ['default_where_conditions' => 'none'])
1796
+			: EE_Registry::instance()->load_model('Price')->create_default_object();
1797
+		$price_args = [
1798
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1799
+			'PRC_amount'            => $price->get('PRC_amount'),
1800
+			'PRT_ID'                => $price->get('PRT_ID'),
1801
+			'PRC_ID'                => $price->get('PRC_ID'),
1802
+			'PRC_is_default'        => $price->get('PRC_is_default'),
1803
+		];
1804
+		// make sure we have default start and end dates if skeleton
1805
+		// handle rows that should NOT be empty
1806
+		if (empty($template_args['TKT_start_date'])) {
1807
+			// if empty then the start date will be now.
1808
+			$template_args['TKT_start_date'] = date('Y-m-d h:i a', current_time('timestamp'));
1809
+		}
1810
+		if (empty($template_args['TKT_end_date'])) {
1811
+			// get the earliest datetime (if present);
1812
+			$earliest_dtt = $this->_cpt_model_obj->ID() > 0
1813
+				? $this->_cpt_model_obj->get_first_related(
1814
+					'Datetime',
1815
+					['order_by' => ['DTT_EVT_start' => 'ASC']]
1816
+				)
1817
+				: null;
1818
+			if (! empty($earliest_dtt)) {
1819
+				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a');
1820
+			} else {
1821
+				$template_args['TKT_end_date'] = date(
1822
+					'Y-m-d h:i a',
1823
+					mktime(0, 0, 0, date('m'), date('d') + 7, date('Y'))
1824
+				);
1825
+			}
1826
+		}
1827
+		$template_args = array_merge($template_args, $price_args);
1828
+		$template = apply_filters(
1829
+			'FHEE__Events_Admin_Page__get_ticket_row__template',
1830
+			EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1831
+			$ticket
1832
+		);
1833
+		return EEH_Template::display_template($template, $template_args, true);
1834
+	}
1835
+
1836
+
1837
+	/**
1838
+	 * @throws DomainException
1839
+	 * @throws EE_Error
1840
+	 */
1841
+	public function registration_options_meta_box()
1842
+	{
1843
+		$yes_no_values = [
1844
+			['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
1845
+			['id' => false, 'text' => esc_html__('No', 'event_espresso')],
1846
+		];
1847
+		$default_reg_status_values = EEM_Registration::reg_status_array(
1848
+			[
1849
+				EEM_Registration::status_id_cancelled,
1850
+				EEM_Registration::status_id_declined,
1851
+				EEM_Registration::status_id_incomplete,
1852
+			],
1853
+			true
1854
+		);
1855
+		// $template_args['is_active_select'] = EEH_Form_Fields::select_input('is_active', $yes_no_values, $this->_cpt_model_obj->is_active());
1856
+		$template_args['_event'] = $this->_cpt_model_obj;
1857
+		$template_args['active_status'] = $this->_cpt_model_obj->pretty_active_status(false);
1858
+		$template_args['additional_limit'] = $this->_cpt_model_obj->additional_limit();
1859
+		$template_args['default_registration_status'] = EEH_Form_Fields::select_input(
1860
+			'default_reg_status',
1861
+			$default_reg_status_values,
1862
+			$this->_cpt_model_obj->default_registration_status()
1863
+		);
1864
+		$template_args['display_description'] = EEH_Form_Fields::select_input(
1865
+			'display_desc',
1866
+			$yes_no_values,
1867
+			$this->_cpt_model_obj->display_description()
1868
+		);
1869
+		$template_args['display_ticket_selector'] = EEH_Form_Fields::select_input(
1870
+			'display_ticket_selector',
1871
+			$yes_no_values,
1872
+			$this->_cpt_model_obj->display_ticket_selector(),
1873
+			'',
1874
+			'',
1875
+			false
1876
+		);
1877
+		$template_args['additional_registration_options'] = apply_filters(
1878
+			'FHEE__Events_Admin_Page__registration_options_meta_box__additional_registration_options',
1879
+			'',
1880
+			$template_args,
1881
+			$yes_no_values,
1882
+			$default_reg_status_values
1883
+		);
1884
+		EEH_Template::display_template(
1885
+			EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1886
+			$template_args
1887
+		);
1888
+	}
1889
+
1890
+
1891
+	/**
1892
+	 * _get_events()
1893
+	 * This method simply returns all the events (for the given _view and paging)
1894
+	 *
1895
+	 * @access public
1896
+	 * @param int  $per_page     count of items per page (20 default);
1897
+	 * @param int  $current_page what is the current page being viewed.
1898
+	 * @param bool $count        if TRUE then we just return a count of ALL events matching the given _view.
1899
+	 *                           If FALSE then we return an array of event objects
1900
+	 *                           that match the given _view and paging parameters.
1901
+	 * @return array an array of event objects.
1902
+	 * @throws EE_Error
1903
+	 * @throws InvalidArgumentException
1904
+	 * @throws InvalidDataTypeException
1905
+	 * @throws InvalidInterfaceException
1906
+	 * @throws ReflectionException
1907
+	 * @throws Exception
1908
+	 * @throws Exception
1909
+	 * @throws Exception
1910
+	 */
1911
+	public function get_events($per_page = 10, $current_page = 1, $count = false)
1912
+	{
1913
+		$EEME = $this->_event_model();
1914
+		$offset = ($current_page - 1) * $per_page;
1915
+		$limit = $count ? null : $offset . ',' . $per_page;
1916
+		$orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'EVT_ID';
1917
+		$order = isset($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
1918
+		if (isset($this->_req_data['month_range'])) {
1919
+			$pieces = explode(' ', $this->_req_data['month_range'], 3);
1920
+			// simulate the FIRST day of the month, that fixes issues for months like February
1921
+			// where PHP doesn't know what to assume for date.
1922
+			// @see https://events.codebasehq.com/projects/event-espresso/tickets/10437
1923
+			$month_r = ! empty($pieces[0]) ? date('m', EEH_DTT_Helper::first_of_month_timestamp($pieces[0])) : '';
1924
+			$year_r = ! empty($pieces[1]) ? $pieces[1] : '';
1925
+		}
1926
+		$where = [];
1927
+		$status = isset($this->_req_data['status']) ? $this->_req_data['status'] : null;
1928
+		// determine what post_status our condition will have for the query.
1929
+		switch ($status) {
1930
+			case 'month':
1931
+			case 'today':
1932
+			case null:
1933
+			case 'all':
1934
+				break;
1935
+			case 'draft':
1936
+				$where['status'] = ['IN', ['draft', 'auto-draft']];
1937
+				break;
1938
+			default:
1939
+				$where['status'] = $status;
1940
+		}
1941
+		// categories?
1942
+		$category = isset($this->_req_data['EVT_CAT']) && $this->_req_data['EVT_CAT'] > 0
1943
+			? $this->_req_data['EVT_CAT'] : null;
1944
+		if (! empty($category)) {
1945
+			$where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1946
+			$where['Term_Taxonomy.term_id'] = $category;
1947
+		}
1948
+		// date where conditions
1949
+		$start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1950
+		if (isset($this->_req_data['month_range']) && $this->_req_data['month_range'] !== '') {
1951
+			$DateTime = new DateTime(
1952
+				$year_r . '-' . $month_r . '-01 00:00:00',
1953
+				new DateTimeZone('UTC')
1954
+			);
1955
+			$start = $DateTime->getTimestamp();
1956
+			// set the datetime to be the end of the month
1957
+			$DateTime->setDate(
1958
+				$year_r,
1959
+				$month_r,
1960
+				$DateTime->format('t')
1961
+			)->setTime(23, 59, 59);
1962
+			$end = $DateTime->getTimestamp();
1963
+			$where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1964
+		} elseif (isset($this->_req_data['status']) && $this->_req_data['status'] === 'today') {
1965
+			$DateTime = new DateTime('now', new DateTimeZone(EEM_Event::instance()->get_timezone()));
1966
+			$start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1967
+			$end = $DateTime->setTime(23, 59, 59)->format(implode(' ', $start_formats));
1968
+			$where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1969
+		} elseif (isset($this->_req_data['status']) && $this->_req_data['status'] === 'month') {
1970
+			$now = date('Y-m-01');
1971
+			$DateTime = new DateTime($now, new DateTimeZone(EEM_Event::instance()->get_timezone()));
1972
+			$start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1973
+			$end = $DateTime->setDate(date('Y'), date('m'), $DateTime->format('t'))
1974
+							->setTime(23, 59, 59)
1975
+							->format(implode(' ', $start_formats));
1976
+			$where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1977
+		}
1978
+		if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1979
+			$where['EVT_wp_user'] = get_current_user_id();
1980
+		} elseif (
1981
+			! isset($where['status'])
1982
+				  && ! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')
1983
+		) {
1984
+			$where['OR'] = [
1985
+				'status*restrict_private' => ['!=', 'private'],
1986
+				'AND'                     => [
1987
+					'status*inclusive' => ['=', 'private'],
1988
+					'EVT_wp_user'      => get_current_user_id(),
1989
+				],
1990
+			];
1991
+		}
1992
+
1993
+		if (
1994
+			isset($this->_req_data['EVT_wp_user'])
1995
+			&& (int) $this->_req_data['EVT_wp_user'] !== (int) get_current_user_id()
1996
+			&& EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')
1997
+		) {
1998
+			$where['EVT_wp_user'] = $this->_req_data['EVT_wp_user'];
1999
+		}
2000
+		// search query handling
2001
+		if (isset($this->_req_data['s'])) {
2002
+			$search_string = '%' . $this->_req_data['s'] . '%';
2003
+			$where['OR'] = [
2004
+				'EVT_name'       => ['LIKE', $search_string],
2005
+				'EVT_desc'       => ['LIKE', $search_string],
2006
+				'EVT_short_desc' => ['LIKE', $search_string],
2007
+			];
2008
+		}
2009
+		// filter events by venue.
2010
+		if (isset($this->_req_data['venue']) && ! empty($this->_req_data['venue'])) {
2011
+			$where['Venue.VNU_ID'] = absint($this->_req_data['venue']);
2012
+		}
2013
+		$where = apply_filters('FHEE__Events_Admin_Page__get_events__where', $where, $this->_req_data);
2014
+		$query_params = apply_filters(
2015
+			'FHEE__Events_Admin_Page__get_events__query_params',
2016
+			[
2017
+				$where,
2018
+				'limit'    => $limit,
2019
+				'order_by' => $orderby,
2020
+				'order'    => $order,
2021
+				'group_by' => 'EVT_ID',
2022
+			],
2023
+			$this->_req_data
2024
+		);
2025
+
2026
+		// let's first check if we have special requests coming in.
2027
+		if (isset($this->_req_data['active_status'])) {
2028
+			switch ($this->_req_data['active_status']) {
2029
+				case 'upcoming':
2030
+					return $EEME->get_upcoming_events($query_params, $count);
2031
+					break;
2032
+				case 'expired':
2033
+					return $EEME->get_expired_events($query_params, $count);
2034
+					break;
2035
+				case 'active':
2036
+					return $EEME->get_active_events($query_params, $count);
2037
+					break;
2038
+				case 'inactive':
2039
+					return $EEME->get_inactive_events($query_params, $count);
2040
+					break;
2041
+			}
2042
+		}
2043
+
2044
+		$events = $count ? $EEME->count([$where], 'EVT_ID', true) : $EEME->get_all($query_params);
2045
+		return $events;
2046
+	}
2047
+
2048
+
2049
+	/**
2050
+	 * handling for WordPress CPT actions (trash, restore, delete)
2051
+	 *
2052
+	 * @param string $post_id
2053
+	 * @throws EE_Error
2054
+	 * @throws InvalidArgumentException
2055
+	 * @throws InvalidDataTypeException
2056
+	 * @throws InvalidInterfaceException
2057
+	 * @throws ReflectionException
2058
+	 */
2059
+	public function trash_cpt_item($post_id)
2060
+	{
2061
+		$this->_req_data['EVT_ID'] = $post_id;
2062
+		$this->_trash_or_restore_event('trash', false);
2063
+	}
2064
+
2065
+
2066
+	/**
2067
+	 * @param string $post_id
2068
+	 * @throws EE_Error
2069
+	 * @throws InvalidArgumentException
2070
+	 * @throws InvalidDataTypeException
2071
+	 * @throws InvalidInterfaceException
2072
+	 * @throws ReflectionException
2073
+	 */
2074
+	public function restore_cpt_item($post_id)
2075
+	{
2076
+		$this->_req_data['EVT_ID'] = $post_id;
2077
+		$this->_trash_or_restore_event('draft', false);
2078
+	}
2079
+
2080
+
2081
+	/**
2082
+	 * @param string $post_id
2083
+	 * @throws EE_Error
2084
+	 * @throws InvalidArgumentException
2085
+	 * @throws InvalidDataTypeException
2086
+	 * @throws InvalidInterfaceException
2087
+	 * @throws ReflectionException
2088
+	 */
2089
+	public function delete_cpt_item($post_id)
2090
+	{
2091
+		throw new EE_Error(esc_html__('Please contact Event Espresso support with the details of the steps taken to produce this error.', 'event_espresso'));
2092
+		$this->_req_data['EVT_ID'] = $post_id;
2093
+		$this->_delete_event();
2094
+	}
2095
+
2096
+
2097
+	/**
2098
+	 * _trash_or_restore_event
2099
+	 *
2100
+	 * @access protected
2101
+	 * @param string $event_status
2102
+	 * @param bool   $redirect_after
2103
+	 * @throws EE_Error
2104
+	 * @throws InvalidArgumentException
2105
+	 * @throws InvalidDataTypeException
2106
+	 * @throws InvalidInterfaceException
2107
+	 * @throws ReflectionException
2108
+	 */
2109
+	protected function _trash_or_restore_event($event_status = 'trash', $redirect_after = true)
2110
+	{
2111
+		// determine the event id and set to array.
2112
+		$EVT_ID = isset($this->_req_data['EVT_ID']) ? absint($this->_req_data['EVT_ID']) : false;
2113
+		// loop thru events
2114
+		if ($EVT_ID) {
2115
+			// clean status
2116
+			$event_status = sanitize_key($event_status);
2117
+			// grab status
2118
+			if (! empty($event_status)) {
2119
+				$success = $this->_change_event_status($EVT_ID, $event_status);
2120
+			} else {
2121
+				$success = false;
2122
+				$msg = esc_html__(
2123
+					'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2124
+					'event_espresso'
2125
+				);
2126
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2127
+			}
2128
+		} else {
2129
+			$success = false;
2130
+			$msg = esc_html__(
2131
+				'An error occurred. The event could not be moved to the trash because a valid event ID was not not supplied.',
2132
+				'event_espresso'
2133
+			);
2134
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2135
+		}
2136
+		$action = $event_status === 'trash' ? 'moved to the trash' : 'restored from the trash';
2137
+		if ($redirect_after) {
2138
+			$this->_redirect_after_action($success, 'Event', $action, ['action' => 'default']);
2139
+		}
2140
+	}
2141
+
2142
+
2143
+	/**
2144
+	 * _trash_or_restore_events
2145
+	 *
2146
+	 * @access protected
2147
+	 * @param string $event_status
2148
+	 * @return void
2149
+	 * @throws EE_Error
2150
+	 * @throws InvalidArgumentException
2151
+	 * @throws InvalidDataTypeException
2152
+	 * @throws InvalidInterfaceException
2153
+	 * @throws ReflectionException
2154
+	 */
2155
+	protected function _trash_or_restore_events($event_status = 'trash')
2156
+	{
2157
+		// clean status
2158
+		$event_status = sanitize_key($event_status);
2159
+		// grab status
2160
+		if (! empty($event_status)) {
2161
+			$success = true;
2162
+			// determine the event id and set to array.
2163
+			$EVT_IDs = isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : [];
2164
+			// loop thru events
2165
+			foreach ($EVT_IDs as $EVT_ID) {
2166
+				if ($EVT_ID = absint($EVT_ID)) {
2167
+					$results = $this->_change_event_status($EVT_ID, $event_status);
2168
+					$success = $results !== false ? $success : false;
2169
+				} else {
2170
+					$msg = sprintf(
2171
+						esc_html__(
2172
+							'An error occurred. Event #%d could not be moved to the trash because a valid event ID was not not supplied.',
2173
+							'event_espresso'
2174
+						),
2175
+						$EVT_ID
2176
+					);
2177
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2178
+					$success = false;
2179
+				}
2180
+			}
2181
+		} else {
2182
+			$success = false;
2183
+			$msg = esc_html__(
2184
+				'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2185
+				'event_espresso'
2186
+			);
2187
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2188
+		}
2189
+		// in order to force a pluralized result message we need to send back a success status greater than 1
2190
+		$success = $success ? 2 : false;
2191
+		$action = $event_status === 'trash' ? 'moved to the trash' : 'restored from the trash';
2192
+		$this->_redirect_after_action($success, 'Events', $action, ['action' => 'default']);
2193
+	}
2194
+
2195
+
2196
+	/**
2197
+	 * _trash_or_restore_events
2198
+	 *
2199
+	 * @access  private
2200
+	 * @param int    $EVT_ID
2201
+	 * @param string $event_status
2202
+	 * @return bool
2203
+	 * @throws EE_Error
2204
+	 * @throws InvalidArgumentException
2205
+	 * @throws InvalidDataTypeException
2206
+	 * @throws InvalidInterfaceException
2207
+	 * @throws ReflectionException
2208
+	 */
2209
+	private function _change_event_status($EVT_ID = 0, $event_status = '')
2210
+	{
2211
+		// grab event id
2212
+		if (! $EVT_ID) {
2213
+			$msg = esc_html__(
2214
+				'An error occurred. No Event ID or an invalid Event ID was received.',
2215
+				'event_espresso'
2216
+			);
2217
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2218
+			return false;
2219
+		}
2220
+		$this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2221
+		// clean status
2222
+		$event_status = sanitize_key($event_status);
2223
+		// grab status
2224
+		if (empty($event_status)) {
2225
+			$msg = esc_html__(
2226
+				'An error occurred. No Event Status or an invalid Event Status was received.',
2227
+				'event_espresso'
2228
+			);
2229
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2230
+			return false;
2231
+		}
2232
+		// was event trashed or restored ?
2233
+		switch ($event_status) {
2234
+			case 'draft':
2235
+				$action = 'restored from the trash';
2236
+				$hook = 'AHEE_event_restored_from_trash';
2237
+				break;
2238
+			case 'trash':
2239
+				$action = 'moved to the trash';
2240
+				$hook = 'AHEE_event_moved_to_trash';
2241
+				break;
2242
+			default:
2243
+				$action = 'updated';
2244
+				$hook = false;
2245
+		}
2246
+		// use class to change status
2247
+		$this->_cpt_model_obj->set_status($event_status);
2248
+		$success = $this->_cpt_model_obj->save();
2249
+		if ($success === false) {
2250
+			$msg = sprintf(esc_html__('An error occurred. The event could not be %s.', 'event_espresso'), $action);
2251
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2252
+			return false;
2253
+		}
2254
+		if ($hook) {
2255
+			do_action($hook);
2256
+		}
2257
+		return true;
2258
+	}
2259
+
2260
+
2261
+	/**
2262
+	 * _delete_event
2263
+	 *
2264
+	 * @access protected
2265
+	 * @param bool $redirect_after
2266
+	 * @throws EE_Error
2267
+	 * @throws InvalidArgumentException
2268
+	 * @throws InvalidDataTypeException
2269
+	 * @throws InvalidInterfaceException
2270
+	 * @throws ReflectionException
2271
+	 */
2272
+	protected function _delete_event()
2273
+	{
2274
+		$this->generateDeletionPreview(isset($this->_req_data['EVT_ID']) ? $this->_req_data['EVT_ID'] : []);
2275
+	}
2276
+
2277
+	/**
2278
+	 * Gets the tree traversal batch persister.
2279
+	 * @since 4.10.12.p
2280
+	 * @return NodeGroupDao
2281
+	 * @throws InvalidArgumentException
2282
+	 * @throws InvalidDataTypeException
2283
+	 * @throws InvalidInterfaceException
2284
+	 */
2285
+	protected function getModelObjNodeGroupPersister()
2286
+	{
2287
+		if (! $this->model_obj_node_group_persister instanceof NodeGroupDao) {
2288
+			$this->model_obj_node_group_persister = $this->getLoader()->load('\EventEspresso\core\services\orm\tree_traversal\NodeGroupDao');
2289
+		}
2290
+		return $this->model_obj_node_group_persister;
2291
+	}
2292
+
2293
+	/**
2294
+	 * _delete_events
2295
+	 *
2296
+	 * @access protected
2297
+	 * @return void
2298
+	 * @throws EE_Error
2299
+	 * @throws InvalidArgumentException
2300
+	 * @throws InvalidDataTypeException
2301
+	 * @throws InvalidInterfaceException
2302
+	 * @throws ReflectionException
2303
+	 */
2304
+	protected function _delete_events()
2305
+	{
2306
+		$this->generateDeletionPreview(isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : []);
2307
+	}
2308
+
2309
+	protected function generateDeletionPreview($event_ids)
2310
+	{
2311
+		$event_ids = (array) $event_ids;
2312
+		// Set a code we can use to reference this deletion task in the batch jobs and preview page.
2313
+		$deletion_job_code = $this->getModelObjNodeGroupPersister()->generateGroupCode();
2314
+		$return_url = EE_Admin_Page::add_query_args_and_nonce(
2315
+			[
2316
+				'action' => 'preview_deletion',
2317
+				'deletion_job_code' => $deletion_job_code,
2318
+			],
2319
+			$this->_admin_base_url
2320
+		);
2321
+		$event_ids = array_map(
2322
+			'intval',
2323
+			$event_ids
2324
+		);
2325
+
2326
+		EEH_URL::safeRedirectAndExit(
2327
+			EE_Admin_Page::add_query_args_and_nonce(
2328
+				[
2329
+					'page'              => 'espresso_batch',
2330
+					'batch'             => EED_Batch::batch_job,
2331
+					'EVT_IDs'           => $event_ids,
2332
+					'deletion_job_code' => $deletion_job_code,
2333
+					'job_handler'       => urlencode('EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'),
2334
+					'return_url'        => urlencode($return_url),
2335
+				],
2336
+				admin_url()
2337
+			)
2338
+		);
2339
+	}
2340
+
2341
+	/**
2342
+	 * Checks for a POST submission
2343
+	 * @since 4.10.12.p
2344
+	 */
2345
+	protected function confirmDeletion()
2346
+	{
2347
+		$deletion_redirect_logic = $this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion');
2348
+		$deletion_redirect_logic->handle($this->get_request_data(), $this->admin_base_url());
2349
+	}
2350
+
2351
+	/**
2352
+	 * A page for users to preview what exactly will be deleted, and confirm they want to delete it.
2353
+	 * @since 4.10.12.p
2354
+	 * @throws EE_Error
2355
+	 */
2356
+	protected function previewDeletion()
2357
+	{
2358
+		$preview_deletion_logic = $this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\PreviewDeletion');
2359
+		$this->set_template_args($preview_deletion_logic->handle($this->get_request_data(), $this->admin_base_url()));
2360
+		$this->display_admin_page_with_no_sidebar();
2361
+	}
2362
+
2363
+	/**
2364
+	 * get total number of events
2365
+	 *
2366
+	 * @access public
2367
+	 * @return int
2368
+	 * @throws EE_Error
2369
+	 * @throws InvalidArgumentException
2370
+	 * @throws InvalidDataTypeException
2371
+	 * @throws InvalidInterfaceException
2372
+	 */
2373
+	public function total_events()
2374
+	{
2375
+		$count = EEM_Event::instance()->count(['caps' => 'read_admin'], 'EVT_ID', true);
2376
+		return $count;
2377
+	}
2378
+
2379
+
2380
+	/**
2381
+	 * get total number of draft events
2382
+	 *
2383
+	 * @access public
2384
+	 * @return int
2385
+	 * @throws EE_Error
2386
+	 * @throws InvalidArgumentException
2387
+	 * @throws InvalidDataTypeException
2388
+	 * @throws InvalidInterfaceException
2389
+	 */
2390
+	public function total_events_draft()
2391
+	{
2392
+		$where = [
2393
+			'status' => ['IN', ['draft', 'auto-draft']],
2394
+		];
2395
+		$count = EEM_Event::instance()->count([$where, 'caps' => 'read_admin'], 'EVT_ID', true);
2396
+		return $count;
2397
+	}
2398
+
2399
+
2400
+	/**
2401
+	 * get total number of trashed events
2402
+	 *
2403
+	 * @access public
2404
+	 * @return int
2405
+	 * @throws EE_Error
2406
+	 * @throws InvalidArgumentException
2407
+	 * @throws InvalidDataTypeException
2408
+	 * @throws InvalidInterfaceException
2409
+	 */
2410
+	public function total_trashed_events()
2411
+	{
2412
+		$where = [
2413
+			'status' => 'trash',
2414
+		];
2415
+		$count = EEM_Event::instance()->count([$where, 'caps' => 'read_admin'], 'EVT_ID', true);
2416
+		return $count;
2417
+	}
2418
+
2419
+
2420
+	/**
2421
+	 *    _default_event_settings
2422
+	 *    This generates the Default Settings Tab
2423
+	 *
2424
+	 * @return void
2425
+	 * @throws DomainException
2426
+	 * @throws EE_Error
2427
+	 * @throws InvalidArgumentException
2428
+	 * @throws InvalidDataTypeException
2429
+	 * @throws InvalidInterfaceException
2430
+	 */
2431
+	protected function _default_event_settings()
2432
+	{
2433
+		$this->_set_add_edit_form_tags('update_default_event_settings');
2434
+		$this->_set_publish_post_box_vars(null, false, false, null, false);
2435
+		$this->_template_args['admin_page_content'] = $this->_default_event_settings_form()->get_html();
2436
+		$this->display_admin_page_with_sidebar();
2437
+	}
2438
+
2439
+
2440
+	/**
2441
+	 * Return the form for event settings.
2442
+	 *
2443
+	 * @return EE_Form_Section_Proper
2444
+	 * @throws EE_Error
2445
+	 */
2446
+	protected function _default_event_settings_form()
2447
+	{
2448
+		$registration_config = EE_Registry::instance()->CFG->registration;
2449
+		$registration_stati_for_selection = EEM_Registration::reg_status_array(
2450
+		// exclude
2451
+			[
2452
+				EEM_Registration::status_id_cancelled,
2453
+				EEM_Registration::status_id_declined,
2454
+				EEM_Registration::status_id_incomplete,
2455
+				EEM_Registration::status_id_wait_list,
2456
+			],
2457
+			true
2458
+		);
2459
+		return new EE_Form_Section_Proper(
2460
+			[
2461
+				'name'            => 'update_default_event_settings',
2462
+				'html_id'         => 'update_default_event_settings',
2463
+				'html_class'      => 'form-table',
2464
+				'layout_strategy' => new EE_Admin_Two_Column_Layout(),
2465
+				'subsections'     => apply_filters(
2466
+					'FHEE__Events_Admin_Page___default_event_settings_form__form_subsections',
2467
+					[
2468
+						'default_reg_status'  => new EE_Select_Input(
2469
+							$registration_stati_for_selection,
2470
+							[
2471
+								'default'         => isset($registration_config->default_STS_ID)
2472
+													 && array_key_exists(
2473
+														 $registration_config->default_STS_ID,
2474
+														 $registration_stati_for_selection
2475
+													 )
2476
+									? sanitize_text_field($registration_config->default_STS_ID)
2477
+									: EEM_Registration::status_id_pending_payment,
2478
+								'html_label_text' => esc_html__('Default Registration Status', 'event_espresso')
2479
+													 . EEH_Template::get_help_tab_link(
2480
+														 'default_settings_status_help_tab'
2481
+													 ),
2482
+								'html_help_text'  => esc_html__(
2483
+									'This setting allows you to preselect what the default registration status setting is when creating an event.  Note that changing this setting does NOT retroactively apply it to existing events.',
2484
+									'event_espresso'
2485
+								),
2486
+							]
2487
+						),
2488
+						'default_max_tickets' => new EE_Integer_Input(
2489
+							[
2490
+								'default'         => isset($registration_config->default_maximum_number_of_tickets)
2491
+									? $registration_config->default_maximum_number_of_tickets
2492
+									: EEM_Event::get_default_additional_limit(),
2493
+								'html_label_text' => esc_html__(
2494
+									'Default Maximum Tickets Allowed Per Order:',
2495
+									'event_espresso'
2496
+								)
2497
+													 . EEH_Template::get_help_tab_link(
2498
+														 'default_maximum_tickets_help_tab"'
2499
+													 ),
2500
+								'html_help_text'  => esc_html__(
2501
+									'This setting allows you to indicate what will be the default for the maximum number of tickets per order when creating new events.',
2502
+									'event_espresso'
2503
+								),
2504
+							]
2505
+						),
2506
+					]
2507
+				),
2508
+			]
2509
+		);
2510
+	}
2511
+
2512
+
2513
+	/**
2514
+	 * @return void
2515
+	 * @throws EE_Error
2516
+	 * @throws InvalidArgumentException
2517
+	 * @throws InvalidDataTypeException
2518
+	 * @throws InvalidInterfaceException
2519
+	 */
2520
+	protected function _update_default_event_settings()
2521
+	{
2522
+		$form = $this->_default_event_settings_form();
2523
+		if ($form->was_submitted()) {
2524
+			$form->receive_form_submission();
2525
+			if ($form->is_valid()) {
2526
+				$registration_config = EE_Registry::instance()->CFG->registration;
2527
+				$valid_data = $form->valid_data();
2528
+				if (isset($valid_data['default_reg_status'])) {
2529
+					$registration_config->default_STS_ID = $valid_data['default_reg_status'];
2530
+				}
2531
+				if (isset($valid_data['default_max_tickets'])) {
2532
+					$registration_config->default_maximum_number_of_tickets = $valid_data['default_max_tickets'];
2533
+				}
2534
+				do_action(
2535
+					'AHEE__Events_Admin_Page___update_default_event_settings',
2536
+					$valid_data,
2537
+					EE_Registry::instance()->CFG,
2538
+					$this
2539
+				);
2540
+				// update because data was valid!
2541
+				EE_Registry::instance()->CFG->update_espresso_config();
2542
+				EE_Error::overwrite_success();
2543
+				EE_Error::add_success(
2544
+					__('Default Event Settings were updated', 'event_espresso')
2545
+				);
2546
+			}
2547
+		}
2548
+		$this->_redirect_after_action(0, '', '', ['action' => 'default_event_settings'], true);
2549
+	}
2550
+
2551
+
2552
+	/*************        Templates        *************/
2553
+	protected function _template_settings()
2554
+	{
2555
+		$this->_admin_page_title = esc_html__('Template Settings (Preview)', 'event_espresso');
2556
+		$this->_template_args['preview_img'] = '<img src="'
2557
+											   . EVENTS_ASSETS_URL
2558
+											   . '/images/'
2559
+											   . 'caffeinated_template_features.jpg" alt="'
2560
+											   . esc_attr__('Template Settings Preview screenshot', 'event_espresso')
2561
+											   . '" />';
2562
+		$this->_template_args['preview_text'] = '<strong>'
2563
+												. esc_html__(
2564
+													'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2565
+													'event_espresso'
2566
+												) . '</strong>';
2567
+		$this->display_admin_caf_preview_page('template_settings_tab');
2568
+	}
2569
+
2570
+
2571
+	/** Event Category Stuff **/
2572
+	/**
2573
+	 * set the _category property with the category object for the loaded page.
2574
+	 *
2575
+	 * @access private
2576
+	 * @return void
2577
+	 */
2578
+	private function _set_category_object()
2579
+	{
2580
+		if (isset($this->_category->id) && ! empty($this->_category->id)) {
2581
+			return;
2582
+		} //already have the category object so get out.
2583
+		// set default category object
2584
+		$this->_set_empty_category_object();
2585
+		// only set if we've got an id
2586
+		if (! isset($this->_req_data['EVT_CAT_ID'])) {
2587
+			return;
2588
+		}
2589
+		$category_id = absint($this->_req_data['EVT_CAT_ID']);
2590
+		$term = get_term($category_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2591
+		if (! empty($term)) {
2592
+			$this->_category->category_name = $term->name;
2593
+			$this->_category->category_identifier = $term->slug;
2594
+			$this->_category->category_desc = $term->description;
2595
+			$this->_category->id = $term->term_id;
2596
+			$this->_category->parent = $term->parent;
2597
+		}
2598
+	}
2599
+
2600
+
2601
+	/**
2602
+	 * Clears out category properties.
2603
+	 */
2604
+	private function _set_empty_category_object()
2605
+	{
2606
+		$this->_category = new stdClass();
2607
+		$this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
2608
+		$this->_category->id = $this->_category->parent = 0;
2609
+	}
2610
+
2611
+
2612
+	/**
2613
+	 * @throws DomainException
2614
+	 * @throws EE_Error
2615
+	 * @throws InvalidArgumentException
2616
+	 * @throws InvalidDataTypeException
2617
+	 * @throws InvalidInterfaceException
2618
+	 */
2619
+	protected function _category_list_table()
2620
+	{
2621
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2622
+		$this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2623
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2624
+			'add_category',
2625
+			'add_category',
2626
+			[],
2627
+			'add-new-h2'
2628
+		);
2629
+		$this->display_admin_list_table_page_with_sidebar();
2630
+	}
2631
+
2632
+
2633
+	/**
2634
+	 * Output category details view.
2635
+	 *
2636
+	 * @param string $view
2637
+	 * @throws DomainException
2638
+	 * @throws EE_Error
2639
+	 * @throws InvalidArgumentException
2640
+	 * @throws InvalidDataTypeException
2641
+	 * @throws InvalidInterfaceException
2642
+	 */
2643
+	protected function _category_details($view)
2644
+	{
2645
+		// load formatter helper
2646
+		// load field generator helper
2647
+		$route = $view === 'edit' ? 'update_category' : 'insert_category';
2648
+		$this->_set_add_edit_form_tags($route);
2649
+		$this->_set_category_object();
2650
+		$id = ! empty($this->_category->id) ? $this->_category->id : '';
2651
+		$delete_action = 'delete_category';
2652
+		// custom redirect
2653
+		$redirect = EE_Admin_Page::add_query_args_and_nonce(
2654
+			['action' => 'category_list'],
2655
+			$this->_admin_base_url
2656
+		);
2657
+		$this->_set_publish_post_box_vars('EVT_CAT_ID', $id, $delete_action, $redirect);
2658
+		// take care of contents
2659
+		$this->_template_args['admin_page_content'] = $this->_category_details_content();
2660
+		$this->display_admin_page_with_sidebar();
2661
+	}
2662
+
2663
+
2664
+	/**
2665
+	 * Output category details content.
2666
+	 *
2667
+	 * @throws DomainException
2668
+	 */
2669
+	protected function _category_details_content()
2670
+	{
2671
+		$editor_args['category_desc'] = [
2672
+			'type'          => 'wp_editor',
2673
+			'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
2674
+			'class'         => 'my_editor_custom',
2675
+			'wpeditor_args' => ['media_buttons' => false],
2676
+		];
2677
+		$_wp_editor = $this->_generate_admin_form_fields($editor_args, 'array');
2678
+		$all_terms = get_terms(
2679
+			[EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY],
2680
+			['hide_empty' => 0, 'exclude' => [$this->_category->id]]
2681
+		);
2682
+		// setup category select for term parents.
2683
+		$category_select_values[] = [
2684
+			'text' => esc_html__('No Parent', 'event_espresso'),
2685
+			'id'   => 0,
2686
+		];
2687
+		foreach ($all_terms as $term) {
2688
+			$category_select_values[] = [
2689
+				'text' => $term->name,
2690
+				'id'   => $term->term_id,
2691
+			];
2692
+		}
2693
+		$category_select = EEH_Form_Fields::select_input(
2694
+			'category_parent',
2695
+			$category_select_values,
2696
+			$this->_category->parent
2697
+		);
2698
+		$template_args = [
2699
+			'category'                 => $this->_category,
2700
+			'category_select'          => $category_select,
2701
+			'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
2702
+			'category_desc_editor'     => $_wp_editor['category_desc']['field'],
2703
+			'disable'                  => '',
2704
+			'disabled_message'         => false,
2705
+		];
2706
+		$template = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2707
+		return EEH_Template::display_template($template, $template_args, true);
2708
+	}
2709
+
2710
+
2711
+	/**
2712
+	 * Handles deleting categories.
2713
+	 */
2714
+	protected function _delete_categories()
2715
+	{
2716
+		$cat_ids = isset($this->_req_data['EVT_CAT_ID']) ? (array) $this->_req_data['EVT_CAT_ID']
2717
+			: (array) $this->_req_data['category_id'];
2718
+		foreach ($cat_ids as $cat_id) {
2719
+			$this->_delete_category($cat_id);
2720
+		}
2721
+		// doesn't matter what page we're coming from... we're going to the same place after delete.
2722
+		$query_args = [
2723
+			'action' => 'category_list',
2724
+		];
2725
+		$this->_redirect_after_action(0, '', '', $query_args);
2726
+	}
2727
+
2728
+
2729
+	/**
2730
+	 * Handles deleting specific category.
2731
+	 *
2732
+	 * @param int $cat_id
2733
+	 */
2734
+	protected function _delete_category($cat_id)
2735
+	{
2736
+		$cat_id = absint($cat_id);
2737
+		wp_delete_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2738
+	}
2739
+
2740
+
2741
+	/**
2742
+	 * Handles triggering the update or insertion of a new category.
2743
+	 *
2744
+	 * @param bool $new_category true means we're triggering the insert of a new category.
2745
+	 * @throws EE_Error
2746
+	 * @throws InvalidArgumentException
2747
+	 * @throws InvalidDataTypeException
2748
+	 * @throws InvalidInterfaceException
2749
+	 */
2750
+	protected function _insert_or_update_category($new_category)
2751
+	{
2752
+		$cat_id = $new_category ? $this->_insert_category() : $this->_insert_category(true);
2753
+		$success = 0; // we already have a success message so lets not send another.
2754
+		if ($cat_id) {
2755
+			$query_args = [
2756
+				'action'     => 'edit_category',
2757
+				'EVT_CAT_ID' => $cat_id,
2758
+			];
2759
+		} else {
2760
+			$query_args = ['action' => 'add_category'];
2761
+		}
2762
+		$this->_redirect_after_action($success, '', '', $query_args, true);
2763
+	}
2764
+
2765
+
2766
+	/**
2767
+	 * Inserts or updates category
2768
+	 *
2769
+	 * @param bool $update (true indicates we're updating a category).
2770
+	 * @return bool|mixed|string
2771
+	 */
2772
+	private function _insert_category($update = false)
2773
+	{
2774
+		$cat_id = $update ? $this->_req_data['EVT_CAT_ID'] : '';
2775
+		$category_name = isset($this->_req_data['category_name']) ? $this->_req_data['category_name'] : '';
2776
+		$category_desc = isset($this->_req_data['category_desc']) ? $this->_req_data['category_desc'] : '';
2777
+		$category_parent = isset($this->_req_data['category_parent']) ? $this->_req_data['category_parent'] : 0;
2778
+		if (empty($category_name)) {
2779
+			$msg = esc_html__('You must add a name for the category.', 'event_espresso');
2780
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2781
+			return false;
2782
+		}
2783
+		$term_args = [
2784
+			'name'        => $category_name,
2785
+			'description' => $category_desc,
2786
+			'parent'      => $category_parent,
2787
+		];
2788
+		// was the category_identifier input disabled?
2789
+		if (isset($this->_req_data['category_identifier'])) {
2790
+			$term_args['slug'] = $this->_req_data['category_identifier'];
2791
+		}
2792
+		$insert_ids = $update
2793
+			? wp_update_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2794
+			: wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2795
+		if (! is_array($insert_ids)) {
2796
+			$msg = esc_html__(
2797
+				'An error occurred and the category has not been saved to the database.',
2798
+				'event_espresso'
2799
+			);
2800
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2801
+		} else {
2802
+			$cat_id = $insert_ids['term_id'];
2803
+			$msg = sprintf(esc_html__('The category %s was successfully saved', 'event_espresso'), $category_name);
2804
+			EE_Error::add_success($msg);
2805
+		}
2806
+		return $cat_id;
2807
+	}
2808
+
2809
+
2810
+	/**
2811
+	 * Gets categories or count of categories matching the arguments in the request.
2812
+	 *
2813
+	 * @param int  $per_page
2814
+	 * @param int  $current_page
2815
+	 * @param bool $count
2816
+	 * @return EE_Base_Class[]|EE_Term_Taxonomy[]|int
2817
+	 * @throws EE_Error
2818
+	 * @throws InvalidArgumentException
2819
+	 * @throws InvalidDataTypeException
2820
+	 * @throws InvalidInterfaceException
2821
+	 */
2822
+	public function get_categories($per_page = 10, $current_page = 1, $count = false)
2823
+	{
2824
+		// testing term stuff
2825
+		$orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'Term.term_id';
2826
+		$order = isset($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
2827
+		$limit = ($current_page - 1) * $per_page;
2828
+		$where = ['taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY];
2829
+		if (isset($this->_req_data['s'])) {
2830
+			$sstr = '%' . $this->_req_data['s'] . '%';
2831
+			$where['OR'] = [
2832
+				'Term.name'   => ['LIKE', $sstr],
2833
+				'description' => ['LIKE', $sstr],
2834
+			];
2835
+		}
2836
+		$query_params = [
2837
+			$where,
2838
+			'order_by'   => [$orderby => $order],
2839
+			'limit'      => $limit . ',' . $per_page,
2840
+			'force_join' => ['Term'],
2841
+		];
2842
+		$categories = $count
2843
+			? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
2844
+			: EEM_Term_Taxonomy::instance()->get_all($query_params);
2845
+		return $categories;
2846
+	}
2847
+
2848
+	/* end category stuff */
2849
+	/**************/
2850
+
2851
+
2852
+	/**
2853
+	 * Callback for the `ee_save_timezone_setting` ajax action.
2854
+	 *
2855
+	 * @throws EE_Error
2856
+	 * @throws InvalidArgumentException
2857
+	 * @throws InvalidDataTypeException
2858
+	 * @throws InvalidInterfaceException
2859
+	 */
2860
+	public function save_timezonestring_setting()
2861
+	{
2862
+		$timezone_string = isset($this->_req_data['timezone_selected'])
2863
+			? $this->_req_data['timezone_selected']
2864
+			: '';
2865
+		if (empty($timezone_string) || ! EEH_DTT_Helper::validate_timezone($timezone_string, false)) {
2866
+			EE_Error::add_error(
2867
+				esc_html__('An invalid timezone string submitted.', 'event_espresso'),
2868
+				__FILE__,
2869
+				__FUNCTION__,
2870
+				__LINE__
2871
+			);
2872
+			$this->_template_args['error'] = true;
2873
+			$this->_return_json();
2874
+		}
2875
+
2876
+		update_option('timezone_string', $timezone_string);
2877
+		EE_Error::add_success(
2878
+			esc_html__('Your timezone string was updated.', 'event_espresso')
2879
+		);
2880
+		$this->_template_args['success'] = true;
2881
+		$this->_return_json(true, ['action' => 'create_new']);
2882
+	}
2883 2883
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Base.model.php 2 patches
Indentation   +6477 added lines, -6477 removed lines patch added patch discarded remove patch
@@ -35,6483 +35,6483 @@
 block discarded – undo
35 35
 abstract class EEM_Base extends EE_Base implements ResettableInterface
36 36
 {
37 37
 
38
-    /**
39
-     * Flag to indicate whether the values provided to EEM_Base have already been prepared
40
-     * by the model object or not (ie, the model object has used the field's _prepare_for_set function on the values).
41
-     * They almost always WILL NOT, but it's not necessarily a requirement.
42
-     * For example, if you want to run EEM_Event::instance()->get_all(array(array('EVT_ID'=>$_GET['event_id'])));
43
-     *
44
-     * @var boolean
45
-     */
46
-    private $_values_already_prepared_by_model_object = 0;
47
-
48
-    /**
49
-     * when $_values_already_prepared_by_model_object equals this, we assume
50
-     * the data is just like form input that needs to have the model fields'
51
-     * prepare_for_set and prepare_for_use_in_db called on it
52
-     */
53
-    const not_prepared_by_model_object = 0;
54
-
55
-    /**
56
-     * when $_values_already_prepared_by_model_object equals this, we
57
-     * assume this value is coming from a model object and doesn't need to have
58
-     * prepare_for_set called on it, just prepare_for_use_in_db is used
59
-     */
60
-    const prepared_by_model_object = 1;
61
-
62
-    /**
63
-     * when $_values_already_prepared_by_model_object equals this, we assume
64
-     * the values are already to be used in the database (ie no processing is done
65
-     * on them by the model's fields)
66
-     */
67
-    const prepared_for_use_in_db = 2;
68
-
69
-
70
-    protected $singular_item = 'Item';
71
-
72
-    protected $plural_item   = 'Items';
73
-
74
-    /**
75
-     * @type \EE_Table_Base[] $_tables array of EE_Table objects for defining which tables comprise this model.
76
-     */
77
-    protected $_tables;
78
-
79
-    /**
80
-     * with two levels: top-level has array keys which are database table aliases (ie, keys in _tables)
81
-     * and the value is an array. Each of those sub-arrays have keys of field names (eg 'ATT_ID', which should also be
82
-     * variable names on the model objects (eg, EE_Attendee), and the keys should be children of EE_Model_Field
83
-     *
84
-     * @var \EE_Model_Field_Base[][] $_fields
85
-     */
86
-    protected $_fields;
87
-
88
-    /**
89
-     * array of different kinds of relations
90
-     *
91
-     * @var \EE_Model_Relation_Base[] $_model_relations
92
-     */
93
-    protected $_model_relations;
94
-
95
-    /**
96
-     * @var \EE_Index[] $_indexes
97
-     */
98
-    protected $_indexes = array();
99
-
100
-    /**
101
-     * Default strategy for getting where conditions on this model. This strategy is used to get default
102
-     * where conditions which are added to get_all, update, and delete queries. They can be overridden
103
-     * by setting the same columns as used in these queries in the query yourself.
104
-     *
105
-     * @var EE_Default_Where_Conditions
106
-     */
107
-    protected $_default_where_conditions_strategy;
108
-
109
-    /**
110
-     * Strategy for getting conditions on this model when 'default_where_conditions' equals 'minimum'.
111
-     * This is particularly useful when you want something between 'none' and 'default'
112
-     *
113
-     * @var EE_Default_Where_Conditions
114
-     */
115
-    protected $_minimum_where_conditions_strategy;
116
-
117
-    /**
118
-     * String describing how to find the "owner" of this model's objects.
119
-     * When there is a foreign key on this model to the wp_users table, this isn't needed.
120
-     * But when there isn't, this indicates which related model, or transiently-related model,
121
-     * has the foreign key to the wp_users table.
122
-     * Eg, for EEM_Registration this would be 'Event' because registrations are directly
123
-     * related to events, and events have a foreign key to wp_users.
124
-     * On EEM_Transaction, this would be 'Transaction.Event'
125
-     *
126
-     * @var string
127
-     */
128
-    protected $_model_chain_to_wp_user = '';
129
-
130
-    /**
131
-     * String describing how to find the model with a password controlling access to this model. This property has the
132
-     * same format as $_model_chain_to_wp_user. This is primarily used by the query param "exclude_protected".
133
-     * This value is the path of models to follow to arrive at the model with the password field.
134
-     * If it is an empty string, it means this model has the password field. If it is null, it means there is no
135
-     * model with a password that should affect reading this on the front-end.
136
-     * Eg this is an empty string for the Event model because it has a password.
137
-     * This is null for the Registration model, because its event's password has no bearing on whether
138
-     * you can read the registration or not on the front-end (it just depends on your capabilities.)
139
-     * This is 'Datetime.Event' on the Ticket model, because model queries for tickets that set "exclude_protected"
140
-     * should hide tickets for datetimes for events that have a password set.
141
-     * @var string |null
142
-     */
143
-    protected $model_chain_to_password = null;
144
-
145
-    /**
146
-     * This is a flag typically set by updates so that we don't load the where strategy on updates because updates
147
-     * don't need it (particularly CPT models)
148
-     *
149
-     * @var bool
150
-     */
151
-    protected $_ignore_where_strategy = false;
152
-
153
-    /**
154
-     * String used in caps relating to this model. Eg, if the caps relating to this
155
-     * model are 'ee_edit_events', 'ee_read_events', etc, it would be 'events'.
156
-     *
157
-     * @var string. If null it hasn't been initialized yet. If false then we
158
-     * have indicated capabilities don't apply to this
159
-     */
160
-    protected $_caps_slug = null;
161
-
162
-    /**
163
-     * 2d array where top-level keys are one of EEM_Base::valid_cap_contexts(),
164
-     * and next-level keys are capability names, and each's value is a
165
-     * EE_Default_Where_Condition. If the requester requests to apply caps to the query,
166
-     * they specify which context to use (ie, frontend, backend, edit or delete)
167
-     * and then each capability in the corresponding sub-array that they're missing
168
-     * adds the where conditions onto the query.
169
-     *
170
-     * @var array
171
-     */
172
-    protected $_cap_restrictions = array(
173
-        self::caps_read       => array(),
174
-        self::caps_read_admin => array(),
175
-        self::caps_edit       => array(),
176
-        self::caps_delete     => array(),
177
-    );
178
-
179
-    /**
180
-     * Array defining which cap restriction generators to use to create default
181
-     * cap restrictions to put in EEM_Base::_cap_restrictions.
182
-     * Array-keys are one of EEM_Base::valid_cap_contexts(), and values are a child of
183
-     * EE_Restriction_Generator_Base. If you don't want any cap restrictions generated
184
-     * automatically set this to false (not just null).
185
-     *
186
-     * @var EE_Restriction_Generator_Base[]
187
-     */
188
-    protected $_cap_restriction_generators = array();
189
-
190
-    /**
191
-     * constants used to categorize capability restrictions on EEM_Base::_caps_restrictions
192
-     */
193
-    const caps_read       = 'read';
194
-
195
-    const caps_read_admin = 'read_admin';
196
-
197
-    const caps_edit       = 'edit';
198
-
199
-    const caps_delete     = 'delete';
200
-
201
-    /**
202
-     * Keys are all the cap contexts (ie constants EEM_Base::_caps_*) and values are their 'action'
203
-     * as how they'd be used in capability names. Eg EEM_Base::caps_read ('read_frontend')
204
-     * maps to 'read' because when looking for relevant permissions we're going to use
205
-     * 'read' in teh capabilities names like 'ee_read_events' etc.
206
-     *
207
-     * @var array
208
-     */
209
-    protected $_cap_contexts_to_cap_action_map = array(
210
-        self::caps_read       => 'read',
211
-        self::caps_read_admin => 'read',
212
-        self::caps_edit       => 'edit',
213
-        self::caps_delete     => 'delete',
214
-    );
215
-
216
-    /**
217
-     * Timezone
218
-     * This gets set via the constructor so that we know what timezone incoming strings|timestamps are in when there
219
-     * are EE_Datetime_Fields in use.  This can also be used before a get to set what timezone you want strings coming
220
-     * out of the created objects.  NOT all EEM_Base child classes use this property but any that use a
221
-     * EE_Datetime_Field data type will have access to it.
222
-     *
223
-     * @var string
224
-     */
225
-    protected $_timezone;
226
-
227
-
228
-    /**
229
-     * This holds the id of the blog currently making the query.  Has no bearing on single site but is used for
230
-     * multisite.
231
-     *
232
-     * @var int
233
-     */
234
-    protected static $_model_query_blog_id;
235
-
236
-    /**
237
-     * A copy of _fields, except the array keys are the model names pointed to by
238
-     * the field
239
-     *
240
-     * @var EE_Model_Field_Base[]
241
-     */
242
-    private $_cache_foreign_key_to_fields = array();
243
-
244
-    /**
245
-     * Cached list of all the fields on the model, indexed by their name
246
-     *
247
-     * @var EE_Model_Field_Base[]
248
-     */
249
-    private $_cached_fields = null;
250
-
251
-    /**
252
-     * Cached list of all the fields on the model, except those that are
253
-     * marked as only pertinent to the database
254
-     *
255
-     * @var EE_Model_Field_Base[]
256
-     */
257
-    private $_cached_fields_non_db_only = null;
258
-
259
-    /**
260
-     * A cached reference to the primary key for quick lookup
261
-     *
262
-     * @var EE_Model_Field_Base
263
-     */
264
-    private $_primary_key_field = null;
265
-
266
-    /**
267
-     * Flag indicating whether this model has a primary key or not
268
-     *
269
-     * @var boolean
270
-     */
271
-    protected $_has_primary_key_field = null;
272
-
273
-    /**
274
-     * array in the format:  [ FK alias => full PK ]
275
-     * where keys are local column name aliases for foreign keys
276
-     * and values are the fully qualified column name for the primary key they represent
277
-     *  ex:
278
-     *      [ 'Event.EVT_wp_user' => 'WP_User.ID' ]
279
-     *
280
-     * @var array $foreign_key_aliases
281
-     */
282
-    protected $foreign_key_aliases = [];
283
-
284
-    /**
285
-     * Whether or not this model is based off a table in WP core only (CPTs should set
286
-     * this to FALSE, but if we were to make an EE_WP_Post model, it should set this to true).
287
-     * This should be true for models that deal with data that should exist independent of EE.
288
-     * For example, if the model can read and insert data that isn't used by EE, this should be true.
289
-     * It would be false, however, if you could guarantee the model would only interact with EE data,
290
-     * even if it uses a WP core table (eg event and venue models set this to false for that reason:
291
-     * they can only read and insert events and venues custom post types, not arbitrary post types)
292
-     * @var boolean
293
-     */
294
-    protected $_wp_core_model = false;
295
-
296
-    /**
297
-     * @var bool stores whether this model has a password field or not.
298
-     * null until initialized by hasPasswordField()
299
-     */
300
-    protected $has_password_field;
301
-
302
-    /**
303
-     * @var EE_Password_Field|null Automatically set when calling getPasswordField()
304
-     */
305
-    protected $password_field;
306
-
307
-    /**
308
-     *    List of valid operators that can be used for querying.
309
-     * The keys are all operators we'll accept, the values are the real SQL
310
-     * operators used
311
-     *
312
-     * @var array
313
-     */
314
-    protected $_valid_operators = array(
315
-        '='           => '=',
316
-        '<='          => '<=',
317
-        '<'           => '<',
318
-        '>='          => '>=',
319
-        '>'           => '>',
320
-        '!='          => '!=',
321
-        'LIKE'        => 'LIKE',
322
-        'like'        => 'LIKE',
323
-        'NOT_LIKE'    => 'NOT LIKE',
324
-        'not_like'    => 'NOT LIKE',
325
-        'NOT LIKE'    => 'NOT LIKE',
326
-        'not like'    => 'NOT LIKE',
327
-        'IN'          => 'IN',
328
-        'in'          => 'IN',
329
-        'NOT_IN'      => 'NOT IN',
330
-        'not_in'      => 'NOT IN',
331
-        'NOT IN'      => 'NOT IN',
332
-        'not in'      => 'NOT IN',
333
-        'between'     => 'BETWEEN',
334
-        'BETWEEN'     => 'BETWEEN',
335
-        'IS_NOT_NULL' => 'IS NOT NULL',
336
-        'is_not_null' => 'IS NOT NULL',
337
-        'IS NOT NULL' => 'IS NOT NULL',
338
-        'is not null' => 'IS NOT NULL',
339
-        'IS_NULL'     => 'IS NULL',
340
-        'is_null'     => 'IS NULL',
341
-        'IS NULL'     => 'IS NULL',
342
-        'is null'     => 'IS NULL',
343
-        'REGEXP'      => 'REGEXP',
344
-        'regexp'      => 'REGEXP',
345
-        'NOT_REGEXP'  => 'NOT REGEXP',
346
-        'not_regexp'  => 'NOT REGEXP',
347
-        'NOT REGEXP'  => 'NOT REGEXP',
348
-        'not regexp'  => 'NOT REGEXP',
349
-    );
350
-
351
-    /**
352
-     * operators that work like 'IN', accepting a comma-separated list of values inside brackets. Eg '(1,2,3)'
353
-     *
354
-     * @var array
355
-     */
356
-    protected $_in_style_operators = array('IN', 'NOT IN');
357
-
358
-    /**
359
-     * operators that work like 'BETWEEN'.  Typically used for datetime calculations, i.e. "BETWEEN '12-1-2011' AND
360
-     * '12-31-2012'"
361
-     *
362
-     * @var array
363
-     */
364
-    protected $_between_style_operators = array('BETWEEN');
365
-
366
-    /**
367
-     * Operators that work like SQL's like: input should be assumed to be a string, already prepared for a LIKE query.
368
-     * @var array
369
-     */
370
-    protected $_like_style_operators = array('LIKE', 'NOT LIKE');
371
-    /**
372
-     * operators that are used for handling NUll and !NULL queries.  Typically used for when checking if a row exists
373
-     * on a join table.
374
-     *
375
-     * @var array
376
-     */
377
-    protected $_null_style_operators = array('IS NOT NULL', 'IS NULL');
378
-
379
-    /**
380
-     * Allowed values for $query_params['order'] for ordering in queries
381
-     *
382
-     * @var array
383
-     */
384
-    protected $_allowed_order_values = array('asc', 'desc', 'ASC', 'DESC');
385
-
386
-    /**
387
-     * When these are keys in a WHERE or HAVING clause, they are handled much differently
388
-     * than regular field names. It is assumed that their values are an array of WHERE conditions
389
-     *
390
-     * @var array
391
-     */
392
-    private $_logic_query_param_keys = array('not', 'and', 'or', 'NOT', 'AND', 'OR');
393
-
394
-    /**
395
-     * Allowed keys in $query_params arrays passed into queries. Note that 0 is meant to always be a
396
-     * 'where', but 'where' clauses are so common that we thought we'd omit it
397
-     *
398
-     * @var array
399
-     */
400
-    private $_allowed_query_params = array(
401
-        0,
402
-        'limit',
403
-        'order_by',
404
-        'group_by',
405
-        'having',
406
-        'force_join',
407
-        'order',
408
-        'on_join_limit',
409
-        'default_where_conditions',
410
-        'caps',
411
-        'extra_selects',
412
-        'exclude_protected',
413
-    );
414
-
415
-    /**
416
-     * All the data types that can be used in $wpdb->prepare statements.
417
-     *
418
-     * @var array
419
-     */
420
-    private $_valid_wpdb_data_types = array('%d', '%s', '%f');
421
-
422
-    /**
423
-     * @var EE_Registry $EE
424
-     */
425
-    protected $EE = null;
426
-
427
-
428
-    /**
429
-     * Property which, when set, will have this model echo out the next X queries to the page for debugging.
430
-     *
431
-     * @var int
432
-     */
433
-    protected $_show_next_x_db_queries = 0;
434
-
435
-    /**
436
-     * When using _get_all_wpdb_results, you can specify a custom selection. If you do so,
437
-     * it gets saved on this property as an instance of CustomSelects so those selections can be used in
438
-     * WHERE, GROUP_BY, etc.
439
-     *
440
-     * @var CustomSelects
441
-     */
442
-    protected $_custom_selections = array();
443
-
444
-    /**
445
-     * key => value Entity Map using  array( EEM_Base::$_model_query_blog_id => array( ID => model object ) )
446
-     * caches every model object we've fetched from the DB on this request
447
-     *
448
-     * @var array
449
-     */
450
-    protected $_entity_map;
451
-
452
-    /**
453
-     * @var LoaderInterface $loader
454
-     */
455
-    private static $loader;
456
-
457
-
458
-    /**
459
-     * constant used to show EEM_Base has not yet verified the db on this http request
460
-     */
461
-    const db_verified_none = 0;
462
-
463
-    /**
464
-     * constant used to show EEM_Base has verified the EE core db on this http request,
465
-     * but not the addons' dbs
466
-     */
467
-    const db_verified_core = 1;
468
-
469
-    /**
470
-     * constant used to show EEM_Base has verified the addons' dbs (and implicitly
471
-     * the EE core db too)
472
-     */
473
-    const db_verified_addons = 2;
474
-
475
-    /**
476
-     * indicates whether an EEM_Base child has already re-verified the DB
477
-     * is ok (we don't want to do it repetitively). Should be set to one the constants
478
-     * looking like EEM_Base::db_verified_*
479
-     *
480
-     * @var int - 0 = none, 1 = core, 2 = addons
481
-     */
482
-    protected static $_db_verification_level = EEM_Base::db_verified_none;
483
-
484
-    /**
485
-     * @const constant for 'default_where_conditions' to apply default where conditions to ALL queried models
486
-     *        (eg, if retrieving registrations ordered by their datetimes, this will only return non-trashed
487
-     *        registrations for non-trashed tickets for non-trashed datetimes)
488
-     */
489
-    const default_where_conditions_all = 'all';
490
-
491
-    /**
492
-     * @const constant for 'default_where_conditions' to apply default where conditions to THIS model only, but
493
-     *        no other models which are joined to (eg, if retrieving registrations ordered by their datetimes, this will
494
-     *        return non-trashed registrations, regardless of the related datetimes and tickets' statuses).
495
-     *        It is preferred to use EEM_Base::default_where_conditions_minimum_others because, when joining to
496
-     *        models which share tables with other models, this can return data for the wrong model.
497
-     */
498
-    const default_where_conditions_this_only = 'this_model_only';
499
-
500
-    /**
501
-     * @const constant for 'default_where_conditions' to apply default where conditions to other models queried,
502
-     *        but not the current model (eg, if retrieving registrations ordered by their datetimes, this will
503
-     *        return all registrations related to non-trashed tickets and non-trashed datetimes)
504
-     */
505
-    const default_where_conditions_others_only = 'other_models_only';
506
-
507
-    /**
508
-     * @const constant for 'default_where_conditions' to apply minimum where conditions to all models queried.
509
-     *        For most models this the same as EEM_Base::default_where_conditions_none, except for models which share
510
-     *        their table with other models, like the Event and Venue models. For example, when querying for events
511
-     *        ordered by their venues' name, this will be sure to only return real events with associated real venues
512
-     *        (regardless of whether those events and venues are trashed)
513
-     *        In contrast, using EEM_Base::default_where_conditions_none would could return WP posts other than EE
514
-     *        events.
515
-     */
516
-    const default_where_conditions_minimum_all = 'minimum';
517
-
518
-    /**
519
-     * @const constant for 'default_where_conditions' to apply apply where conditions to other models, and full default
520
-     *        where conditions for the queried model (eg, when querying events ordered by venues' names, this will
521
-     *        return non-trashed events for any venues, regardless of whether those associated venues are trashed or
522
-     *        not)
523
-     */
524
-    const default_where_conditions_minimum_others = 'full_this_minimum_others';
525
-
526
-    /**
527
-     * @const constant for 'default_where_conditions' to NOT apply any where conditions. This should very rarely be
528
-     *        used, because when querying from a model which shares its table with another model (eg Events and Venues)
529
-     *        it's possible it will return table entries for other models. You should use
530
-     *        EEM_Base::default_where_conditions_minimum_all instead.
531
-     */
532
-    const default_where_conditions_none = 'none';
533
-
534
-
535
-
536
-    /**
537
-     * About all child constructors:
538
-     * they should define the _tables, _fields and _model_relations arrays.
539
-     * Should ALWAYS be called after child constructor.
540
-     * In order to make the child constructors to be as simple as possible, this parent constructor
541
-     * finalizes constructing all the object's attributes.
542
-     * Generally, rather than requiring a child to code
543
-     * $this->_tables = array(
544
-     *        'Event_Post_Table' => new EE_Table('Event_Post_Table','wp_posts')
545
-     *        ...);
546
-     *  (thus repeating itself in the array key and in the constructor of the new EE_Table,)
547
-     * each EE_Table has a function to set the table's alias after the constructor, using
548
-     * the array key ('Event_Post_Table'), instead of repeating it. The model fields and model relations
549
-     * do something similar.
550
-     *
551
-     * @param null $timezone
552
-     * @throws EE_Error
553
-     */
554
-    protected function __construct($timezone = null)
555
-    {
556
-        // check that the model has not been loaded too soon
557
-        if (! did_action('AHEE__EE_System__load_espresso_addons')) {
558
-            throw new EE_Error(
559
-                sprintf(
560
-                    __(
561
-                        'The %1$s model can not be loaded before the "AHEE__EE_System__load_espresso_addons" hook has been called. This gives other addons a chance to extend this model.',
562
-                        'event_espresso'
563
-                    ),
564
-                    get_class($this)
565
-                )
566
-            );
567
-        }
568
-        /**
569
-         * Set blogid for models to current blog. However we ONLY do this if $_model_query_blog_id is not already set.
570
-         */
571
-        if (empty(EEM_Base::$_model_query_blog_id)) {
572
-            EEM_Base::set_model_query_blog_id();
573
-        }
574
-        /**
575
-         * Filters the list of tables on a model. It is best to NOT use this directly and instead
576
-         * just use EE_Register_Model_Extension
577
-         *
578
-         * @var EE_Table_Base[] $_tables
579
-         */
580
-        $this->_tables = (array) apply_filters('FHEE__' . get_class($this) . '__construct__tables', $this->_tables);
581
-        foreach ($this->_tables as $table_alias => $table_obj) {
582
-            /** @var $table_obj EE_Table_Base */
583
-            $table_obj->_construct_finalize_with_alias($table_alias);
584
-            if ($table_obj instanceof EE_Secondary_Table) {
585
-                /** @var $table_obj EE_Secondary_Table */
586
-                $table_obj->_construct_finalize_set_table_to_join_with($this->_get_main_table());
587
-            }
588
-        }
589
-        /**
590
-         * Filters the list of fields on a model. It is best to NOT use this directly and instead just use
591
-         * EE_Register_Model_Extension
592
-         *
593
-         * @param EE_Model_Field_Base[] $_fields
594
-         */
595
-        $this->_fields = (array) apply_filters('FHEE__' . get_class($this) . '__construct__fields', $this->_fields);
596
-        $this->_invalidate_field_caches();
597
-        foreach ($this->_fields as $table_alias => $fields_for_table) {
598
-            if (! array_key_exists($table_alias, $this->_tables)) {
599
-                throw new EE_Error(sprintf(__(
600
-                    "Table alias %s does not exist in EEM_Base child's _tables array. Only tables defined are %s",
601
-                    'event_espresso'
602
-                ), $table_alias, implode(",", $this->_fields)));
603
-            }
604
-            foreach ($fields_for_table as $field_name => $field_obj) {
605
-                /** @var $field_obj EE_Model_Field_Base | EE_Primary_Key_Field_Base */
606
-                // primary key field base has a slightly different _construct_finalize
607
-                /** @var $field_obj EE_Model_Field_Base */
608
-                $field_obj->_construct_finalize($table_alias, $field_name, $this->get_this_model_name());
609
-            }
610
-        }
611
-        // everything is related to Extra_Meta
612
-        if (get_class($this) !== 'EEM_Extra_Meta') {
613
-            // make extra meta related to everything, but don't block deleting things just
614
-            // because they have related extra meta info. For now just orphan those extra meta
615
-            // in the future we should automatically delete them
616
-            $this->_model_relations['Extra_Meta'] = new EE_Has_Many_Any_Relation(false);
617
-        }
618
-        // and change logs
619
-        if (get_class($this) !== 'EEM_Change_Log') {
620
-            $this->_model_relations['Change_Log'] = new EE_Has_Many_Any_Relation(false);
621
-        }
622
-        /**
623
-         * Filters the list of relations on a model. It is best to NOT use this directly and instead just use
624
-         * EE_Register_Model_Extension
625
-         *
626
-         * @param EE_Model_Relation_Base[] $_model_relations
627
-         */
628
-        $this->_model_relations = (array) apply_filters(
629
-            'FHEE__' . get_class($this) . '__construct__model_relations',
630
-            $this->_model_relations
631
-        );
632
-        foreach ($this->_model_relations as $model_name => $relation_obj) {
633
-            /** @var $relation_obj EE_Model_Relation_Base */
634
-            $relation_obj->_construct_finalize_set_models($this->get_this_model_name(), $model_name);
635
-        }
636
-        foreach ($this->_indexes as $index_name => $index_obj) {
637
-            /** @var $index_obj EE_Index */
638
-            $index_obj->_construct_finalize($index_name, $this->get_this_model_name());
639
-        }
640
-        $this->set_timezone($timezone);
641
-        // finalize default where condition strategy, or set default
642
-        if (! $this->_default_where_conditions_strategy) {
643
-            // nothing was set during child constructor, so set default
644
-            $this->_default_where_conditions_strategy = new EE_Default_Where_Conditions();
645
-        }
646
-        $this->_default_where_conditions_strategy->_finalize_construct($this);
647
-        if (! $this->_minimum_where_conditions_strategy) {
648
-            // nothing was set during child constructor, so set default
649
-            $this->_minimum_where_conditions_strategy = new EE_Default_Where_Conditions();
650
-        }
651
-        $this->_minimum_where_conditions_strategy->_finalize_construct($this);
652
-        // if the cap slug hasn't been set, and we haven't set it to false on purpose
653
-        // to indicate to NOT set it, set it to the logical default
654
-        if ($this->_caps_slug === null) {
655
-            $this->_caps_slug = EEH_Inflector::pluralize_and_lower($this->get_this_model_name());
656
-        }
657
-        // initialize the standard cap restriction generators if none were specified by the child constructor
658
-        if ($this->_cap_restriction_generators !== false) {
659
-            foreach ($this->cap_contexts_to_cap_action_map() as $cap_context => $action) {
660
-                if (! isset($this->_cap_restriction_generators[ $cap_context ])) {
661
-                    $this->_cap_restriction_generators[ $cap_context ] = apply_filters(
662
-                        'FHEE__EEM_Base___construct__standard_cap_restriction_generator',
663
-                        new EE_Restriction_Generator_Protected(),
664
-                        $cap_context,
665
-                        $this
666
-                    );
667
-                }
668
-            }
669
-        }
670
-        // if there are cap restriction generators, use them to make the default cap restrictions
671
-        if ($this->_cap_restriction_generators !== false) {
672
-            foreach ($this->_cap_restriction_generators as $context => $generator_object) {
673
-                if (! $generator_object) {
674
-                    continue;
675
-                }
676
-                if (! $generator_object instanceof EE_Restriction_Generator_Base) {
677
-                    throw new EE_Error(
678
-                        sprintf(
679
-                            __(
680
-                                'Index "%1$s" in the model %2$s\'s _cap_restriction_generators is not a child of EE_Restriction_Generator_Base. It should be that or NULL.',
681
-                                'event_espresso'
682
-                            ),
683
-                            $context,
684
-                            $this->get_this_model_name()
685
-                        )
686
-                    );
687
-                }
688
-                $action = $this->cap_action_for_context($context);
689
-                if (! $generator_object->construction_finalized()) {
690
-                    $generator_object->_construct_finalize($this, $action);
691
-                }
692
-            }
693
-        }
694
-        do_action('AHEE__' . get_class($this) . '__construct__end');
695
-    }
696
-
697
-
698
-
699
-    /**
700
-     * Used to set the $_model_query_blog_id static property.
701
-     *
702
-     * @param int $blog_id  If provided then will set the blog_id for the models to this id.  If not provided then the
703
-     *                      value for get_current_blog_id() will be used.
704
-     */
705
-    public static function set_model_query_blog_id($blog_id = 0)
706
-    {
707
-        EEM_Base::$_model_query_blog_id = $blog_id > 0 ? (int) $blog_id : get_current_blog_id();
708
-    }
709
-
710
-
711
-
712
-    /**
713
-     * Returns whatever is set as the internal $model_query_blog_id.
714
-     *
715
-     * @return int
716
-     */
717
-    public static function get_model_query_blog_id()
718
-    {
719
-        return EEM_Base::$_model_query_blog_id;
720
-    }
721
-
722
-
723
-
724
-    /**
725
-     * This function is a singleton method used to instantiate the Espresso_model object
726
-     *
727
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings
728
-     *                                (and any incoming timezone data that gets saved).
729
-     *                                Note this just sends the timezone info to the date time model field objects.
730
-     *                                Default is NULL
731
-     *                                (and will be assumed using the set timezone in the 'timezone_string' wp option)
732
-     * @return static (as in the concrete child class)
733
-     * @throws EE_Error
734
-     * @throws InvalidArgumentException
735
-     * @throws InvalidDataTypeException
736
-     * @throws InvalidInterfaceException
737
-     */
738
-    public static function instance($timezone = null)
739
-    {
740
-        // check if instance of Espresso_model already exists
741
-        if (! static::$_instance instanceof static) {
742
-            // instantiate Espresso_model
743
-            static::$_instance = new static(
744
-                $timezone,
745
-                LoaderFactory::getLoader()->load('EventEspresso\core\services\orm\ModelFieldFactory')
746
-            );
747
-        }
748
-        // we might have a timezone set, let set_timezone decide what to do with it
749
-        static::$_instance->set_timezone($timezone);
750
-        // Espresso_model object
751
-        return static::$_instance;
752
-    }
753
-
754
-
755
-
756
-    /**
757
-     * resets the model and returns it
758
-     *
759
-     * @param null | string $timezone
760
-     * @return EEM_Base|null (if the model was already instantiated, returns it, with
761
-     * all its properties reset; if it wasn't instantiated, returns null)
762
-     * @throws EE_Error
763
-     * @throws ReflectionException
764
-     * @throws InvalidArgumentException
765
-     * @throws InvalidDataTypeException
766
-     * @throws InvalidInterfaceException
767
-     */
768
-    public static function reset($timezone = null)
769
-    {
770
-        if (static::$_instance instanceof EEM_Base) {
771
-            // let's try to NOT swap out the current instance for a new one
772
-            // because if someone has a reference to it, we can't remove their reference
773
-            // so it's best to keep using the same reference, but change the original object
774
-            // reset all its properties to their original values as defined in the class
775
-            $r = new ReflectionClass(get_class(static::$_instance));
776
-            $static_properties = $r->getStaticProperties();
777
-            foreach ($r->getDefaultProperties() as $property => $value) {
778
-                // don't set instance to null like it was originally,
779
-                // but it's static anyways, and we're ignoring static properties (for now at least)
780
-                if (! isset($static_properties[ $property ])) {
781
-                    static::$_instance->{$property} = $value;
782
-                }
783
-            }
784
-            // and then directly call its constructor again, like we would if we were creating a new one
785
-            static::$_instance->__construct(
786
-                $timezone,
787
-                LoaderFactory::getLoader()->load('EventEspresso\core\services\orm\ModelFieldFactory')
788
-            );
789
-            return self::instance();
790
-        }
791
-        return null;
792
-    }
793
-
794
-
795
-
796
-    /**
797
-     * @return LoaderInterface
798
-     * @throws InvalidArgumentException
799
-     * @throws InvalidDataTypeException
800
-     * @throws InvalidInterfaceException
801
-     */
802
-    private static function getLoader()
803
-    {
804
-        if (! EEM_Base::$loader instanceof LoaderInterface) {
805
-            EEM_Base::$loader = LoaderFactory::getLoader();
806
-        }
807
-        return EEM_Base::$loader;
808
-    }
809
-
810
-
811
-
812
-    /**
813
-     * retrieve the status details from esp_status table as an array IF this model has the status table as a relation.
814
-     *
815
-     * @param  boolean $translated return localized strings or JUST the array.
816
-     * @return array
817
-     * @throws EE_Error
818
-     * @throws InvalidArgumentException
819
-     * @throws InvalidDataTypeException
820
-     * @throws InvalidInterfaceException
821
-     */
822
-    public function status_array($translated = false)
823
-    {
824
-        if (! array_key_exists('Status', $this->_model_relations)) {
825
-            return array();
826
-        }
827
-        $model_name = $this->get_this_model_name();
828
-        $status_type = str_replace(' ', '_', strtolower(str_replace('_', ' ', $model_name)));
829
-        $stati = EEM_Status::instance()->get_all(array(array('STS_type' => $status_type)));
830
-        $status_array = array();
831
-        foreach ($stati as $status) {
832
-            $status_array[ $status->ID() ] = $status->get('STS_code');
833
-        }
834
-        return $translated
835
-            ? EEM_Status::instance()->localized_status($status_array, false, 'sentence')
836
-            : $status_array;
837
-    }
838
-
839
-
840
-
841
-    /**
842
-     * Gets all the EE_Base_Class objects which match the $query_params, by querying the DB.
843
-     *
844
-     * @param array $query_params  @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
845
-     *                             or if you have the development copy of EE you can view this at the path:
846
-     *                             /docs/G--Model-System/model-query-params.md
847
-     * @return EE_Base_Class[]  *note that there is NO option to pass the output type. If you want results different
848
-     *                                        from EE_Base_Class[], use get_all_wpdb_results(). Array keys are object IDs (if there is a primary key on the model.
849
-     *                                        if not, numerically indexed) Some full examples: get 10 transactions
850
-     *                                        which have Scottish attendees: EEM_Transaction::instance()->get_all(
851
-     *                                        array( array(
852
-     *                                        'OR'=>array(
853
-     *                                        'Registration.Attendee.ATT_fname'=>array('like','Mc%'),
854
-     *                                        'Registration.Attendee.ATT_fname*other'=>array('like','Mac%')
855
-     *                                        )
856
-     *                                        ),
857
-     *                                        'limit'=>10,
858
-     *                                        'group_by'=>'TXN_ID'
859
-     *                                        ));
860
-     *                                        get all the answers to the question titled "shirt size" for event with id
861
-     *                                        12, ordered by their answer EEM_Answer::instance()->get_all(array( array(
862
-     *                                        'Question.QST_display_text'=>'shirt size',
863
-     *                                        'Registration.Event.EVT_ID'=>12
864
-     *                                        ),
865
-     *                                        'order_by'=>array('ANS_value'=>'ASC')
866
-     *                                        ));
867
-     * @throws EE_Error
868
-     */
869
-    public function get_all($query_params = array())
870
-    {
871
-        if (
872
-            isset($query_params['limit'])
873
-            && ! isset($query_params['group_by'])
874
-        ) {
875
-            $query_params['group_by'] = array_keys($this->get_combined_primary_key_fields());
876
-        }
877
-        return $this->_create_objects($this->_get_all_wpdb_results($query_params, ARRAY_A, null));
878
-    }
879
-
880
-
881
-
882
-    /**
883
-     * Modifies the query parameters so we only get back model objects
884
-     * that "belong" to the current user
885
-     *
886
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
887
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
888
-     */
889
-    public function alter_query_params_to_only_include_mine($query_params = array())
890
-    {
891
-        $wp_user_field_name = $this->wp_user_field_name();
892
-        if ($wp_user_field_name) {
893
-            $query_params[0][ $wp_user_field_name ] = get_current_user_id();
894
-        }
895
-        return $query_params;
896
-    }
897
-
898
-
899
-
900
-    /**
901
-     * Returns the name of the field's name that points to the WP_User table
902
-     *  on this model (or follows the _model_chain_to_wp_user and uses that model's
903
-     * foreign key to the WP_User table)
904
-     *
905
-     * @return string|boolean string on success, boolean false when there is no
906
-     * foreign key to the WP_User table
907
-     */
908
-    public function wp_user_field_name()
909
-    {
910
-        try {
911
-            if (! empty($this->_model_chain_to_wp_user)) {
912
-                $models_to_follow_to_wp_users = explode('.', $this->_model_chain_to_wp_user);
913
-                $last_model_name = end($models_to_follow_to_wp_users);
914
-                $model_with_fk_to_wp_users = EE_Registry::instance()->load_model($last_model_name);
915
-                $model_chain_to_wp_user = $this->_model_chain_to_wp_user . '.';
916
-            } else {
917
-                $model_with_fk_to_wp_users = $this;
918
-                $model_chain_to_wp_user = '';
919
-            }
920
-            $wp_user_field = $model_with_fk_to_wp_users->get_foreign_key_to('WP_User');
921
-            return $model_chain_to_wp_user . $wp_user_field->get_name();
922
-        } catch (EE_Error $e) {
923
-            return false;
924
-        }
925
-    }
926
-
927
-
928
-
929
-    /**
930
-     * Returns the _model_chain_to_wp_user string, which indicates which related model
931
-     * (or transiently-related model) has a foreign key to the wp_users table;
932
-     * useful for finding if model objects of this type are 'owned' by the current user.
933
-     * This is an empty string when the foreign key is on this model and when it isn't,
934
-     * but is only non-empty when this model's ownership is indicated by a RELATED model
935
-     * (or transiently-related model)
936
-     *
937
-     * @return string
938
-     */
939
-    public function model_chain_to_wp_user()
940
-    {
941
-        return $this->_model_chain_to_wp_user;
942
-    }
943
-
944
-
945
-
946
-    /**
947
-     * Whether this model is 'owned' by a specific wordpress user (even indirectly,
948
-     * like how registrations don't have a foreign key to wp_users, but the
949
-     * events they are for are), or is unrelated to wp users.
950
-     * generally available
951
-     *
952
-     * @return boolean
953
-     */
954
-    public function is_owned()
955
-    {
956
-        if ($this->model_chain_to_wp_user()) {
957
-            return true;
958
-        }
959
-        try {
960
-            $this->get_foreign_key_to('WP_User');
961
-            return true;
962
-        } catch (EE_Error $e) {
963
-            return false;
964
-        }
965
-    }
966
-
967
-
968
-    /**
969
-     * Used internally to get WPDB results, because other functions, besides get_all, may want to do some queries, but
970
-     * may want to preserve the WPDB results (eg, update, which first queries to make sure we have all the tables on
971
-     * the model)
972
-     *
973
-     * @param array  $query_params      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
974
-     * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
975
-     * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
976
-     *                                  fields on the model, and the models we joined to in the query. However, you can
977
-     *                                  override this and set the select to "*", or a specific column name, like
978
-     *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
979
-     *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
980
-     *                                  the aliases used to refer to this selection, and values are to be
981
-     *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
982
-     *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
983
-     * @return array | stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
984
-     * @throws EE_Error
985
-     * @throws InvalidArgumentException
986
-     */
987
-    protected function _get_all_wpdb_results($query_params = array(), $output = ARRAY_A, $columns_to_select = null)
988
-    {
989
-        $this->_custom_selections = $this->getCustomSelection($query_params, $columns_to_select);
990
-        ;
991
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
992
-        $select_expressions = $columns_to_select === null
993
-            ? $this->_construct_default_select_sql($model_query_info)
994
-            : '';
995
-        if ($this->_custom_selections instanceof CustomSelects) {
996
-            $custom_expressions = $this->_custom_selections->columnsToSelectExpression();
997
-            $select_expressions .= $select_expressions
998
-                ? ', ' . $custom_expressions
999
-                : $custom_expressions;
1000
-        }
1001
-
1002
-        $SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1003
-        return $this->_do_wpdb_query('get_results', array($SQL, $output));
1004
-    }
1005
-
1006
-
1007
-    /**
1008
-     * Get a CustomSelects object if the $query_params or $columns_to_select allows for it.
1009
-     * Note: $query_params['extra_selects'] will always override any $columns_to_select values. It is the preferred
1010
-     * method of including extra select information.
1011
-     *
1012
-     * @param array             $query_params
1013
-     * @param null|array|string $columns_to_select
1014
-     * @return null|CustomSelects
1015
-     * @throws InvalidArgumentException
1016
-     */
1017
-    protected function getCustomSelection(array $query_params, $columns_to_select = null)
1018
-    {
1019
-        if (! isset($query_params['extra_selects']) && $columns_to_select === null) {
1020
-            return null;
1021
-        }
1022
-        $selects = isset($query_params['extra_selects']) ? $query_params['extra_selects'] : $columns_to_select;
1023
-        $selects = is_string($selects) ? explode(',', $selects) : $selects;
1024
-        return new CustomSelects($selects);
1025
-    }
1026
-
1027
-
1028
-
1029
-    /**
1030
-     * Gets an array of rows from the database just like $wpdb->get_results would,
1031
-     * but you can use the model query params to more easily
1032
-     * take care of joins, field preparation etc.
1033
-     *
1034
-     * @param array  $query_params      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1035
-     * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
1036
-     * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
1037
-     *                                  fields on the model, and the models we joined to in the query. However, you can
1038
-     *                                  override this and set the select to "*", or a specific column name, like
1039
-     *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
1040
-     *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
1041
-     *                                  the aliases used to refer to this selection, and values are to be
1042
-     *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
1043
-     *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
1044
-     * @return array|stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
1045
-     * @throws EE_Error
1046
-     */
1047
-    public function get_all_wpdb_results($query_params = array(), $output = ARRAY_A, $columns_to_select = null)
1048
-    {
1049
-        return $this->_get_all_wpdb_results($query_params, $output, $columns_to_select);
1050
-    }
1051
-
1052
-
1053
-
1054
-    /**
1055
-     * For creating a custom select statement
1056
-     *
1057
-     * @param mixed $columns_to_select either a string to be inserted directly as the select statement,
1058
-     *                                 or an array where keys are aliases, and values are arrays where 0=>the selection
1059
-     *                                 SQL, and 1=>is the datatype
1060
-     * @throws EE_Error
1061
-     * @return string
1062
-     */
1063
-    private function _construct_select_from_input($columns_to_select)
1064
-    {
1065
-        if (is_array($columns_to_select)) {
1066
-            $select_sql_array = array();
1067
-            foreach ($columns_to_select as $alias => $selection_and_datatype) {
1068
-                if (! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1069
-                    throw new EE_Error(
1070
-                        sprintf(
1071
-                            __(
1072
-                                "Custom selection %s (alias %s) needs to be an array like array('COUNT(REG_ID)','%%d')",
1073
-                                'event_espresso'
1074
-                            ),
1075
-                            $selection_and_datatype,
1076
-                            $alias
1077
-                        )
1078
-                    );
1079
-                }
1080
-                if (! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1081
-                    throw new EE_Error(
1082
-                        sprintf(
1083
-                            esc_html__(
1084
-                                "Datatype %s (for selection '%s' and alias '%s') is not a valid wpdb datatype (eg %%s)",
1085
-                                'event_espresso'
1086
-                            ),
1087
-                            $selection_and_datatype[1],
1088
-                            $selection_and_datatype[0],
1089
-                            $alias,
1090
-                            implode(', ', $this->_valid_wpdb_data_types)
1091
-                        )
1092
-                    );
1093
-                }
1094
-                $select_sql_array[] = "{$selection_and_datatype[0]} AS $alias";
1095
-            }
1096
-            $columns_to_select_string = implode(', ', $select_sql_array);
1097
-        } else {
1098
-            $columns_to_select_string = $columns_to_select;
1099
-        }
1100
-        return $columns_to_select_string;
1101
-    }
1102
-
1103
-
1104
-
1105
-    /**
1106
-     * Convenient wrapper for getting the primary key field's name. Eg, on Registration, this would be 'REG_ID'
1107
-     *
1108
-     * @return string
1109
-     * @throws EE_Error
1110
-     */
1111
-    public function primary_key_name()
1112
-    {
1113
-        return $this->get_primary_key_field()->get_name();
1114
-    }
1115
-
1116
-
1117
-    /**
1118
-     * Gets a single item for this model from the DB, given only its ID (or null if none is found).
1119
-     * If there is no primary key on this model, $id is treated as primary key string
1120
-     *
1121
-     * @param mixed $id int or string, depending on the type of the model's primary key
1122
-     * @return EE_Base_Class
1123
-     * @throws EE_Error
1124
-     */
1125
-    public function get_one_by_ID($id)
1126
-    {
1127
-        if ($this->get_from_entity_map($id)) {
1128
-            return $this->get_from_entity_map($id);
1129
-        }
1130
-        $model_object = $this->get_one(
1131
-            $this->alter_query_params_to_restrict_by_ID(
1132
-                $id,
1133
-                array('default_where_conditions' => EEM_Base::default_where_conditions_minimum_all)
1134
-            )
1135
-        );
1136
-        $className = $this->_get_class_name();
1137
-        if ($model_object instanceof $className) {
1138
-            // make sure valid objects get added to the entity map
1139
-            // so that the next call to this method doesn't trigger another trip to the db
1140
-            $this->add_to_entity_map($model_object);
1141
-        }
1142
-        return $model_object;
1143
-    }
1144
-
1145
-
1146
-
1147
-    /**
1148
-     * Alters query parameters to only get items with this ID are returned.
1149
-     * Takes into account that the ID might be a string produced by EEM_Base::get_index_primary_key_string(),
1150
-     * or could just be a simple primary key ID
1151
-     *
1152
-     * @param int   $id
1153
-     * @param array $query_params
1154
-     * @return array of normal query params, @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1155
-     * @throws EE_Error
1156
-     */
1157
-    public function alter_query_params_to_restrict_by_ID($id, $query_params = array())
1158
-    {
1159
-        if (! isset($query_params[0])) {
1160
-            $query_params[0] = array();
1161
-        }
1162
-        $conditions_from_id = $this->parse_index_primary_key_string($id);
1163
-        if ($conditions_from_id === null) {
1164
-            $query_params[0][ $this->primary_key_name() ] = $id;
1165
-        } else {
1166
-            // no primary key, so the $id must be from the get_index_primary_key_string()
1167
-            $query_params[0] = array_replace_recursive($query_params[0], $this->parse_index_primary_key_string($id));
1168
-        }
1169
-        return $query_params;
1170
-    }
1171
-
1172
-
1173
-
1174
-    /**
1175
-     * Gets a single item for this model from the DB, given the $query_params. Only returns a single class, not an
1176
-     * array. If no item is found, null is returned.
1177
-     *
1178
-     * @param array $query_params like EEM_Base's $query_params variable.
1179
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1180
-     * @throws EE_Error
1181
-     */
1182
-    public function get_one($query_params = array())
1183
-    {
1184
-        if (! is_array($query_params)) {
1185
-            EE_Error::doing_it_wrong(
1186
-                'EEM_Base::get_one',
1187
-                sprintf(
1188
-                    __('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1189
-                    gettype($query_params)
1190
-                ),
1191
-                '4.6.0'
1192
-            );
1193
-            $query_params = array();
1194
-        }
1195
-        $query_params['limit'] = 1;
1196
-        $items = $this->get_all($query_params);
1197
-        if (empty($items)) {
1198
-            return null;
1199
-        }
1200
-        return array_shift($items);
1201
-    }
1202
-
1203
-
1204
-
1205
-    /**
1206
-     * Returns the next x number of items in sequence from the given value as
1207
-     * found in the database matching the given query conditions.
1208
-     *
1209
-     * @param mixed $current_field_value    Value used for the reference point.
1210
-     * @param null  $field_to_order_by      What field is used for the
1211
-     *                                      reference point.
1212
-     * @param int   $limit                  How many to return.
1213
-     * @param array $query_params           Extra conditions on the query.
1214
-     * @param null  $columns_to_select      If left null, then an array of
1215
-     *                                      EE_Base_Class objects is returned,
1216
-     *                                      otherwise you can indicate just the
1217
-     *                                      columns you want returned.
1218
-     * @return EE_Base_Class[]|array
1219
-     * @throws EE_Error
1220
-     */
1221
-    public function next_x(
1222
-        $current_field_value,
1223
-        $field_to_order_by = null,
1224
-        $limit = 1,
1225
-        $query_params = array(),
1226
-        $columns_to_select = null
1227
-    ) {
1228
-        return $this->_get_consecutive(
1229
-            $current_field_value,
1230
-            '>',
1231
-            $field_to_order_by,
1232
-            $limit,
1233
-            $query_params,
1234
-            $columns_to_select
1235
-        );
1236
-    }
1237
-
1238
-
1239
-
1240
-    /**
1241
-     * Returns the previous x number of items in sequence from the given value
1242
-     * as found in the database matching the given query conditions.
1243
-     *
1244
-     * @param mixed $current_field_value    Value used for the reference point.
1245
-     * @param null  $field_to_order_by      What field is used for the
1246
-     *                                      reference point.
1247
-     * @param int   $limit                  How many to return.
1248
-     * @param array $query_params           Extra conditions on the query.
1249
-     * @param null  $columns_to_select      If left null, then an array of
1250
-     *                                      EE_Base_Class objects is returned,
1251
-     *                                      otherwise you can indicate just the
1252
-     *                                      columns you want returned.
1253
-     * @return EE_Base_Class[]|array
1254
-     * @throws EE_Error
1255
-     */
1256
-    public function previous_x(
1257
-        $current_field_value,
1258
-        $field_to_order_by = null,
1259
-        $limit = 1,
1260
-        $query_params = array(),
1261
-        $columns_to_select = null
1262
-    ) {
1263
-        return $this->_get_consecutive(
1264
-            $current_field_value,
1265
-            '<',
1266
-            $field_to_order_by,
1267
-            $limit,
1268
-            $query_params,
1269
-            $columns_to_select
1270
-        );
1271
-    }
1272
-
1273
-
1274
-
1275
-    /**
1276
-     * Returns the next item in sequence from the given value as found in the
1277
-     * database matching the given query conditions.
1278
-     *
1279
-     * @param mixed $current_field_value    Value used for the reference point.
1280
-     * @param null  $field_to_order_by      What field is used for the
1281
-     *                                      reference point.
1282
-     * @param array $query_params           Extra conditions on the query.
1283
-     * @param null  $columns_to_select      If left null, then an EE_Base_Class
1284
-     *                                      object is returned, otherwise you
1285
-     *                                      can indicate just the columns you
1286
-     *                                      want and a single array indexed by
1287
-     *                                      the columns will be returned.
1288
-     * @return EE_Base_Class|null|array()
1289
-     * @throws EE_Error
1290
-     */
1291
-    public function next(
1292
-        $current_field_value,
1293
-        $field_to_order_by = null,
1294
-        $query_params = array(),
1295
-        $columns_to_select = null
1296
-    ) {
1297
-        $results = $this->_get_consecutive(
1298
-            $current_field_value,
1299
-            '>',
1300
-            $field_to_order_by,
1301
-            1,
1302
-            $query_params,
1303
-            $columns_to_select
1304
-        );
1305
-        return empty($results) ? null : reset($results);
1306
-    }
1307
-
1308
-
1309
-
1310
-    /**
1311
-     * Returns the previous item in sequence from the given value as found in
1312
-     * the database matching the given query conditions.
1313
-     *
1314
-     * @param mixed $current_field_value    Value used for the reference point.
1315
-     * @param null  $field_to_order_by      What field is used for the
1316
-     *                                      reference point.
1317
-     * @param array $query_params           Extra conditions on the query.
1318
-     * @param null  $columns_to_select      If left null, then an EE_Base_Class
1319
-     *                                      object is returned, otherwise you
1320
-     *                                      can indicate just the columns you
1321
-     *                                      want and a single array indexed by
1322
-     *                                      the columns will be returned.
1323
-     * @return EE_Base_Class|null|array()
1324
-     * @throws EE_Error
1325
-     */
1326
-    public function previous(
1327
-        $current_field_value,
1328
-        $field_to_order_by = null,
1329
-        $query_params = array(),
1330
-        $columns_to_select = null
1331
-    ) {
1332
-        $results = $this->_get_consecutive(
1333
-            $current_field_value,
1334
-            '<',
1335
-            $field_to_order_by,
1336
-            1,
1337
-            $query_params,
1338
-            $columns_to_select
1339
-        );
1340
-        return empty($results) ? null : reset($results);
1341
-    }
1342
-
1343
-
1344
-
1345
-    /**
1346
-     * Returns the a consecutive number of items in sequence from the given
1347
-     * value as found in the database matching the given query conditions.
1348
-     *
1349
-     * @param mixed  $current_field_value   Value used for the reference point.
1350
-     * @param string $operand               What operand is used for the sequence.
1351
-     * @param string $field_to_order_by     What field is used for the reference point.
1352
-     * @param int    $limit                 How many to return.
1353
-     * @param array  $query_params          Extra conditions on the query.
1354
-     * @param null   $columns_to_select     If left null, then an array of EE_Base_Class objects is returned,
1355
-     *                                      otherwise you can indicate just the columns you want returned.
1356
-     * @return EE_Base_Class[]|array
1357
-     * @throws EE_Error
1358
-     */
1359
-    protected function _get_consecutive(
1360
-        $current_field_value,
1361
-        $operand = '>',
1362
-        $field_to_order_by = null,
1363
-        $limit = 1,
1364
-        $query_params = array(),
1365
-        $columns_to_select = null
1366
-    ) {
1367
-        // if $field_to_order_by is empty then let's assume we're ordering by the primary key.
1368
-        if (empty($field_to_order_by)) {
1369
-            if ($this->has_primary_key_field()) {
1370
-                $field_to_order_by = $this->get_primary_key_field()->get_name();
1371
-            } else {
1372
-                if (WP_DEBUG) {
1373
-                    throw new EE_Error(__(
1374
-                        'EEM_Base::_get_consecutive() has been called with no $field_to_order_by argument and there is no primary key on the field.  Please provide the field you would like to use as the base for retrieving the next item(s).',
1375
-                        'event_espresso'
1376
-                    ));
1377
-                }
1378
-                EE_Error::add_error(__('There was an error with the query.', 'event_espresso'));
1379
-                return array();
1380
-            }
1381
-        }
1382
-        if (! is_array($query_params)) {
1383
-            EE_Error::doing_it_wrong(
1384
-                'EEM_Base::_get_consecutive',
1385
-                sprintf(
1386
-                    __('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1387
-                    gettype($query_params)
1388
-                ),
1389
-                '4.6.0'
1390
-            );
1391
-            $query_params = array();
1392
-        }
1393
-        // let's add the where query param for consecutive look up.
1394
-        $query_params[0][ $field_to_order_by ] = array($operand, $current_field_value);
1395
-        $query_params['limit'] = $limit;
1396
-        // set direction
1397
-        $incoming_orderby = isset($query_params['order_by']) ? (array) $query_params['order_by'] : array();
1398
-        $query_params['order_by'] = $operand === '>'
1399
-            ? array($field_to_order_by => 'ASC') + $incoming_orderby
1400
-            : array($field_to_order_by => 'DESC') + $incoming_orderby;
1401
-        // if $columns_to_select is empty then that means we're returning EE_Base_Class objects
1402
-        if (empty($columns_to_select)) {
1403
-            return $this->get_all($query_params);
1404
-        }
1405
-        // getting just the fields
1406
-        return $this->_get_all_wpdb_results($query_params, ARRAY_A, $columns_to_select);
1407
-    }
1408
-
1409
-
1410
-
1411
-    /**
1412
-     * This sets the _timezone property after model object has been instantiated.
1413
-     *
1414
-     * @param null | string $timezone valid PHP DateTimeZone timezone string
1415
-     */
1416
-    public function set_timezone($timezone)
1417
-    {
1418
-        if ($timezone !== null) {
1419
-            $this->_timezone = $timezone;
1420
-        }
1421
-        // note we need to loop through relations and set the timezone on those objects as well.
1422
-        foreach ($this->_model_relations as $relation) {
1423
-            $relation->set_timezone($timezone);
1424
-        }
1425
-        // and finally we do the same for any datetime fields
1426
-        foreach ($this->_fields as $field) {
1427
-            if ($field instanceof EE_Datetime_Field) {
1428
-                $field->set_timezone($timezone);
1429
-            }
1430
-        }
1431
-    }
1432
-
1433
-
1434
-
1435
-    /**
1436
-     * This just returns whatever is set for the current timezone.
1437
-     *
1438
-     * @access public
1439
-     * @return string
1440
-     */
1441
-    public function get_timezone()
1442
-    {
1443
-        // first validate if timezone is set.  If not, then let's set it be whatever is set on the model fields.
1444
-        if (empty($this->_timezone)) {
1445
-            foreach ($this->_fields as $field) {
1446
-                if ($field instanceof EE_Datetime_Field) {
1447
-                    $this->set_timezone($field->get_timezone());
1448
-                    break;
1449
-                }
1450
-            }
1451
-        }
1452
-        // if timezone STILL empty then return the default timezone for the site.
1453
-        if (empty($this->_timezone)) {
1454
-            $this->set_timezone(EEH_DTT_Helper::get_timezone());
1455
-        }
1456
-        return $this->_timezone;
1457
-    }
1458
-
1459
-
1460
-
1461
-    /**
1462
-     * This returns the date formats set for the given field name and also ensures that
1463
-     * $this->_timezone property is set correctly.
1464
-     *
1465
-     * @since 4.6.x
1466
-     * @param string $field_name The name of the field the formats are being retrieved for.
1467
-     * @param bool   $pretty     Whether to return the pretty formats (true) or not (false).
1468
-     * @throws EE_Error   If the given field_name is not of the EE_Datetime_Field type.
1469
-     * @return array formats in an array with the date format first, and the time format last.
1470
-     */
1471
-    public function get_formats_for($field_name, $pretty = false)
1472
-    {
1473
-        $field_settings = $this->field_settings_for($field_name);
1474
-        // if not a valid EE_Datetime_Field then throw error
1475
-        if (! $field_settings instanceof EE_Datetime_Field) {
1476
-            throw new EE_Error(sprintf(__(
1477
-                'The field sent into EEM_Base::get_formats_for (%s) is not registered as a EE_Datetime_Field. Please check the spelling and make sure you are submitting the right field name to retrieve date_formats for.',
1478
-                'event_espresso'
1479
-            ), $field_name));
1480
-        }
1481
-        // while we are here, let's make sure the timezone internally in EEM_Base matches what is stored on
1482
-        // the field.
1483
-        $this->_timezone = $field_settings->get_timezone();
1484
-        return array($field_settings->get_date_format($pretty), $field_settings->get_time_format($pretty));
1485
-    }
1486
-
1487
-
1488
-
1489
-    /**
1490
-     * This returns the current time in a format setup for a query on this model.
1491
-     * Usage of this method makes it easier to setup queries against EE_Datetime_Field columns because
1492
-     * it will return:
1493
-     *  - a formatted string in the timezone and format currently set on the EE_Datetime_Field for the given field for
1494
-     *  NOW
1495
-     *  - or a unix timestamp (equivalent to time())
1496
-     * Note: When requesting a formatted string, if the date or time format doesn't include seconds, for example,
1497
-     * the time returned, because it uses that format, will also NOT include seconds. For this reason, if you want
1498
-     * the time returned to be the current time down to the exact second, set $timestamp to true.
1499
-     * @since 4.6.x
1500
-     * @param string $field_name       The field the current time is needed for.
1501
-     * @param bool   $timestamp        True means to return a unix timestamp. Otherwise a
1502
-     *                                 formatted string matching the set format for the field in the set timezone will
1503
-     *                                 be returned.
1504
-     * @param string $what             Whether to return the string in just the time format, the date format, or both.
1505
-     * @throws EE_Error    If the given field_name is not of the EE_Datetime_Field type.
1506
-     * @return int|string  If the given field_name is not of the EE_Datetime_Field type, then an EE_Error
1507
-     *                                 exception is triggered.
1508
-     */
1509
-    public function current_time_for_query($field_name, $timestamp = false, $what = 'both')
1510
-    {
1511
-        $formats = $this->get_formats_for($field_name);
1512
-        $DateTime = new DateTime("now", new DateTimeZone($this->_timezone));
1513
-        if ($timestamp) {
1514
-            return $DateTime->format('U');
1515
-        }
1516
-        // not returning timestamp, so return formatted string in timezone.
1517
-        switch ($what) {
1518
-            case 'time':
1519
-                return $DateTime->format($formats[1]);
1520
-                break;
1521
-            case 'date':
1522
-                return $DateTime->format($formats[0]);
1523
-                break;
1524
-            default:
1525
-                return $DateTime->format(implode(' ', $formats));
1526
-                break;
1527
-        }
1528
-    }
1529
-
1530
-
1531
-
1532
-    /**
1533
-     * This receives a time string for a given field and ensures that it is setup to match what the internal settings
1534
-     * for the model are.  Returns a DateTime object.
1535
-     * Note: a gotcha for when you send in unix timestamp.  Remember a unix timestamp is already timezone agnostic,
1536
-     * (functionally the equivalent of UTC+0).  So when you send it in, whatever timezone string you include is
1537
-     * ignored.
1538
-     *
1539
-     * @param string $field_name      The field being setup.
1540
-     * @param string $timestring      The date time string being used.
1541
-     * @param string $incoming_format The format for the time string.
1542
-     * @param string $timezone        By default, it is assumed the incoming time string is in timezone for
1543
-     *                                the blog.  If this is not the case, then it can be specified here.  If incoming
1544
-     *                                format is
1545
-     *                                'U', this is ignored.
1546
-     * @return DateTime
1547
-     * @throws EE_Error
1548
-     */
1549
-    public function convert_datetime_for_query($field_name, $timestring, $incoming_format, $timezone = '')
1550
-    {
1551
-        // just using this to ensure the timezone is set correctly internally
1552
-        $this->get_formats_for($field_name);
1553
-        // load EEH_DTT_Helper
1554
-        $set_timezone = empty($timezone) ? EEH_DTT_Helper::get_timezone() : $timezone;
1555
-        $incomingDateTime = date_create_from_format($incoming_format, $timestring, new DateTimeZone($set_timezone));
1556
-        EEH_DTT_Helper::setTimezone($incomingDateTime, new DateTimeZone($this->_timezone));
1557
-        return \EventEspresso\core\domain\entities\DbSafeDateTime::createFromDateTime($incomingDateTime);
1558
-    }
1559
-
1560
-
1561
-
1562
-    /**
1563
-     * Gets all the tables comprising this model. Array keys are the table aliases, and values are EE_Table objects
1564
-     *
1565
-     * @return EE_Table_Base[]
1566
-     */
1567
-    public function get_tables()
1568
-    {
1569
-        return $this->_tables;
1570
-    }
1571
-
1572
-
1573
-
1574
-    /**
1575
-     * Updates all the database entries (in each table for this model) according to $fields_n_values and optionally
1576
-     * also updates all the model objects, where the criteria expressed in $query_params are met..
1577
-     * Also note: if this model has multiple tables, this update verifies all the secondary tables have an entry for
1578
-     * each row (in the primary table) we're trying to update; if not, it inserts an entry in the secondary table. Eg:
1579
-     * if our model has 2 tables: wp_posts (primary), and wp_esp_event (secondary). Let's say we are trying to update a
1580
-     * model object with EVT_ID = 1
1581
-     * (which means where wp_posts has ID = 1, because wp_posts.ID is the primary key's column), which exists, but
1582
-     * there is no entry in wp_esp_event for this entry in wp_posts. So, this update script will insert a row into
1583
-     * wp_esp_event, using any available parameters from $fields_n_values (eg, if "EVT_limit" => 40 is in
1584
-     * $fields_n_values, the new entry in wp_esp_event will set EVT_limit = 40, and use default for other columns which
1585
-     * are not specified)
1586
-     *
1587
-     * @param array   $fields_n_values         keys are model fields (exactly like keys in EEM_Base::_fields, NOT db
1588
-     *                                         columns!), values are strings, ints, floats, and maybe arrays if they
1589
-     *                                         are to be serialized. Basically, the values are what you'd expect to be
1590
-     *                                         values on the model, NOT necessarily what's in the DB. For example, if
1591
-     *                                         we wanted to update only the TXN_details on any Transactions where its
1592
-     *                                         ID=34, we'd use this method as follows:
1593
-     *                                         EEM_Transaction::instance()->update(
1594
-     *                                         array('TXN_details'=>array('detail1'=>'monkey','detail2'=>'banana'),
1595
-     *                                         array(array('TXN_ID'=>34)));
1596
-     * @param array   $query_params            @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1597
-     *                                         Eg, consider updating Question's QST_admin_label field is of type
1598
-     *                                         Simple_HTML. If you use this function to update that field to $new_value
1599
-     *                                         = (note replace 8's with appropriate opening and closing tags in the
1600
-     *                                         following example)"8script8alert('I hack all');8/script88b8boom
1601
-     *                                         baby8/b8", then if you set $values_already_prepared_by_model_object to
1602
-     *                                         TRUE, it is assumed that you've already called
1603
-     *                                         EE_Simple_HTML_Field->prepare_for_set($new_value), which removes the
1604
-     *                                         malicious javascript. However, if
1605
-     *                                         $values_already_prepared_by_model_object is left as FALSE, then
1606
-     *                                         EE_Simple_HTML_Field->prepare_for_set($new_value) will be called on it,
1607
-     *                                         and every other field, before insertion. We provide this parameter
1608
-     *                                         because model objects perform their prepare_for_set function on all
1609
-     *                                         their values, and so don't need to be called again (and in many cases,
1610
-     *                                         shouldn't be called again. Eg: if we escape HTML characters in the
1611
-     *                                         prepare_for_set method...)
1612
-     * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
1613
-     *                                         in this model's entity map according to $fields_n_values that match
1614
-     *                                         $query_params. This obviously has some overhead, so you can disable it
1615
-     *                                         by setting this to FALSE, but be aware that model objects being used
1616
-     *                                         could get out-of-sync with the database
1617
-     * @return int how many rows got updated or FALSE if something went wrong with the query (wp returns FALSE or num
1618
-     *                                         rows affected which *could* include 0 which DOES NOT mean the query was
1619
-     *                                         bad)
1620
-     * @throws EE_Error
1621
-     */
1622
-    public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
1623
-    {
1624
-        if (! is_array($query_params)) {
1625
-            EE_Error::doing_it_wrong(
1626
-                'EEM_Base::update',
1627
-                sprintf(
1628
-                    __('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1629
-                    gettype($query_params)
1630
-                ),
1631
-                '4.6.0'
1632
-            );
1633
-            $query_params = array();
1634
-        }
1635
-        /**
1636
-         * Action called before a model update call has been made.
1637
-         *
1638
-         * @param EEM_Base $model
1639
-         * @param array    $fields_n_values the updated fields and their new values
1640
-         * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1641
-         */
1642
-        do_action('AHEE__EEM_Base__update__begin', $this, $fields_n_values, $query_params);
1643
-        /**
1644
-         * Filters the fields about to be updated given the query parameters. You can provide the
1645
-         * $query_params to $this->get_all() to find exactly which records will be updated
1646
-         *
1647
-         * @param array    $fields_n_values fields and their new values
1648
-         * @param EEM_Base $model           the model being queried
1649
-         * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1650
-         */
1651
-        $fields_n_values = (array) apply_filters(
1652
-            'FHEE__EEM_Base__update__fields_n_values',
1653
-            $fields_n_values,
1654
-            $this,
1655
-            $query_params
1656
-        );
1657
-        // need to verify that, for any entry we want to update, there are entries in each secondary table.
1658
-        // to do that, for each table, verify that it's PK isn't null.
1659
-        $tables = $this->get_tables();
1660
-        // and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1661
-        // NOTE: we should make this code more efficient by NOT querying twice
1662
-        // before the real update, but that needs to first go through ALPHA testing
1663
-        // as it's dangerous. says Mike August 8 2014
1664
-        // we want to make sure the default_where strategy is ignored
1665
-        $this->_ignore_where_strategy = true;
1666
-        $wpdb_select_results = $this->_get_all_wpdb_results($query_params);
1667
-        foreach ($wpdb_select_results as $wpdb_result) {
1668
-            // type cast stdClass as array
1669
-            $wpdb_result = (array) $wpdb_result;
1670
-            // get the model object's PK, as we'll want this if we need to insert a row into secondary tables
1671
-            if ($this->has_primary_key_field()) {
1672
-                $main_table_pk_value = $wpdb_result[ $this->get_primary_key_field()->get_qualified_column() ];
1673
-            } else {
1674
-                // if there's no primary key, we basically can't support having a 2nd table on the model (we could but it would be lots of work)
1675
-                $main_table_pk_value = null;
1676
-            }
1677
-            // if there are more than 1 tables, we'll want to verify that each table for this model has an entry in the other tables
1678
-            // and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1679
-            if (count($tables) > 1) {
1680
-                // foreach matching row in the DB, ensure that each table's PK isn't null. If so, there must not be an entry
1681
-                // in that table, and so we'll want to insert one
1682
-                foreach ($tables as $table_obj) {
1683
-                    $this_table_pk_column = $table_obj->get_fully_qualified_pk_column();
1684
-                    // if there is no private key for this table on the results, it means there's no entry
1685
-                    // in this table, right? so insert a row in the current table, using any fields available
1686
-                    if (
1687
-                        ! (array_key_exists($this_table_pk_column, $wpdb_result)
1688
-                           && $wpdb_result[ $this_table_pk_column ])
1689
-                    ) {
1690
-                        $success = $this->_insert_into_specific_table(
1691
-                            $table_obj,
1692
-                            $fields_n_values,
1693
-                            $main_table_pk_value
1694
-                        );
1695
-                        // if we died here, report the error
1696
-                        if (! $success) {
1697
-                            return false;
1698
-                        }
1699
-                    }
1700
-                }
1701
-            }
1702
-            //              //and now check that if we have cached any models by that ID on the model, that
1703
-            //              //they also get updated properly
1704
-            //              $model_object = $this->get_from_entity_map( $main_table_pk_value );
1705
-            //              if( $model_object ){
1706
-            //                  foreach( $fields_n_values as $field => $value ){
1707
-            //                      $model_object->set($field, $value);
1708
-            // let's make sure default_where strategy is followed now
1709
-            $this->_ignore_where_strategy = false;
1710
-        }
1711
-        // if we want to keep model objects in sync, AND
1712
-        // if this wasn't called from a model object (to update itself)
1713
-        // then we want to make sure we keep all the existing
1714
-        // model objects in sync with the db
1715
-        if ($keep_model_objs_in_sync && ! $this->_values_already_prepared_by_model_object) {
1716
-            if ($this->has_primary_key_field()) {
1717
-                $model_objs_affected_ids = $this->get_col($query_params);
1718
-            } else {
1719
-                // we need to select a bunch of columns and then combine them into the the "index primary key string"s
1720
-                $models_affected_key_columns = $this->_get_all_wpdb_results($query_params, ARRAY_A);
1721
-                $model_objs_affected_ids = array();
1722
-                foreach ($models_affected_key_columns as $row) {
1723
-                    $combined_index_key = $this->get_index_primary_key_string($row);
1724
-                    $model_objs_affected_ids[ $combined_index_key ] = $combined_index_key;
1725
-                }
1726
-            }
1727
-            if (! $model_objs_affected_ids) {
1728
-                // wait wait wait- if nothing was affected let's stop here
1729
-                return 0;
1730
-            }
1731
-            foreach ($model_objs_affected_ids as $id) {
1732
-                $model_obj_in_entity_map = $this->get_from_entity_map($id);
1733
-                if ($model_obj_in_entity_map) {
1734
-                    foreach ($fields_n_values as $field => $new_value) {
1735
-                        $model_obj_in_entity_map->set($field, $new_value);
1736
-                    }
1737
-                }
1738
-            }
1739
-            // if there is a primary key on this model, we can now do a slight optimization
1740
-            if ($this->has_primary_key_field()) {
1741
-                // we already know what we want to update. So let's make the query simpler so it's a little more efficient
1742
-                $query_params = array(
1743
-                    array($this->primary_key_name() => array('IN', $model_objs_affected_ids)),
1744
-                    'limit'                    => count($model_objs_affected_ids),
1745
-                    'default_where_conditions' => EEM_Base::default_where_conditions_none,
1746
-                );
1747
-            }
1748
-        }
1749
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
1750
-        $SQL = "UPDATE "
1751
-               . $model_query_info->get_full_join_sql()
1752
-               . " SET "
1753
-               . $this->_construct_update_sql($fields_n_values)
1754
-               . $model_query_info->get_where_sql();// note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1755
-        $rows_affected = $this->_do_wpdb_query('query', array($SQL));
1756
-        /**
1757
-         * Action called after a model update call has been made.
1758
-         *
1759
-         * @param EEM_Base $model
1760
-         * @param array    $fields_n_values the updated fields and their new values
1761
-         * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1762
-         * @param int      $rows_affected
1763
-         */
1764
-        do_action('AHEE__EEM_Base__update__end', $this, $fields_n_values, $query_params, $rows_affected);
1765
-        return $rows_affected;// how many supposedly got updated
1766
-    }
1767
-
1768
-
1769
-
1770
-    /**
1771
-     * Analogous to $wpdb->get_col, returns a 1-dimensional array where teh values
1772
-     * are teh values of the field specified (or by default the primary key field)
1773
-     * that matched the query params. Note that you should pass the name of the
1774
-     * model FIELD, not the database table's column name.
1775
-     *
1776
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1777
-     * @param string $field_to_select
1778
-     * @return array just like $wpdb->get_col()
1779
-     * @throws EE_Error
1780
-     */
1781
-    public function get_col($query_params = array(), $field_to_select = null)
1782
-    {
1783
-        if ($field_to_select) {
1784
-            $field = $this->field_settings_for($field_to_select);
1785
-        } elseif ($this->has_primary_key_field()) {
1786
-            $field = $this->get_primary_key_field();
1787
-        } else {
1788
-            // no primary key, just grab the first column
1789
-            $field = reset($this->field_settings());
1790
-        }
1791
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
1792
-        $select_expressions = $field->get_qualified_column();
1793
-        $SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1794
-        return $this->_do_wpdb_query('get_col', array($SQL));
1795
-    }
1796
-
1797
-
1798
-
1799
-    /**
1800
-     * Returns a single column value for a single row from the database
1801
-     *
1802
-     * @param array  $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1803
-     * @param string $field_to_select @see EEM_Base::get_col()
1804
-     * @return string
1805
-     * @throws EE_Error
1806
-     */
1807
-    public function get_var($query_params = array(), $field_to_select = null)
1808
-    {
1809
-        $query_params['limit'] = 1;
1810
-        $col = $this->get_col($query_params, $field_to_select);
1811
-        if (! empty($col)) {
1812
-            return reset($col);
1813
-        }
1814
-        return null;
1815
-    }
1816
-
1817
-
1818
-
1819
-    /**
1820
-     * Makes the SQL for after "UPDATE table_X inner join table_Y..." and before "...WHERE". Eg "Question.name='party
1821
-     * time?', Question.desc='what do you think?',..." Values are filtered through wpdb->prepare to avoid against SQL
1822
-     * injection, but currently no further filtering is done
1823
-     *
1824
-     * @global      $wpdb
1825
-     * @param array $fields_n_values array keys are field names on this model, and values are what those fields should
1826
-     *                               be updated to in the DB
1827
-     * @return string of SQL
1828
-     * @throws EE_Error
1829
-     */
1830
-    public function _construct_update_sql($fields_n_values)
1831
-    {
1832
-        /** @type WPDB $wpdb */
1833
-        global $wpdb;
1834
-        $cols_n_values = array();
1835
-        foreach ($fields_n_values as $field_name => $value) {
1836
-            $field_obj = $this->field_settings_for($field_name);
1837
-            // if the value is NULL, we want to assign the value to that.
1838
-            // wpdb->prepare doesn't really handle that properly
1839
-            $prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
1840
-            $value_sql = $prepared_value === null ? 'NULL'
1841
-                : $wpdb->prepare($field_obj->get_wpdb_data_type(), $prepared_value);
1842
-            $cols_n_values[] = $field_obj->get_qualified_column() . "=" . $value_sql;
1843
-        }
1844
-        return implode(",", $cols_n_values);
1845
-    }
1846
-
1847
-
1848
-
1849
-    /**
1850
-     * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1851
-     * Performs a HARD delete, meaning the database row should always be removed,
1852
-     * not just have a flag field on it switched
1853
-     * Wrapper for EEM_Base::delete_permanently()
1854
-     *
1855
-     * @param mixed $id
1856
-     * @param boolean $allow_blocking
1857
-     * @return int the number of rows deleted
1858
-     * @throws EE_Error
1859
-     */
1860
-    public function delete_permanently_by_ID($id, $allow_blocking = true)
1861
-    {
1862
-        return $this->delete_permanently(
1863
-            array(
1864
-                array($this->get_primary_key_field()->get_name() => $id),
1865
-                'limit' => 1,
1866
-            ),
1867
-            $allow_blocking
1868
-        );
1869
-    }
1870
-
1871
-
1872
-
1873
-    /**
1874
-     * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1875
-     * Wrapper for EEM_Base::delete()
1876
-     *
1877
-     * @param mixed $id
1878
-     * @param boolean $allow_blocking
1879
-     * @return int the number of rows deleted
1880
-     * @throws EE_Error
1881
-     */
1882
-    public function delete_by_ID($id, $allow_blocking = true)
1883
-    {
1884
-        return $this->delete(
1885
-            array(
1886
-                array($this->get_primary_key_field()->get_name() => $id),
1887
-                'limit' => 1,
1888
-            ),
1889
-            $allow_blocking
1890
-        );
1891
-    }
1892
-
1893
-
1894
-
1895
-    /**
1896
-     * Identical to delete_permanently, but does a "soft" delete if possible,
1897
-     * meaning if the model has a field that indicates its been "trashed" or
1898
-     * "soft deleted", we will just set that instead of actually deleting the rows.
1899
-     *
1900
-     * @see EEM_Base::delete_permanently
1901
-     * @param array   $query_params
1902
-     * @param boolean $allow_blocking
1903
-     * @return int how many rows got deleted
1904
-     * @throws EE_Error
1905
-     */
1906
-    public function delete($query_params, $allow_blocking = true)
1907
-    {
1908
-        return $this->delete_permanently($query_params, $allow_blocking);
1909
-    }
1910
-
1911
-
1912
-
1913
-    /**
1914
-     * Deletes the model objects that meet the query params. Note: this method is overridden
1915
-     * in EEM_Soft_Delete_Base so that soft-deleted model objects are instead only flagged
1916
-     * as archived, not actually deleted
1917
-     *
1918
-     * @param array   $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1919
-     * @param boolean $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
1920
-     *                                that blocks it (ie, there' sno other data that depends on this data); if false,
1921
-     *                                deletes regardless of other objects which may depend on it. Its generally
1922
-     *                                advisable to always leave this as TRUE, otherwise you could easily corrupt your
1923
-     *                                DB
1924
-     * @return int how many rows got deleted
1925
-     * @throws EE_Error
1926
-     */
1927
-    public function delete_permanently($query_params, $allow_blocking = true)
1928
-    {
1929
-        /**
1930
-         * Action called just before performing a real deletion query. You can use the
1931
-         * model and its $query_params to find exactly which items will be deleted
1932
-         *
1933
-         * @param EEM_Base $model
1934
-         * @param array    $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1935
-         * @param boolean  $allow_blocking whether or not to allow related model objects
1936
-         *                                 to block (prevent) this deletion
1937
-         */
1938
-        do_action('AHEE__EEM_Base__delete__begin', $this, $query_params, $allow_blocking);
1939
-        // some MySQL databases may be running safe mode, which may restrict
1940
-        // deletion if there is no KEY column used in the WHERE statement of a deletion.
1941
-        // to get around this, we first do a SELECT, get all the IDs, and then run another query
1942
-        // to delete them
1943
-        $items_for_deletion = $this->_get_all_wpdb_results($query_params);
1944
-        $columns_and_ids_for_deleting = $this->_get_ids_for_delete($items_for_deletion, $allow_blocking);
1945
-        $deletion_where_query_part = $this->_build_query_part_for_deleting_from_columns_and_values(
1946
-            $columns_and_ids_for_deleting
1947
-        );
1948
-        /**
1949
-         * Allows client code to act on the items being deleted before the query is actually executed.
1950
-         *
1951
-         * @param EEM_Base $this  The model instance being acted on.
1952
-         * @param array    $query_params  The incoming array of query parameters influencing what gets deleted.
1953
-         * @param bool     $allow_blocking @see param description in method phpdoc block.
1954
-         * @param array $columns_and_ids_for_deleting       An array indicating what entities will get removed as
1955
-         *                                                  derived from the incoming query parameters.
1956
-         *                                                  @see details on the structure of this array in the phpdocs
1957
-         *                                                  for the `_get_ids_for_delete_method`
1958
-         *
1959
-         */
1960
-        do_action(
1961
-            'AHEE__EEM_Base__delete__before_query',
1962
-            $this,
1963
-            $query_params,
1964
-            $allow_blocking,
1965
-            $columns_and_ids_for_deleting
1966
-        );
1967
-        if ($deletion_where_query_part) {
1968
-            $model_query_info = $this->_create_model_query_info_carrier($query_params);
1969
-            $table_aliases = array_keys($this->_tables);
1970
-            $SQL = "DELETE "
1971
-                   . implode(", ", $table_aliases)
1972
-                   . " FROM "
1973
-                   . $model_query_info->get_full_join_sql()
1974
-                   . " WHERE "
1975
-                   . $deletion_where_query_part;
1976
-            $rows_deleted = $this->_do_wpdb_query('query', array($SQL));
1977
-        } else {
1978
-            $rows_deleted = 0;
1979
-        }
1980
-
1981
-        // Next, make sure those items are removed from the entity map; if they could be put into it at all; and if
1982
-        // there was no error with the delete query.
1983
-        if (
1984
-            $this->has_primary_key_field()
1985
-            && $rows_deleted !== false
1986
-            && isset($columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ])
1987
-        ) {
1988
-            $ids_for_removal = $columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ];
1989
-            foreach ($ids_for_removal as $id) {
1990
-                if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
1991
-                    unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
1992
-                }
1993
-            }
1994
-
1995
-            // delete any extra meta attached to the deleted entities but ONLY if this model is not an instance of
1996
-            // `EEM_Extra_Meta`.  In other words we want to prevent recursion on EEM_Extra_Meta::delete_permanently calls
1997
-            // unnecessarily.  It's very unlikely that users will have assigned Extra Meta to Extra Meta
1998
-            // (although it is possible).
1999
-            // Note this can be skipped by using the provided filter and returning false.
2000
-            if (
2001
-                apply_filters(
2002
-                    'FHEE__EEM_Base__delete_permanently__dont_delete_extra_meta_for_extra_meta',
2003
-                    ! $this instanceof EEM_Extra_Meta,
2004
-                    $this
2005
-                )
2006
-            ) {
2007
-                EEM_Extra_Meta::instance()->delete_permanently(array(
2008
-                    0 => array(
2009
-                        'EXM_type' => $this->get_this_model_name(),
2010
-                        'OBJ_ID'   => array(
2011
-                            'IN',
2012
-                            $ids_for_removal
2013
-                        )
2014
-                    )
2015
-                ));
2016
-            }
2017
-        }
2018
-
2019
-        /**
2020
-         * Action called just after performing a real deletion query. Although at this point the
2021
-         * items should have been deleted
2022
-         *
2023
-         * @param EEM_Base $model
2024
-         * @param array    $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2025
-         * @param int      $rows_deleted
2026
-         */
2027
-        do_action('AHEE__EEM_Base__delete__end', $this, $query_params, $rows_deleted, $columns_and_ids_for_deleting);
2028
-        return $rows_deleted;// how many supposedly got deleted
2029
-    }
2030
-
2031
-
2032
-
2033
-    /**
2034
-     * Checks all the relations that throw error messages when there are blocking related objects
2035
-     * for related model objects. If there are any related model objects on those relations,
2036
-     * adds an EE_Error, and return true
2037
-     *
2038
-     * @param EE_Base_Class|int $this_model_obj_or_id
2039
-     * @param EE_Base_Class     $ignore_this_model_obj a model object like 'EE_Event', or 'EE_Term_Taxonomy', which
2040
-     *                                                 should be ignored when determining whether there are related
2041
-     *                                                 model objects which block this model object's deletion. Useful
2042
-     *                                                 if you know A is related to B and are considering deleting A,
2043
-     *                                                 but want to see if A has any other objects blocking its deletion
2044
-     *                                                 before removing the relation between A and B
2045
-     * @return boolean
2046
-     * @throws EE_Error
2047
-     */
2048
-    public function delete_is_blocked_by_related_models($this_model_obj_or_id, $ignore_this_model_obj = null)
2049
-    {
2050
-        // first, if $ignore_this_model_obj was supplied, get its model
2051
-        if ($ignore_this_model_obj && $ignore_this_model_obj instanceof EE_Base_Class) {
2052
-            $ignored_model = $ignore_this_model_obj->get_model();
2053
-        } else {
2054
-            $ignored_model = null;
2055
-        }
2056
-        // now check all the relations of $this_model_obj_or_id and see if there
2057
-        // are any related model objects blocking it?
2058
-        $is_blocked = false;
2059
-        foreach ($this->_model_relations as $relation_name => $relation_obj) {
2060
-            if ($relation_obj->block_delete_if_related_models_exist()) {
2061
-                // if $ignore_this_model_obj was supplied, then for the query
2062
-                // on that model needs to be told to ignore $ignore_this_model_obj
2063
-                if ($ignored_model && $relation_name === $ignored_model->get_this_model_name()) {
2064
-                    $related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id, array(
2065
-                        array(
2066
-                            $ignored_model->get_primary_key_field()->get_name() => array(
2067
-                                '!=',
2068
-                                $ignore_this_model_obj->ID(),
2069
-                            ),
2070
-                        ),
2071
-                    ));
2072
-                } else {
2073
-                    $related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id);
2074
-                }
2075
-                if ($related_model_objects) {
2076
-                    EE_Error::add_error($relation_obj->get_deletion_error_message(), __FILE__, __FUNCTION__, __LINE__);
2077
-                    $is_blocked = true;
2078
-                }
2079
-            }
2080
-        }
2081
-        return $is_blocked;
2082
-    }
2083
-
2084
-
2085
-    /**
2086
-     * Builds the columns and values for items to delete from the incoming $row_results_for_deleting array.
2087
-     * @param array $row_results_for_deleting
2088
-     * @param bool  $allow_blocking
2089
-     * @return array   The shape of this array depends on whether the model `has_primary_key_field` or not.  If the
2090
-     *                 model DOES have a primary_key_field, then the array will be a simple single dimension array where
2091
-     *                 the key is the fully qualified primary key column and the value is an array of ids that will be
2092
-     *                 deleted. Example:
2093
-     *                      array('Event.EVT_ID' => array( 1,2,3))
2094
-     *                 If the model DOES NOT have a primary_key_field, then the array will be a two dimensional array
2095
-     *                 where each element is a group of columns and values that get deleted. Example:
2096
-     *                      array(
2097
-     *                          0 => array(
2098
-     *                              'Term_Relationship.object_id' => 1
2099
-     *                              'Term_Relationship.term_taxonomy_id' => 5
2100
-     *                          ),
2101
-     *                          1 => array(
2102
-     *                              'Term_Relationship.object_id' => 1
2103
-     *                              'Term_Relationship.term_taxonomy_id' => 6
2104
-     *                          )
2105
-     *                      )
2106
-     * @throws EE_Error
2107
-     */
2108
-    protected function _get_ids_for_delete(array $row_results_for_deleting, $allow_blocking = true)
2109
-    {
2110
-        $ids_to_delete_indexed_by_column = array();
2111
-        if ($this->has_primary_key_field()) {
2112
-            $primary_table = $this->_get_main_table();
2113
-            $primary_table_pk_field = $this->get_field_by_column($primary_table->get_fully_qualified_pk_column());
2114
-            $other_tables = $this->_get_other_tables();
2115
-            $ids_to_delete_indexed_by_column = $query = array();
2116
-            foreach ($row_results_for_deleting as $item_to_delete) {
2117
-                // before we mark this item for deletion,
2118
-                // make sure there's no related entities blocking its deletion (if we're checking)
2119
-                if (
2120
-                    $allow_blocking
2121
-                    && $this->delete_is_blocked_by_related_models(
2122
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ]
2123
-                    )
2124
-                ) {
2125
-                    continue;
2126
-                }
2127
-                // primary table deletes
2128
-                if (isset($item_to_delete[ $primary_table->get_fully_qualified_pk_column() ])) {
2129
-                    $ids_to_delete_indexed_by_column[ $primary_table->get_fully_qualified_pk_column() ][] =
2130
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ];
2131
-                }
2132
-            }
2133
-        } elseif (count($this->get_combined_primary_key_fields()) > 1) {
2134
-            $fields = $this->get_combined_primary_key_fields();
2135
-            foreach ($row_results_for_deleting as $item_to_delete) {
2136
-                $ids_to_delete_indexed_by_column_for_row = array();
2137
-                foreach ($fields as $cpk_field) {
2138
-                    if ($cpk_field instanceof EE_Model_Field_Base) {
2139
-                        $ids_to_delete_indexed_by_column_for_row[ $cpk_field->get_qualified_column() ] =
2140
-                            $item_to_delete[ $cpk_field->get_qualified_column() ];
2141
-                    }
2142
-                }
2143
-                $ids_to_delete_indexed_by_column[] = $ids_to_delete_indexed_by_column_for_row;
2144
-            }
2145
-        } else {
2146
-            // so there's no primary key and no combined key...
2147
-            // sorry, can't help you
2148
-            throw new EE_Error(
2149
-                sprintf(
2150
-                    __(
2151
-                        "Cannot delete objects of type %s because there is no primary key NOR combined key",
2152
-                        "event_espresso"
2153
-                    ),
2154
-                    get_class($this)
2155
-                )
2156
-            );
2157
-        }
2158
-        return $ids_to_delete_indexed_by_column;
2159
-    }
2160
-
2161
-
2162
-    /**
2163
-     * This receives an array of columns and values set to be deleted (as prepared by _get_ids_for_delete) and prepares
2164
-     * the corresponding query_part for the query performing the delete.
2165
-     *
2166
-     * @param array $ids_to_delete_indexed_by_column @see _get_ids_for_delete for how this array might be shaped.
2167
-     * @return string
2168
-     * @throws EE_Error
2169
-     */
2170
-    protected function _build_query_part_for_deleting_from_columns_and_values(array $ids_to_delete_indexed_by_column)
2171
-    {
2172
-        $query_part = '';
2173
-        if (empty($ids_to_delete_indexed_by_column)) {
2174
-            return $query_part;
2175
-        } elseif ($this->has_primary_key_field()) {
2176
-            $query = array();
2177
-            foreach ($ids_to_delete_indexed_by_column as $column => $ids) {
2178
-                $query[] = $column . ' IN' . $this->_construct_in_value($ids, $this->_primary_key_field);
2179
-            }
2180
-            $query_part = ! empty($query) ? implode(' AND ', $query) : $query_part;
2181
-        } elseif (count($this->get_combined_primary_key_fields()) > 1) {
2182
-            $ways_to_identify_a_row = array();
2183
-            foreach ($ids_to_delete_indexed_by_column as $ids_to_delete_indexed_by_column_for_each_row) {
2184
-                $values_for_each_combined_primary_key_for_a_row = array();
2185
-                foreach ($ids_to_delete_indexed_by_column_for_each_row as $column => $id) {
2186
-                    $values_for_each_combined_primary_key_for_a_row[] = $column . '=' . $id;
2187
-                }
2188
-                $ways_to_identify_a_row[] = '('
2189
-                                            . implode(' AND ', $values_for_each_combined_primary_key_for_a_row)
2190
-                                            . ')';
2191
-            }
2192
-            $query_part = implode(' OR ', $ways_to_identify_a_row);
2193
-        }
2194
-        return $query_part;
2195
-    }
2196
-
2197
-
2198
-
2199
-    /**
2200
-     * Gets the model field by the fully qualified name
2201
-     * @param string $qualified_column_name eg 'Event_CPT.post_name' or $field_obj->get_qualified_column()
2202
-     * @return EE_Model_Field_Base
2203
-     */
2204
-    public function get_field_by_column($qualified_column_name)
2205
-    {
2206
-        foreach ($this->field_settings(true) as $field_name => $field_obj) {
2207
-            if ($field_obj->get_qualified_column() === $qualified_column_name) {
2208
-                return $field_obj;
2209
-            }
2210
-        }
2211
-        throw new EE_Error(
2212
-            sprintf(
2213
-                esc_html__('Could not find a field on the model "%1$s" for qualified column "%2$s"', 'event_espresso'),
2214
-                $this->get_this_model_name(),
2215
-                $qualified_column_name
2216
-            )
2217
-        );
2218
-    }
2219
-
2220
-
2221
-
2222
-    /**
2223
-     * Count all the rows that match criteria the model query params.
2224
-     * If $field_to_count isn't provided, the model's primary key is used. Otherwise, we count by field_to_count's
2225
-     * column
2226
-     *
2227
-     * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2228
-     * @param string $field_to_count field on model to count by (not column name)
2229
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2230
-     *                               that by the setting $distinct to TRUE;
2231
-     * @return int
2232
-     * @throws EE_Error
2233
-     */
2234
-    public function count($query_params = array(), $field_to_count = null, $distinct = false)
2235
-    {
2236
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
2237
-        if ($field_to_count) {
2238
-            $field_obj = $this->field_settings_for($field_to_count);
2239
-            $column_to_count = $field_obj->get_qualified_column();
2240
-        } elseif ($this->has_primary_key_field()) {
2241
-            $pk_field_obj = $this->get_primary_key_field();
2242
-            $column_to_count = $pk_field_obj->get_qualified_column();
2243
-        } else {
2244
-            // there's no primary key
2245
-            // if we're counting distinct items, and there's no primary key,
2246
-            // we need to list out the columns for distinction;
2247
-            // otherwise we can just use star
2248
-            if ($distinct) {
2249
-                $columns_to_use = array();
2250
-                foreach ($this->get_combined_primary_key_fields() as $field_obj) {
2251
-                    $columns_to_use[] = $field_obj->get_qualified_column();
2252
-                }
2253
-                $column_to_count = implode(',', $columns_to_use);
2254
-            } else {
2255
-                $column_to_count = '*';
2256
-            }
2257
-        }
2258
-        $column_to_count = $distinct ? "DISTINCT " . $column_to_count : $column_to_count;
2259
-        $SQL = "SELECT COUNT(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2260
-        return (int) $this->_do_wpdb_query('get_var', array($SQL));
2261
-    }
2262
-
2263
-
2264
-
2265
-    /**
2266
-     * Sums up the value of the $field_to_sum (defaults to the primary key, which isn't terribly useful)
2267
-     *
2268
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2269
-     * @param string $field_to_sum name of field (array key in $_fields array)
2270
-     * @return float
2271
-     * @throws EE_Error
2272
-     */
2273
-    public function sum($query_params, $field_to_sum = null)
2274
-    {
2275
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
2276
-        if ($field_to_sum) {
2277
-            $field_obj = $this->field_settings_for($field_to_sum);
2278
-        } else {
2279
-            $field_obj = $this->get_primary_key_field();
2280
-        }
2281
-        $column_to_count = $field_obj->get_qualified_column();
2282
-        $SQL = "SELECT SUM(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2283
-        $return_value = $this->_do_wpdb_query('get_var', array($SQL));
2284
-        $data_type = $field_obj->get_wpdb_data_type();
2285
-        if ($data_type === '%d' || $data_type === '%s') {
2286
-            return (float) $return_value;
2287
-        }
2288
-        // must be %f
2289
-        return (float) $return_value;
2290
-    }
2291
-
2292
-
2293
-
2294
-    /**
2295
-     * Just calls the specified method on $wpdb with the given arguments
2296
-     * Consolidates a little extra error handling code
2297
-     *
2298
-     * @param string $wpdb_method
2299
-     * @param array  $arguments_to_provide
2300
-     * @throws EE_Error
2301
-     * @global wpdb  $wpdb
2302
-     * @return mixed
2303
-     */
2304
-    protected function _do_wpdb_query($wpdb_method, $arguments_to_provide)
2305
-    {
2306
-        // if we're in maintenance mode level 2, DON'T run any queries
2307
-        // because level 2 indicates the database needs updating and
2308
-        // is probably out of sync with the code
2309
-        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
2310
-            throw new EE_Error(sprintf(__(
2311
-                "Event Espresso Level 2 Maintenance mode is active. That means EE can not run ANY database queries until the necessary migration scripts have run which will take EE out of maintenance mode level 2. Please inform support of this error.",
2312
-                "event_espresso"
2313
-            )));
2314
-        }
2315
-        /** @type WPDB $wpdb */
2316
-        global $wpdb;
2317
-        if (! method_exists($wpdb, $wpdb_method)) {
2318
-            throw new EE_Error(sprintf(__(
2319
-                'There is no method named "%s" on Wordpress\' $wpdb object',
2320
-                'event_espresso'
2321
-            ), $wpdb_method));
2322
-        }
2323
-        if (WP_DEBUG) {
2324
-            $old_show_errors_value = $wpdb->show_errors;
2325
-            $wpdb->show_errors(false);
2326
-        }
2327
-        $result = $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2328
-        $this->show_db_query_if_previously_requested($wpdb->last_query);
2329
-        if (WP_DEBUG) {
2330
-            $wpdb->show_errors($old_show_errors_value);
2331
-            if (! empty($wpdb->last_error)) {
2332
-                throw new EE_Error(sprintf(__('WPDB Error: "%s"', 'event_espresso'), $wpdb->last_error));
2333
-            }
2334
-            if ($result === false) {
2335
-                throw new EE_Error(sprintf(__(
2336
-                    'WPDB Error occurred, but no error message was logged by wpdb! The wpdb method called was "%1$s" and the arguments were "%2$s"',
2337
-                    'event_espresso'
2338
-                ), $wpdb_method, var_export($arguments_to_provide, true)));
2339
-            }
2340
-        } elseif ($result === false) {
2341
-            EE_Error::add_error(
2342
-                sprintf(
2343
-                    __(
2344
-                        'A database error has occurred. Turn on WP_DEBUG for more information.||A database error occurred doing wpdb method "%1$s", with arguments "%2$s". The error was "%3$s"',
2345
-                        'event_espresso'
2346
-                    ),
2347
-                    $wpdb_method,
2348
-                    var_export($arguments_to_provide, true),
2349
-                    $wpdb->last_error
2350
-                ),
2351
-                __FILE__,
2352
-                __FUNCTION__,
2353
-                __LINE__
2354
-            );
2355
-        }
2356
-        return $result;
2357
-    }
2358
-
2359
-
2360
-
2361
-    /**
2362
-     * Attempts to run the indicated WPDB method with the provided arguments,
2363
-     * and if there's an error tries to verify the DB is correct. Uses
2364
-     * the static property EEM_Base::$_db_verification_level to determine whether
2365
-     * we should try to fix the EE core db, the addons, or just give up
2366
-     *
2367
-     * @param string $wpdb_method
2368
-     * @param array  $arguments_to_provide
2369
-     * @return mixed
2370
-     */
2371
-    private function _process_wpdb_query($wpdb_method, $arguments_to_provide)
2372
-    {
2373
-        /** @type WPDB $wpdb */
2374
-        global $wpdb;
2375
-        $wpdb->last_error = null;
2376
-        $result = call_user_func_array(array($wpdb, $wpdb_method), $arguments_to_provide);
2377
-        // was there an error running the query? but we don't care on new activations
2378
-        // (we're going to setup the DB anyway on new activations)
2379
-        if (
2380
-            ($result === false || ! empty($wpdb->last_error))
2381
-            && EE_System::instance()->detect_req_type() !== EE_System::req_type_new_activation
2382
-        ) {
2383
-            switch (EEM_Base::$_db_verification_level) {
2384
-                case EEM_Base::db_verified_none:
2385
-                    // let's double-check core's DB
2386
-                    $error_message = $this->_verify_core_db($wpdb_method, $arguments_to_provide);
2387
-                    break;
2388
-                case EEM_Base::db_verified_core:
2389
-                    // STILL NO LOVE?? verify all the addons too. Maybe they need to be fixed
2390
-                    $error_message = $this->_verify_addons_db($wpdb_method, $arguments_to_provide);
2391
-                    break;
2392
-                case EEM_Base::db_verified_addons:
2393
-                    // ummmm... you in trouble
2394
-                    return $result;
2395
-                    break;
2396
-            }
2397
-            if (! empty($error_message)) {
2398
-                EE_Log::instance()->log(__FILE__, __FUNCTION__, $error_message, 'error');
2399
-                trigger_error($error_message);
2400
-            }
2401
-            return $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2402
-        }
2403
-        return $result;
2404
-    }
2405
-
2406
-
2407
-
2408
-    /**
2409
-     * Verifies the EE core database is up-to-date and records that we've done it on
2410
-     * EEM_Base::$_db_verification_level
2411
-     *
2412
-     * @param string $wpdb_method
2413
-     * @param array  $arguments_to_provide
2414
-     * @return string
2415
-     */
2416
-    private function _verify_core_db($wpdb_method, $arguments_to_provide)
2417
-    {
2418
-        /** @type WPDB $wpdb */
2419
-        global $wpdb;
2420
-        // ok remember that we've already attempted fixing the core db, in case the problem persists
2421
-        EEM_Base::$_db_verification_level = EEM_Base::db_verified_core;
2422
-        $error_message = sprintf(
2423
-            __(
2424
-                'WPDB Error "%1$s" while running wpdb method "%2$s" with arguments %3$s. Automatically attempting to fix EE Core DB',
2425
-                'event_espresso'
2426
-            ),
2427
-            $wpdb->last_error,
2428
-            $wpdb_method,
2429
-            wp_json_encode($arguments_to_provide)
2430
-        );
2431
-        EE_System::instance()->initialize_db_if_no_migrations_required(false, true);
2432
-        return $error_message;
2433
-    }
2434
-
2435
-
2436
-
2437
-    /**
2438
-     * Verifies the EE addons' database is up-to-date and records that we've done it on
2439
-     * EEM_Base::$_db_verification_level
2440
-     *
2441
-     * @param $wpdb_method
2442
-     * @param $arguments_to_provide
2443
-     * @return string
2444
-     */
2445
-    private function _verify_addons_db($wpdb_method, $arguments_to_provide)
2446
-    {
2447
-        /** @type WPDB $wpdb */
2448
-        global $wpdb;
2449
-        // ok remember that we've already attempted fixing the addons dbs, in case the problem persists
2450
-        EEM_Base::$_db_verification_level = EEM_Base::db_verified_addons;
2451
-        $error_message = sprintf(
2452
-            __(
2453
-                'WPDB AGAIN: Error "%1$s" while running the same method and arguments as before. Automatically attempting to fix EE Addons DB',
2454
-                'event_espresso'
2455
-            ),
2456
-            $wpdb->last_error,
2457
-            $wpdb_method,
2458
-            wp_json_encode($arguments_to_provide)
2459
-        );
2460
-        EE_System::instance()->initialize_addons();
2461
-        return $error_message;
2462
-    }
2463
-
2464
-
2465
-
2466
-    /**
2467
-     * In order to avoid repeating this code for the get_all, sum, and count functions, put the code parts
2468
-     * that are identical in here. Returns a string of SQL of everything in a SELECT query except the beginning
2469
-     * SELECT clause, eg " FROM wp_posts AS Event INNER JOIN ... WHERE ... ORDER BY ... LIMIT ... GROUP BY ... HAVING
2470
-     * ..."
2471
-     *
2472
-     * @param EE_Model_Query_Info_Carrier $model_query_info
2473
-     * @return string
2474
-     */
2475
-    private function _construct_2nd_half_of_select_query(EE_Model_Query_Info_Carrier $model_query_info)
2476
-    {
2477
-        return " FROM " . $model_query_info->get_full_join_sql() .
2478
-               $model_query_info->get_where_sql() .
2479
-               $model_query_info->get_group_by_sql() .
2480
-               $model_query_info->get_having_sql() .
2481
-               $model_query_info->get_order_by_sql() .
2482
-               $model_query_info->get_limit_sql();
2483
-    }
2484
-
2485
-
2486
-
2487
-    /**
2488
-     * Set to easily debug the next X queries ran from this model.
2489
-     *
2490
-     * @param int $count
2491
-     */
2492
-    public function show_next_x_db_queries($count = 1)
2493
-    {
2494
-        $this->_show_next_x_db_queries = $count;
2495
-    }
2496
-
2497
-
2498
-
2499
-    /**
2500
-     * @param $sql_query
2501
-     */
2502
-    public function show_db_query_if_previously_requested($sql_query)
2503
-    {
2504
-        if ($this->_show_next_x_db_queries > 0) {
2505
-            echo $sql_query;
2506
-            $this->_show_next_x_db_queries--;
2507
-        }
2508
-    }
2509
-
2510
-
2511
-
2512
-    /**
2513
-     * Adds a relationship of the correct type between $modelObject and $otherModelObject.
2514
-     * There are the 3 cases:
2515
-     * 'belongsTo' relationship: sets $id_or_obj's foreign_key to be $other_model_id_or_obj's primary_key. If
2516
-     * $otherModelObject has no ID, it is first saved.
2517
-     * 'hasMany' relationship: sets $other_model_id_or_obj's foreign_key to be $id_or_obj's primary_key. If $id_or_obj
2518
-     * has no ID, it is first saved.
2519
-     * 'hasAndBelongsToMany' relationships: checks that there isn't already an entry in the join table, and adds one.
2520
-     * If one of the model Objects has not yet been saved to the database, it is saved before adding the entry in the
2521
-     * join table
2522
-     *
2523
-     * @param        EE_Base_Class                     /int $thisModelObject
2524
-     * @param        EE_Base_Class                     /int $id_or_obj EE_base_Class or ID of other Model Object
2525
-     * @param string $relationName                     , key in EEM_Base::_relations
2526
-     *                                                 an attendee to a group, you also want to specify which role they
2527
-     *                                                 will have in that group. So you would use this parameter to
2528
-     *                                                 specify array('role-column-name'=>'role-id')
2529
-     * @param array  $extra_join_model_fields_n_values This allows you to enter further query params for the relation
2530
-     *                                                 to for relation to methods that allow you to further specify
2531
-     *                                                 extra columns to join by (such as HABTM).  Keep in mind that the
2532
-     *                                                 only acceptable query_params is strict "col" => "value" pairs
2533
-     *                                                 because these will be inserted in any new rows created as well.
2534
-     * @return EE_Base_Class which was added as a relation. Object referred to by $other_model_id_or_obj
2535
-     * @throws EE_Error
2536
-     */
2537
-    public function add_relationship_to(
2538
-        $id_or_obj,
2539
-        $other_model_id_or_obj,
2540
-        $relationName,
2541
-        $extra_join_model_fields_n_values = array()
2542
-    ) {
2543
-        $relation_obj = $this->related_settings_for($relationName);
2544
-        return $relation_obj->add_relation_to($id_or_obj, $other_model_id_or_obj, $extra_join_model_fields_n_values);
2545
-    }
2546
-
2547
-
2548
-
2549
-    /**
2550
-     * Removes a relationship of the correct type between $modelObject and $otherModelObject.
2551
-     * There are the 3 cases:
2552
-     * 'belongsTo' relationship: sets $modelObject's foreign_key to null, if that field is nullable.Otherwise throws an
2553
-     * error
2554
-     * 'hasMany' relationship: sets $otherModelObject's foreign_key to null,if that field is nullable.Otherwise throws
2555
-     * an error
2556
-     * 'hasAndBelongsToMany' relationships:removes any existing entry in the join table between the two models.
2557
-     *
2558
-     * @param        EE_Base_Class /int $id_or_obj
2559
-     * @param        EE_Base_Class /int $other_model_id_or_obj EE_Base_Class or ID of other Model Object
2560
-     * @param string $relationName key in EEM_Base::_relations
2561
-     * @return boolean of success
2562
-     * @throws EE_Error
2563
-     * @param array  $where_query  This allows you to enter further query params for the relation to for relation to
2564
-     *                             methods that allow you to further specify extra columns to join by (such as HABTM).
2565
-     *                             Keep in mind that the only acceptable query_params is strict "col" => "value" pairs
2566
-     *                             because these will be inserted in any new rows created as well.
2567
-     */
2568
-    public function remove_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
2569
-    {
2570
-        $relation_obj = $this->related_settings_for($relationName);
2571
-        return $relation_obj->remove_relation_to($id_or_obj, $other_model_id_or_obj, $where_query);
2572
-    }
2573
-
2574
-
2575
-
2576
-    /**
2577
-     * @param mixed           $id_or_obj
2578
-     * @param string          $relationName
2579
-     * @param array           $where_query_params
2580
-     * @param EE_Base_Class[] objects to which relations were removed
2581
-     * @return \EE_Base_Class[]
2582
-     * @throws EE_Error
2583
-     */
2584
-    public function remove_relations($id_or_obj, $relationName, $where_query_params = array())
2585
-    {
2586
-        $relation_obj = $this->related_settings_for($relationName);
2587
-        return $relation_obj->remove_relations($id_or_obj, $where_query_params);
2588
-    }
2589
-
2590
-
2591
-
2592
-    /**
2593
-     * Gets all the related items of the specified $model_name, using $query_params.
2594
-     * Note: by default, we remove the "default query params"
2595
-     * because we want to get even deleted items etc.
2596
-     *
2597
-     * @param mixed  $id_or_obj    EE_Base_Class child or its ID
2598
-     * @param string $model_name   like 'Event', 'Registration', etc. always singular
2599
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2600
-     * @return EE_Base_Class[]
2601
-     * @throws EE_Error
2602
-     */
2603
-    public function get_all_related($id_or_obj, $model_name, $query_params = null)
2604
-    {
2605
-        $model_obj = $this->ensure_is_obj($id_or_obj);
2606
-        $relation_settings = $this->related_settings_for($model_name);
2607
-        return $relation_settings->get_all_related($model_obj, $query_params);
2608
-    }
2609
-
2610
-
2611
-
2612
-    /**
2613
-     * Deletes all the model objects across the relation indicated by $model_name
2614
-     * which are related to $id_or_obj which meet the criteria set in $query_params.
2615
-     * However, if the model objects can't be deleted because of blocking related model objects, then
2616
-     * they aren't deleted. (Unless the thing that would have been deleted can be soft-deleted, that still happens).
2617
-     *
2618
-     * @param EE_Base_Class|int|string $id_or_obj
2619
-     * @param string                   $model_name
2620
-     * @param array                    $query_params
2621
-     * @return int how many deleted
2622
-     * @throws EE_Error
2623
-     */
2624
-    public function delete_related($id_or_obj, $model_name, $query_params = array())
2625
-    {
2626
-        $model_obj = $this->ensure_is_obj($id_or_obj);
2627
-        $relation_settings = $this->related_settings_for($model_name);
2628
-        return $relation_settings->delete_all_related($model_obj, $query_params);
2629
-    }
2630
-
2631
-
2632
-
2633
-    /**
2634
-     * Hard deletes all the model objects across the relation indicated by $model_name
2635
-     * which are related to $id_or_obj which meet the criteria set in $query_params. If
2636
-     * the model objects can't be hard deleted because of blocking related model objects,
2637
-     * just does a soft-delete on them instead.
2638
-     *
2639
-     * @param EE_Base_Class|int|string $id_or_obj
2640
-     * @param string                   $model_name
2641
-     * @param array                    $query_params
2642
-     * @return int how many deleted
2643
-     * @throws EE_Error
2644
-     */
2645
-    public function delete_related_permanently($id_or_obj, $model_name, $query_params = array())
2646
-    {
2647
-        $model_obj = $this->ensure_is_obj($id_or_obj);
2648
-        $relation_settings = $this->related_settings_for($model_name);
2649
-        return $relation_settings->delete_related_permanently($model_obj, $query_params);
2650
-    }
2651
-
2652
-
2653
-
2654
-    /**
2655
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2656
-     * unless otherwise specified in the $query_params
2657
-     *
2658
-     * @param        int             /EE_Base_Class $id_or_obj
2659
-     * @param string $model_name     like 'Event', or 'Registration'
2660
-     * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2661
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2662
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2663
-     *                               that by the setting $distinct to TRUE;
2664
-     * @return int
2665
-     * @throws EE_Error
2666
-     */
2667
-    public function count_related(
2668
-        $id_or_obj,
2669
-        $model_name,
2670
-        $query_params = array(),
2671
-        $field_to_count = null,
2672
-        $distinct = false
2673
-    ) {
2674
-        $related_model = $this->get_related_model_obj($model_name);
2675
-        // we're just going to use the query params on the related model's normal get_all query,
2676
-        // except add a condition to say to match the current mod
2677
-        if (! isset($query_params['default_where_conditions'])) {
2678
-            $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2679
-        }
2680
-        $this_model_name = $this->get_this_model_name();
2681
-        $this_pk_field_name = $this->get_primary_key_field()->get_name();
2682
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2683
-        return $related_model->count($query_params, $field_to_count, $distinct);
2684
-    }
2685
-
2686
-
2687
-
2688
-    /**
2689
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2690
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2691
-     *
2692
-     * @param        int           /EE_Base_Class $id_or_obj
2693
-     * @param string $model_name   like 'Event', or 'Registration'
2694
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2695
-     * @param string $field_to_sum name of field to count by. By default, uses primary key
2696
-     * @return float
2697
-     * @throws EE_Error
2698
-     */
2699
-    public function sum_related($id_or_obj, $model_name, $query_params, $field_to_sum = null)
2700
-    {
2701
-        $related_model = $this->get_related_model_obj($model_name);
2702
-        if (! is_array($query_params)) {
2703
-            EE_Error::doing_it_wrong(
2704
-                'EEM_Base::sum_related',
2705
-                sprintf(
2706
-                    __('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
2707
-                    gettype($query_params)
2708
-                ),
2709
-                '4.6.0'
2710
-            );
2711
-            $query_params = array();
2712
-        }
2713
-        // we're just going to use the query params on the related model's normal get_all query,
2714
-        // except add a condition to say to match the current mod
2715
-        if (! isset($query_params['default_where_conditions'])) {
2716
-            $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2717
-        }
2718
-        $this_model_name = $this->get_this_model_name();
2719
-        $this_pk_field_name = $this->get_primary_key_field()->get_name();
2720
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2721
-        return $related_model->sum($query_params, $field_to_sum);
2722
-    }
2723
-
2724
-
2725
-
2726
-    /**
2727
-     * Uses $this->_relatedModels info to find the first related model object of relation $relationName to the given
2728
-     * $modelObject
2729
-     *
2730
-     * @param int | EE_Base_Class $id_or_obj        EE_Base_Class child or its ID
2731
-     * @param string              $other_model_name , key in $this->_relatedModels, eg 'Registration', or 'Events'
2732
-     * @param array               $query_params     @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2733
-     * @return EE_Base_Class
2734
-     * @throws EE_Error
2735
-     */
2736
-    public function get_first_related(EE_Base_Class $id_or_obj, $other_model_name, $query_params)
2737
-    {
2738
-        $query_params['limit'] = 1;
2739
-        $results = $this->get_all_related($id_or_obj, $other_model_name, $query_params);
2740
-        if ($results) {
2741
-            return array_shift($results);
2742
-        }
2743
-        return null;
2744
-    }
2745
-
2746
-
2747
-
2748
-    /**
2749
-     * Gets the model's name as it's expected in queries. For example, if this is EEM_Event model, that would be Event
2750
-     *
2751
-     * @return string
2752
-     */
2753
-    public function get_this_model_name()
2754
-    {
2755
-        return str_replace("EEM_", "", get_class($this));
2756
-    }
2757
-
2758
-
2759
-
2760
-    /**
2761
-     * Gets the model field on this model which is of type EE_Any_Foreign_Model_Name_Field
2762
-     *
2763
-     * @return EE_Any_Foreign_Model_Name_Field
2764
-     * @throws EE_Error
2765
-     */
2766
-    public function get_field_containing_related_model_name()
2767
-    {
2768
-        foreach ($this->field_settings(true) as $field) {
2769
-            if ($field instanceof EE_Any_Foreign_Model_Name_Field) {
2770
-                $field_with_model_name = $field;
2771
-            }
2772
-        }
2773
-        if (! isset($field_with_model_name) || ! $field_with_model_name) {
2774
-            throw new EE_Error(sprintf(
2775
-                __("There is no EE_Any_Foreign_Model_Name field on model %s", "event_espresso"),
2776
-                $this->get_this_model_name()
2777
-            ));
2778
-        }
2779
-        return $field_with_model_name;
2780
-    }
2781
-
2782
-
2783
-
2784
-    /**
2785
-     * Inserts a new entry into the database, for each table.
2786
-     * Note: does not add the item to the entity map because that is done by EE_Base_Class::save() right after this.
2787
-     * If client code uses EEM_Base::insert() directly, then although the item isn't in the entity map,
2788
-     * we also know there is no model object with the newly inserted item's ID at the moment (because
2789
-     * if there were, then they would already be in the DB and this would fail); and in the future if someone
2790
-     * creates a model object with this ID (or grabs it from the DB) then it will be added to the
2791
-     * entity map at that time anyways. SO, no need for EEM_Base::insert ot add to the entity map
2792
-     *
2793
-     * @param array $field_n_values keys are field names, values are their values (in the client code's domain if
2794
-     *                              $values_already_prepared_by_model_object is false, in the model object's domain if
2795
-     *                              $values_already_prepared_by_model_object is true. See comment about this at the top
2796
-     *                              of EEM_Base)
2797
-     * @return int|string new primary key on main table that got inserted
2798
-     * @throws EE_Error
2799
-     */
2800
-    public function insert($field_n_values)
2801
-    {
2802
-        /**
2803
-         * Filters the fields and their values before inserting an item using the models
2804
-         *
2805
-         * @param array    $fields_n_values keys are the fields and values are their new values
2806
-         * @param EEM_Base $model           the model used
2807
-         */
2808
-        $field_n_values = (array) apply_filters('FHEE__EEM_Base__insert__fields_n_values', $field_n_values, $this);
2809
-        if ($this->_satisfies_unique_indexes($field_n_values)) {
2810
-            $main_table = $this->_get_main_table();
2811
-            $new_id = $this->_insert_into_specific_table($main_table, $field_n_values, false);
2812
-            if ($new_id !== false) {
2813
-                foreach ($this->_get_other_tables() as $other_table) {
2814
-                    $this->_insert_into_specific_table($other_table, $field_n_values, $new_id);
2815
-                }
2816
-            }
2817
-            /**
2818
-             * Done just after attempting to insert a new model object
2819
-             *
2820
-             * @param EEM_Base   $model           used
2821
-             * @param array      $fields_n_values fields and their values
2822
-             * @param int|string the              ID of the newly-inserted model object
2823
-             */
2824
-            do_action('AHEE__EEM_Base__insert__end', $this, $field_n_values, $new_id);
2825
-            return $new_id;
2826
-        }
2827
-        return false;
2828
-    }
2829
-
2830
-
2831
-
2832
-    /**
2833
-     * Checks that the result would satisfy the unique indexes on this model
2834
-     *
2835
-     * @param array  $field_n_values
2836
-     * @param string $action
2837
-     * @return boolean
2838
-     * @throws EE_Error
2839
-     */
2840
-    protected function _satisfies_unique_indexes($field_n_values, $action = 'insert')
2841
-    {
2842
-        foreach ($this->unique_indexes() as $index_name => $index) {
2843
-            $uniqueness_where_params = array_intersect_key($field_n_values, $index->fields());
2844
-            if ($this->exists(array($uniqueness_where_params))) {
2845
-                EE_Error::add_error(
2846
-                    sprintf(
2847
-                        __(
2848
-                            "Could not %s %s. %s uniqueness index failed. Fields %s must form a unique set, but an entry already exists with values %s.",
2849
-                            "event_espresso"
2850
-                        ),
2851
-                        $action,
2852
-                        $this->_get_class_name(),
2853
-                        $index_name,
2854
-                        implode(",", $index->field_names()),
2855
-                        http_build_query($uniqueness_where_params)
2856
-                    ),
2857
-                    __FILE__,
2858
-                    __FUNCTION__,
2859
-                    __LINE__
2860
-                );
2861
-                return false;
2862
-            }
2863
-        }
2864
-        return true;
2865
-    }
2866
-
2867
-
2868
-
2869
-    /**
2870
-     * Checks the database for an item that conflicts (ie, if this item were
2871
-     * saved to the DB would break some uniqueness requirement, like a primary key
2872
-     * or an index primary key set) with the item specified. $id_obj_or_fields_array
2873
-     * can be either an EE_Base_Class or an array of fields n values
2874
-     *
2875
-     * @param EE_Base_Class|array $obj_or_fields_array
2876
-     * @param boolean             $include_primary_key whether to use the model object's primary key
2877
-     *                                                 when looking for conflicts
2878
-     *                                                 (ie, if false, we ignore the model object's primary key
2879
-     *                                                 when finding "conflicts". If true, it's also considered).
2880
-     *                                                 Only works for INT primary key,
2881
-     *                                                 STRING primary keys cannot be ignored
2882
-     * @throws EE_Error
2883
-     * @return EE_Base_Class|array
2884
-     */
2885
-    public function get_one_conflicting($obj_or_fields_array, $include_primary_key = true)
2886
-    {
2887
-        if ($obj_or_fields_array instanceof EE_Base_Class) {
2888
-            $fields_n_values = $obj_or_fields_array->model_field_array();
2889
-        } elseif (is_array($obj_or_fields_array)) {
2890
-            $fields_n_values = $obj_or_fields_array;
2891
-        } else {
2892
-            throw new EE_Error(
2893
-                sprintf(
2894
-                    __(
2895
-                        "%s get_all_conflicting should be called with a model object or an array of field names and values, you provided %d",
2896
-                        "event_espresso"
2897
-                    ),
2898
-                    get_class($this),
2899
-                    $obj_or_fields_array
2900
-                )
2901
-            );
2902
-        }
2903
-        $query_params = array();
2904
-        if (
2905
-            $this->has_primary_key_field()
2906
-            && ($include_primary_key
2907
-                || $this->get_primary_key_field()
2908
-                   instanceof
2909
-                   EE_Primary_Key_String_Field)
2910
-            && isset($fields_n_values[ $this->primary_key_name() ])
2911
-        ) {
2912
-            $query_params[0]['OR'][ $this->primary_key_name() ] = $fields_n_values[ $this->primary_key_name() ];
2913
-        }
2914
-        foreach ($this->unique_indexes() as $unique_index_name => $unique_index) {
2915
-            $uniqueness_where_params = array_intersect_key($fields_n_values, $unique_index->fields());
2916
-            $query_params[0]['OR'][ 'AND*' . $unique_index_name ] = $uniqueness_where_params;
2917
-        }
2918
-        // if there is nothing to base this search on, then we shouldn't find anything
2919
-        if (empty($query_params)) {
2920
-            return array();
2921
-        }
2922
-        return $this->get_one($query_params);
2923
-    }
2924
-
2925
-
2926
-
2927
-    /**
2928
-     * Like count, but is optimized and returns a boolean instead of an int
2929
-     *
2930
-     * @param array $query_params
2931
-     * @return boolean
2932
-     * @throws EE_Error
2933
-     */
2934
-    public function exists($query_params)
2935
-    {
2936
-        $query_params['limit'] = 1;
2937
-        return $this->count($query_params) > 0;
2938
-    }
2939
-
2940
-
2941
-
2942
-    /**
2943
-     * Wrapper for exists, except ignores default query parameters so we're only considering ID
2944
-     *
2945
-     * @param int|string $id
2946
-     * @return boolean
2947
-     * @throws EE_Error
2948
-     */
2949
-    public function exists_by_ID($id)
2950
-    {
2951
-        return $this->exists(
2952
-            array(
2953
-                'default_where_conditions' => EEM_Base::default_where_conditions_none,
2954
-                array(
2955
-                    $this->primary_key_name() => $id,
2956
-                ),
2957
-            )
2958
-        );
2959
-    }
2960
-
2961
-
2962
-
2963
-    /**
2964
-     * Inserts a new row in $table, using the $cols_n_values which apply to that table.
2965
-     * If a $new_id is supplied and if $table is an EE_Other_Table, we assume
2966
-     * we need to add a foreign key column to point to $new_id (which should be the primary key's value
2967
-     * on the main table)
2968
-     * This is protected rather than private because private is not accessible to any child methods and there MAY be
2969
-     * cases where we want to call it directly rather than via insert().
2970
-     *
2971
-     * @access   protected
2972
-     * @param EE_Table_Base $table
2973
-     * @param array         $fields_n_values each key should be in field's keys, and value should be an int, string or
2974
-     *                                       float
2975
-     * @param int           $new_id          for now we assume only int keys
2976
-     * @throws EE_Error
2977
-     * @global WPDB         $wpdb            only used to get the $wpdb->insert_id after performing an insert
2978
-     * @return int ID of new row inserted, or FALSE on failure
2979
-     */
2980
-    protected function _insert_into_specific_table(EE_Table_Base $table, $fields_n_values, $new_id = 0)
2981
-    {
2982
-        global $wpdb;
2983
-        $insertion_col_n_values = array();
2984
-        $format_for_insertion = array();
2985
-        $fields_on_table = $this->_get_fields_for_table($table->get_table_alias());
2986
-        foreach ($fields_on_table as $field_name => $field_obj) {
2987
-            // check if its an auto-incrementing column, in which case we should just leave it to do its autoincrement thing
2988
-            if ($field_obj->is_auto_increment()) {
2989
-                continue;
2990
-            }
2991
-            $prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
2992
-            // if the value we want to assign it to is NULL, just don't mention it for the insertion
2993
-            if ($prepared_value !== null) {
2994
-                $insertion_col_n_values[ $field_obj->get_table_column() ] = $prepared_value;
2995
-                $format_for_insertion[] = $field_obj->get_wpdb_data_type();
2996
-            }
2997
-        }
2998
-        if ($table instanceof EE_Secondary_Table && $new_id) {
2999
-            // its not the main table, so we should have already saved the main table's PK which we just inserted
3000
-            // so add the fk to the main table as a column
3001
-            $insertion_col_n_values[ $table->get_fk_on_table() ] = $new_id;
3002
-            $format_for_insertion[] = '%d';// yes right now we're only allowing these foreign keys to be INTs
3003
-        }
3004
-        // insert the new entry
3005
-        $result = $this->_do_wpdb_query(
3006
-            'insert',
3007
-            array($table->get_table_name(), $insertion_col_n_values, $format_for_insertion)
3008
-        );
3009
-        if ($result === false) {
3010
-            return false;
3011
-        }
3012
-        // ok, now what do we return for the ID of the newly-inserted thing?
3013
-        if ($this->has_primary_key_field()) {
3014
-            if ($this->get_primary_key_field()->is_auto_increment()) {
3015
-                return $wpdb->insert_id;
3016
-            }
3017
-            // it's not an auto-increment primary key, so
3018
-            // it must have been supplied
3019
-            return $fields_n_values[ $this->get_primary_key_field()->get_name() ];
3020
-        }
3021
-        // we can't return a  primary key because there is none. instead return
3022
-        // a unique string indicating this model
3023
-        return $this->get_index_primary_key_string($fields_n_values);
3024
-    }
3025
-
3026
-
3027
-
3028
-    /**
3029
-     * Prepare the $field_obj 's value in $fields_n_values for use in the database.
3030
-     * If the field doesn't allow NULL, try to use its default. (If it doesn't allow NULL,
3031
-     * and there is no default, we pass it along. WPDB will take care of it)
3032
-     *
3033
-     * @param EE_Model_Field_Base $field_obj
3034
-     * @param array               $fields_n_values
3035
-     * @return mixed string|int|float depending on what the table column will be expecting
3036
-     * @throws EE_Error
3037
-     */
3038
-    protected function _prepare_value_or_use_default($field_obj, $fields_n_values)
3039
-    {
3040
-        // if this field doesn't allow nullable, don't allow it
3041
-        if (
3042
-            ! $field_obj->is_nullable()
3043
-            && (
3044
-                ! isset($fields_n_values[ $field_obj->get_name() ])
3045
-                || $fields_n_values[ $field_obj->get_name() ] === null
3046
-            )
3047
-        ) {
3048
-            $fields_n_values[ $field_obj->get_name() ] = $field_obj->get_default_value();
3049
-        }
3050
-        $unprepared_value = isset($fields_n_values[ $field_obj->get_name() ])
3051
-            ? $fields_n_values[ $field_obj->get_name() ]
3052
-            : null;
3053
-        return $this->_prepare_value_for_use_in_db($unprepared_value, $field_obj);
3054
-    }
3055
-
3056
-
3057
-
3058
-    /**
3059
-     * Consolidates code for preparing  a value supplied to the model for use int eh db. Calls the field's
3060
-     * prepare_for_use_in_db method on the value, and depending on $value_already_prepare_by_model_obj, may also call
3061
-     * the field's prepare_for_set() method.
3062
-     *
3063
-     * @param mixed               $value value in the client code domain if $value_already_prepared_by_model_object is
3064
-     *                                   false, otherwise a value in the model object's domain (see lengthy comment at
3065
-     *                                   top of file)
3066
-     * @param EE_Model_Field_Base $field field which will be doing the preparing of the value. If null, we assume
3067
-     *                                   $value is a custom selection
3068
-     * @return mixed a value ready for use in the database for insertions, updating, or in a where clause
3069
-     */
3070
-    private function _prepare_value_for_use_in_db($value, $field)
3071
-    {
3072
-        if ($field && $field instanceof EE_Model_Field_Base) {
3073
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
3074
-            switch ($this->_values_already_prepared_by_model_object) {
3075
-                /** @noinspection PhpMissingBreakStatementInspection */
3076
-                case self::not_prepared_by_model_object:
3077
-                    $value = $field->prepare_for_set($value);
3078
-                // purposefully left out "return"
3079
-                case self::prepared_by_model_object:
3080
-                    /** @noinspection SuspiciousAssignmentsInspection */
3081
-                    $value = $field->prepare_for_use_in_db($value);
3082
-                case self::prepared_for_use_in_db:
3083
-                    // leave the value alone
3084
-            }
3085
-            return $value;
3086
-            // phpcs:enable
3087
-        }
3088
-        return $value;
3089
-    }
3090
-
3091
-
3092
-
3093
-    /**
3094
-     * Returns the main table on this model
3095
-     *
3096
-     * @return EE_Primary_Table
3097
-     * @throws EE_Error
3098
-     */
3099
-    protected function _get_main_table()
3100
-    {
3101
-        foreach ($this->_tables as $table) {
3102
-            if ($table instanceof EE_Primary_Table) {
3103
-                return $table;
3104
-            }
3105
-        }
3106
-        throw new EE_Error(sprintf(__(
3107
-            'There are no main tables on %s. They should be added to _tables array in the constructor',
3108
-            'event_espresso'
3109
-        ), get_class($this)));
3110
-    }
3111
-
3112
-
3113
-
3114
-    /**
3115
-     * table
3116
-     * returns EE_Primary_Table table name
3117
-     *
3118
-     * @return string
3119
-     * @throws EE_Error
3120
-     */
3121
-    public function table()
3122
-    {
3123
-        return $this->_get_main_table()->get_table_name();
3124
-    }
3125
-
3126
-
3127
-
3128
-    /**
3129
-     * table
3130
-     * returns first EE_Secondary_Table table name
3131
-     *
3132
-     * @return string
3133
-     */
3134
-    public function second_table()
3135
-    {
3136
-        // grab second table from tables array
3137
-        $second_table = end($this->_tables);
3138
-        return $second_table instanceof EE_Secondary_Table ? $second_table->get_table_name() : null;
3139
-    }
3140
-
3141
-
3142
-
3143
-    /**
3144
-     * get_table_obj_by_alias
3145
-     * returns table name given it's alias
3146
-     *
3147
-     * @param string $table_alias
3148
-     * @return EE_Primary_Table | EE_Secondary_Table
3149
-     */
3150
-    public function get_table_obj_by_alias($table_alias = '')
3151
-    {
3152
-        return isset($this->_tables[ $table_alias ]) ? $this->_tables[ $table_alias ] : null;
3153
-    }
3154
-
3155
-
3156
-
3157
-    /**
3158
-     * Gets all the tables of type EE_Other_Table from EEM_CPT_Basel_Model::_tables
3159
-     *
3160
-     * @return EE_Secondary_Table[]
3161
-     */
3162
-    protected function _get_other_tables()
3163
-    {
3164
-        $other_tables = array();
3165
-        foreach ($this->_tables as $table_alias => $table) {
3166
-            if ($table instanceof EE_Secondary_Table) {
3167
-                $other_tables[ $table_alias ] = $table;
3168
-            }
3169
-        }
3170
-        return $other_tables;
3171
-    }
3172
-
3173
-
3174
-
3175
-    /**
3176
-     * Finds all the fields that correspond to the given table
3177
-     *
3178
-     * @param string $table_alias , array key in EEM_Base::_tables
3179
-     * @return EE_Model_Field_Base[]
3180
-     */
3181
-    public function _get_fields_for_table($table_alias)
3182
-    {
3183
-        return $this->_fields[ $table_alias ];
3184
-    }
3185
-
3186
-
3187
-
3188
-    /**
3189
-     * Recurses through all the where parameters, and finds all the related models we'll need
3190
-     * to complete this query. Eg, given where parameters like array('EVT_ID'=>3) from within Event model, we won't
3191
-     * need any related models. But if the array were array('Registrations.REG_ID'=>3), we'd need the related
3192
-     * Registration model. If it were array('Registrations.Transactions.Payments.PAY_ID'=>3), then we'd need the
3193
-     * related Registration, Transaction, and Payment models.
3194
-     *
3195
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3196
-     * @return EE_Model_Query_Info_Carrier
3197
-     * @throws EE_Error
3198
-     */
3199
-    public function _extract_related_models_from_query($query_params)
3200
-    {
3201
-        $query_info_carrier = new EE_Model_Query_Info_Carrier();
3202
-        if (array_key_exists(0, $query_params)) {
3203
-            $this->_extract_related_models_from_sub_params_array_keys($query_params[0], $query_info_carrier, 0);
3204
-        }
3205
-        if (array_key_exists('group_by', $query_params)) {
3206
-            if (is_array($query_params['group_by'])) {
3207
-                $this->_extract_related_models_from_sub_params_array_values(
3208
-                    $query_params['group_by'],
3209
-                    $query_info_carrier,
3210
-                    'group_by'
3211
-                );
3212
-            } elseif (! empty($query_params['group_by'])) {
3213
-                $this->_extract_related_model_info_from_query_param(
3214
-                    $query_params['group_by'],
3215
-                    $query_info_carrier,
3216
-                    'group_by'
3217
-                );
3218
-            }
3219
-        }
3220
-        if (array_key_exists('having', $query_params)) {
3221
-            $this->_extract_related_models_from_sub_params_array_keys(
3222
-                $query_params[0],
3223
-                $query_info_carrier,
3224
-                'having'
3225
-            );
3226
-        }
3227
-        if (array_key_exists('order_by', $query_params)) {
3228
-            if (is_array($query_params['order_by'])) {
3229
-                $this->_extract_related_models_from_sub_params_array_keys(
3230
-                    $query_params['order_by'],
3231
-                    $query_info_carrier,
3232
-                    'order_by'
3233
-                );
3234
-            } elseif (! empty($query_params['order_by'])) {
3235
-                $this->_extract_related_model_info_from_query_param(
3236
-                    $query_params['order_by'],
3237
-                    $query_info_carrier,
3238
-                    'order_by'
3239
-                );
3240
-            }
3241
-        }
3242
-        if (array_key_exists('force_join', $query_params)) {
3243
-            $this->_extract_related_models_from_sub_params_array_values(
3244
-                $query_params['force_join'],
3245
-                $query_info_carrier,
3246
-                'force_join'
3247
-            );
3248
-        }
3249
-        $this->extractRelatedModelsFromCustomSelects($query_info_carrier);
3250
-        return $query_info_carrier;
3251
-    }
3252
-
3253
-
3254
-
3255
-    /**
3256
-     * For extracting related models from WHERE (0), HAVING (having), ORDER BY (order_by) or forced joins (force_join)
3257
-     *
3258
-     * @param array                       $sub_query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#-0-where-conditions
3259
-     * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3260
-     * @param string                      $query_param_type one of $this->_allowed_query_params
3261
-     * @throws EE_Error
3262
-     * @return \EE_Model_Query_Info_Carrier
3263
-     */
3264
-    private function _extract_related_models_from_sub_params_array_keys(
3265
-        $sub_query_params,
3266
-        EE_Model_Query_Info_Carrier $model_query_info_carrier,
3267
-        $query_param_type
3268
-    ) {
3269
-        if (! empty($sub_query_params)) {
3270
-            $sub_query_params = (array) $sub_query_params;
3271
-            foreach ($sub_query_params as $param => $possibly_array_of_params) {
3272
-                // $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3273
-                $this->_extract_related_model_info_from_query_param(
3274
-                    $param,
3275
-                    $model_query_info_carrier,
3276
-                    $query_param_type
3277
-                );
3278
-                // if $possibly_array_of_params is an array, try recursing into it, searching for keys which
3279
-                // indicate needed joins. Eg, array('NOT'=>array('Registration.TXN_ID'=>23)). In this case, we tried
3280
-                // extracting models out of the 'NOT', which obviously wasn't successful, and then we recurse into the value
3281
-                // of array('Registration.TXN_ID'=>23)
3282
-                $query_param_sans_stars = $this->_remove_stars_and_anything_after_from_condition_query_param_key($param);
3283
-                if (in_array($query_param_sans_stars, $this->_logic_query_param_keys, true)) {
3284
-                    if (! is_array($possibly_array_of_params)) {
3285
-                        throw new EE_Error(sprintf(
3286
-                            __(
3287
-                                "You used a special where query param %s, but the value isn't an array of where query params, it's just %s'. It should be an array, eg array('EVT_ID'=>23,'OR'=>array('Venue.VNU_ID'=>32,'Venue.VNU_name'=>'monkey_land'))",
3288
-                                "event_espresso"
3289
-                            ),
3290
-                            $param,
3291
-                            $possibly_array_of_params
3292
-                        ));
3293
-                    }
3294
-                    $this->_extract_related_models_from_sub_params_array_keys(
3295
-                        $possibly_array_of_params,
3296
-                        $model_query_info_carrier,
3297
-                        $query_param_type
3298
-                    );
3299
-                } elseif (
3300
-                    $query_param_type === 0 // ie WHERE
3301
-                          && is_array($possibly_array_of_params)
3302
-                          && isset($possibly_array_of_params[2])
3303
-                          && $possibly_array_of_params[2] == true
3304
-                ) {
3305
-                    // then $possible_array_of_params looks something like array('<','DTT_sold',true)
3306
-                    // indicating that $possible_array_of_params[1] is actually a field name,
3307
-                    // from which we should extract query parameters!
3308
-                    if (! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3309
-                        throw new EE_Error(sprintf(__(
3310
-                            "Improperly formed query parameter %s. It should be numerically indexed like array('<','DTT_sold',true); but you provided %s",
3311
-                            "event_espresso"
3312
-                        ), $query_param_type, implode(",", $possibly_array_of_params)));
3313
-                    }
3314
-                    $this->_extract_related_model_info_from_query_param(
3315
-                        $possibly_array_of_params[1],
3316
-                        $model_query_info_carrier,
3317
-                        $query_param_type
3318
-                    );
3319
-                }
3320
-            }
3321
-        }
3322
-        return $model_query_info_carrier;
3323
-    }
3324
-
3325
-
3326
-
3327
-    /**
3328
-     * For extracting related models from forced_joins, where the array values contain the info about what
3329
-     * models to join with. Eg an array like array('Attendee','Price.Price_Type');
3330
-     *
3331
-     * @param array                       $sub_query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3332
-     * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3333
-     * @param string                      $query_param_type one of $this->_allowed_query_params
3334
-     * @throws EE_Error
3335
-     * @return \EE_Model_Query_Info_Carrier
3336
-     */
3337
-    private function _extract_related_models_from_sub_params_array_values(
3338
-        $sub_query_params,
3339
-        EE_Model_Query_Info_Carrier $model_query_info_carrier,
3340
-        $query_param_type
3341
-    ) {
3342
-        if (! empty($sub_query_params)) {
3343
-            if (! is_array($sub_query_params)) {
3344
-                throw new EE_Error(sprintf(
3345
-                    __("Query parameter %s should be an array, but it isn't.", "event_espresso"),
3346
-                    $sub_query_params
3347
-                ));
3348
-            }
3349
-            foreach ($sub_query_params as $param) {
3350
-                // $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3351
-                $this->_extract_related_model_info_from_query_param(
3352
-                    $param,
3353
-                    $model_query_info_carrier,
3354
-                    $query_param_type
3355
-                );
3356
-            }
3357
-        }
3358
-        return $model_query_info_carrier;
3359
-    }
3360
-
3361
-
3362
-    /**
3363
-     * Extract all the query parts from  model query params
3364
-     * and put into a EEM_Related_Model_Info_Carrier for easy extraction into a query. We create this object
3365
-     * instead of directly constructing the SQL because often we need to extract info from the $query_params
3366
-     * but use them in a different order. Eg, we need to know what models we are querying
3367
-     * before we know what joins to perform. However, we need to know what data types correspond to which fields on
3368
-     * other models before we can finalize the where clause SQL.
3369
-     *
3370
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3371
-     * @throws EE_Error
3372
-     * @return EE_Model_Query_Info_Carrier
3373
-     * @throws ModelConfigurationException
3374
-     */
3375
-    public function _create_model_query_info_carrier($query_params)
3376
-    {
3377
-        if (! is_array($query_params)) {
3378
-            EE_Error::doing_it_wrong(
3379
-                'EEM_Base::_create_model_query_info_carrier',
3380
-                sprintf(
3381
-                    __(
3382
-                        '$query_params should be an array, you passed a variable of type %s',
3383
-                        'event_espresso'
3384
-                    ),
3385
-                    gettype($query_params)
3386
-                ),
3387
-                '4.6.0'
3388
-            );
3389
-            $query_params = array();
3390
-        }
3391
-        $query_params[0] = isset($query_params[0]) ? $query_params[0] : array();
3392
-        // first check if we should alter the query to account for caps or not
3393
-        // because the caps might require us to do extra joins
3394
-        if (isset($query_params['caps']) && $query_params['caps'] !== 'none') {
3395
-            $query_params[0] = array_replace_recursive(
3396
-                $query_params[0],
3397
-                $this->caps_where_conditions(
3398
-                    $query_params['caps']
3399
-                )
3400
-            );
3401
-        }
3402
-
3403
-        // check if we should alter the query to remove data related to protected
3404
-        // custom post types
3405
-        if (isset($query_params['exclude_protected']) && $query_params['exclude_protected'] === true) {
3406
-            $where_param_key_for_password = $this->modelChainAndPassword();
3407
-            // only include if related to a cpt where no password has been set
3408
-            $query_params[0]['OR*nopassword'] = array(
3409
-                $where_param_key_for_password => '',
3410
-                $where_param_key_for_password . '*' => array('IS_NULL')
3411
-            );
3412
-        }
3413
-        $query_object = $this->_extract_related_models_from_query($query_params);
3414
-        // verify where_query_params has NO numeric indexes.... that's simply not how you use it!
3415
-        foreach ($query_params[0] as $key => $value) {
3416
-            if (is_int($key)) {
3417
-                throw new EE_Error(
3418
-                    sprintf(
3419
-                        __(
3420
-                            "WHERE query params must NOT be numerically-indexed. You provided the array key '%s' for value '%s' while querying model %s. All the query params provided were '%s' Please read documentation on EEM_Base::get_all.",
3421
-                            "event_espresso"
3422
-                        ),
3423
-                        $key,
3424
-                        var_export($value, true),
3425
-                        var_export($query_params, true),
3426
-                        get_class($this)
3427
-                    )
3428
-                );
3429
-            }
3430
-        }
3431
-        if (
3432
-            array_key_exists('default_where_conditions', $query_params)
3433
-            && ! empty($query_params['default_where_conditions'])
3434
-        ) {
3435
-            $use_default_where_conditions = $query_params['default_where_conditions'];
3436
-        } else {
3437
-            $use_default_where_conditions = EEM_Base::default_where_conditions_all;
3438
-        }
3439
-        $query_params[0] = array_merge(
3440
-            $this->_get_default_where_conditions_for_models_in_query(
3441
-                $query_object,
3442
-                $use_default_where_conditions,
3443
-                $query_params[0]
3444
-            ),
3445
-            $query_params[0]
3446
-        );
3447
-        $query_object->set_where_sql($this->_construct_where_clause($query_params[0]));
3448
-        // if this is a "on_join_limit" then we are limiting on on a specific table in a multi_table join.
3449
-        // So we need to setup a subquery and use that for the main join.
3450
-        // Note for now this only works on the primary table for the model.
3451
-        // So for instance, you could set the limit array like this:
3452
-        // array( 'on_join_limit' => array('Primary_Table_Alias', array(1,10) ) )
3453
-        if (array_key_exists('on_join_limit', $query_params) && ! empty($query_params['on_join_limit'])) {
3454
-            $query_object->set_main_model_join_sql(
3455
-                $this->_construct_limit_join_select(
3456
-                    $query_params['on_join_limit'][0],
3457
-                    $query_params['on_join_limit'][1]
3458
-                )
3459
-            );
3460
-        }
3461
-        // set limit
3462
-        if (array_key_exists('limit', $query_params)) {
3463
-            if (is_array($query_params['limit'])) {
3464
-                if (! isset($query_params['limit'][0], $query_params['limit'][1])) {
3465
-                    $e = sprintf(
3466
-                        __(
3467
-                            "Invalid DB query. You passed '%s' for the LIMIT, but only the following are valid: an integer, string representing an integer, a string like 'int,int', or an array like array(int,int)",
3468
-                            "event_espresso"
3469
-                        ),
3470
-                        http_build_query($query_params['limit'])
3471
-                    );
3472
-                    throw new EE_Error($e . "|" . $e);
3473
-                }
3474
-                // they passed us an array for the limit. Assume it's like array(50,25), meaning offset by 50, and get 25
3475
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit'][0] . "," . $query_params['limit'][1]);
3476
-            } elseif (! empty($query_params['limit'])) {
3477
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit']);
3478
-            }
3479
-        }
3480
-        // set order by
3481
-        if (array_key_exists('order_by', $query_params)) {
3482
-            if (is_array($query_params['order_by'])) {
3483
-                // if they're using 'order_by' as an array, they can't use 'order' (because 'order_by' must
3484
-                // specify whether to ascend or descend on each field. Eg 'order_by'=>array('EVT_ID'=>'ASC'). So
3485
-                // including 'order' wouldn't make any sense if 'order_by' has already specified which way to order!
3486
-                if (array_key_exists('order', $query_params)) {
3487
-                    throw new EE_Error(
3488
-                        sprintf(
3489
-                            __(
3490
-                                "In querying %s, we are using query parameter 'order_by' as an array (keys:%s,values:%s), and so we can't use query parameter 'order' (value %s). You should just use the 'order_by' parameter ",
3491
-                                "event_espresso"
3492
-                            ),
3493
-                            get_class($this),
3494
-                            implode(", ", array_keys($query_params['order_by'])),
3495
-                            implode(", ", $query_params['order_by']),
3496
-                            $query_params['order']
3497
-                        )
3498
-                    );
3499
-                }
3500
-                $this->_extract_related_models_from_sub_params_array_keys(
3501
-                    $query_params['order_by'],
3502
-                    $query_object,
3503
-                    'order_by'
3504
-                );
3505
-                // assume it's an array of fields to order by
3506
-                $order_array = array();
3507
-                foreach ($query_params['order_by'] as $field_name_to_order_by => $order) {
3508
-                    $order = $this->_extract_order($order);
3509
-                    $order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by) . SP . $order;
3510
-                }
3511
-                $query_object->set_order_by_sql(" ORDER BY " . implode(",", $order_array));
3512
-            } elseif (! empty($query_params['order_by'])) {
3513
-                $this->_extract_related_model_info_from_query_param(
3514
-                    $query_params['order_by'],
3515
-                    $query_object,
3516
-                    'order',
3517
-                    $query_params['order_by']
3518
-                );
3519
-                $order = isset($query_params['order'])
3520
-                    ? $this->_extract_order($query_params['order'])
3521
-                    : 'DESC';
3522
-                $query_object->set_order_by_sql(
3523
-                    " ORDER BY " . $this->_deduce_column_name_from_query_param($query_params['order_by']) . SP . $order
3524
-                );
3525
-            }
3526
-        }
3527
-        // if 'order_by' wasn't set, maybe they are just using 'order' on its own?
3528
-        if (
3529
-            ! array_key_exists('order_by', $query_params)
3530
-            && array_key_exists('order', $query_params)
3531
-            && ! empty($query_params['order'])
3532
-        ) {
3533
-            $pk_field = $this->get_primary_key_field();
3534
-            $order = $this->_extract_order($query_params['order']);
3535
-            $query_object->set_order_by_sql(" ORDER BY " . $pk_field->get_qualified_column() . SP . $order);
3536
-        }
3537
-        // set group by
3538
-        if (array_key_exists('group_by', $query_params)) {
3539
-            if (is_array($query_params['group_by'])) {
3540
-                // it's an array, so assume we'll be grouping by a bunch of stuff
3541
-                $group_by_array = array();
3542
-                foreach ($query_params['group_by'] as $field_name_to_group_by) {
3543
-                    $group_by_array[] = $this->_deduce_column_name_from_query_param($field_name_to_group_by);
3544
-                }
3545
-                $query_object->set_group_by_sql(" GROUP BY " . implode(", ", $group_by_array));
3546
-            } elseif (! empty($query_params['group_by'])) {
3547
-                $query_object->set_group_by_sql(
3548
-                    " GROUP BY " . $this->_deduce_column_name_from_query_param($query_params['group_by'])
3549
-                );
3550
-            }
3551
-        }
3552
-        // set having
3553
-        if (array_key_exists('having', $query_params) && $query_params['having']) {
3554
-            $query_object->set_having_sql($this->_construct_having_clause($query_params['having']));
3555
-        }
3556
-        // now, just verify they didn't pass anything wack
3557
-        foreach ($query_params as $query_key => $query_value) {
3558
-            if (! in_array($query_key, $this->_allowed_query_params, true)) {
3559
-                throw new EE_Error(
3560
-                    sprintf(
3561
-                        __(
3562
-                            "You passed %s as a query parameter to %s, which is illegal! The allowed query parameters are %s",
3563
-                            'event_espresso'
3564
-                        ),
3565
-                        $query_key,
3566
-                        get_class($this),
3567
-                        //                      print_r( $this->_allowed_query_params, TRUE )
3568
-                        implode(',', $this->_allowed_query_params)
3569
-                    )
3570
-                );
3571
-            }
3572
-        }
3573
-        $main_model_join_sql = $query_object->get_main_model_join_sql();
3574
-        if (empty($main_model_join_sql)) {
3575
-            $query_object->set_main_model_join_sql($this->_construct_internal_join());
3576
-        }
3577
-        return $query_object;
3578
-    }
3579
-
3580
-
3581
-
3582
-    /**
3583
-     * Gets the where conditions that should be imposed on the query based on the
3584
-     * context (eg reading frontend, backend, edit or delete).
3585
-     *
3586
-     * @param string $context one of EEM_Base::valid_cap_contexts()
3587
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3588
-     * @throws EE_Error
3589
-     */
3590
-    public function caps_where_conditions($context = self::caps_read)
3591
-    {
3592
-        EEM_Base::verify_is_valid_cap_context($context);
3593
-        $cap_where_conditions = array();
3594
-        $cap_restrictions = $this->caps_missing($context);
3595
-        /**
3596
-         * @var $cap_restrictions EE_Default_Where_Conditions[]
3597
-         */
3598
-        foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
3599
-            $cap_where_conditions = array_replace_recursive(
3600
-                $cap_where_conditions,
3601
-                $restriction_if_no_cap->get_default_where_conditions()
3602
-            );
3603
-        }
3604
-        return apply_filters(
3605
-            'FHEE__EEM_Base__caps_where_conditions__return',
3606
-            $cap_where_conditions,
3607
-            $this,
3608
-            $context,
3609
-            $cap_restrictions
3610
-        );
3611
-    }
3612
-
3613
-
3614
-
3615
-    /**
3616
-     * Verifies that $should_be_order_string is in $this->_allowed_order_values,
3617
-     * otherwise throws an exception
3618
-     *
3619
-     * @param string $should_be_order_string
3620
-     * @return string either ASC, asc, DESC or desc
3621
-     * @throws EE_Error
3622
-     */
3623
-    private function _extract_order($should_be_order_string)
3624
-    {
3625
-        if (in_array($should_be_order_string, $this->_allowed_order_values)) {
3626
-            return $should_be_order_string;
3627
-        }
3628
-        throw new EE_Error(
3629
-            sprintf(
3630
-                __(
3631
-                    "While performing a query on '%s', tried to use '%s' as an order parameter. ",
3632
-                    "event_espresso"
3633
-                ),
3634
-                get_class($this),
3635
-                $should_be_order_string
3636
-            )
3637
-        );
3638
-    }
3639
-
3640
-
3641
-
3642
-    /**
3643
-     * Looks at all the models which are included in this query, and asks each
3644
-     * for their universal_where_params, and returns them in the same format as $query_params[0] (where),
3645
-     * so they can be merged
3646
-     *
3647
-     * @param EE_Model_Query_Info_Carrier $query_info_carrier
3648
-     * @param string                      $use_default_where_conditions can be 'none','other_models_only', or 'all'.
3649
-     *                                                                  'none' means NO default where conditions will
3650
-     *                                                                  be used AT ALL during this query.
3651
-     *                                                                  'other_models_only' means default where
3652
-     *                                                                  conditions from other models will be used, but
3653
-     *                                                                  not for this primary model. 'all', the default,
3654
-     *                                                                  means default where conditions will apply as
3655
-     *                                                                  normal
3656
-     * @param array                       $where_query_params           @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3657
-     * @throws EE_Error
3658
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3659
-     */
3660
-    private function _get_default_where_conditions_for_models_in_query(
3661
-        EE_Model_Query_Info_Carrier $query_info_carrier,
3662
-        $use_default_where_conditions = EEM_Base::default_where_conditions_all,
3663
-        $where_query_params = array()
3664
-    ) {
3665
-        $allowed_used_default_where_conditions_values = EEM_Base::valid_default_where_conditions();
3666
-        if (! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3667
-            throw new EE_Error(sprintf(
3668
-                __(
3669
-                    "You passed an invalid value to the query parameter 'default_where_conditions' of '%s'. Allowed values are %s",
3670
-                    "event_espresso"
3671
-                ),
3672
-                $use_default_where_conditions,
3673
-                implode(", ", $allowed_used_default_where_conditions_values)
3674
-            ));
3675
-        }
3676
-        $universal_query_params = array();
3677
-        if ($this->_should_use_default_where_conditions($use_default_where_conditions, true)) {
3678
-            $universal_query_params = $this->_get_default_where_conditions();
3679
-        } elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, true)) {
3680
-            $universal_query_params = $this->_get_minimum_where_conditions();
3681
-        }
3682
-        foreach ($query_info_carrier->get_model_names_included() as $model_relation_path => $model_name) {
3683
-            $related_model = $this->get_related_model_obj($model_name);
3684
-            if ($this->_should_use_default_where_conditions($use_default_where_conditions, false)) {
3685
-                $related_model_universal_where_params = $related_model->_get_default_where_conditions($model_relation_path);
3686
-            } elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, false)) {
3687
-                $related_model_universal_where_params = $related_model->_get_minimum_where_conditions($model_relation_path);
3688
-            } else {
3689
-                // we don't want to add full or even minimum default where conditions from this model, so just continue
3690
-                continue;
3691
-            }
3692
-            $overrides = $this->_override_defaults_or_make_null_friendly(
3693
-                $related_model_universal_where_params,
3694
-                $where_query_params,
3695
-                $related_model,
3696
-                $model_relation_path
3697
-            );
3698
-            $universal_query_params = EEH_Array::merge_arrays_and_overwrite_keys(
3699
-                $universal_query_params,
3700
-                $overrides
3701
-            );
3702
-        }
3703
-        return $universal_query_params;
3704
-    }
3705
-
3706
-
3707
-
3708
-    /**
3709
-     * Determines whether or not we should use default where conditions for the model in question
3710
-     * (this model, or other related models).
3711
-     * Basically, we should use default where conditions on this model if they have requested to use them on all models,
3712
-     * this model only, or to use minimum where conditions on all other models and normal where conditions on this one.
3713
-     * We should use default where conditions on related models when they requested to use default where conditions
3714
-     * on all models, or specifically just on other related models
3715
-     * @param      $default_where_conditions_value
3716
-     * @param bool $for_this_model false means this is for OTHER related models
3717
-     * @return bool
3718
-     */
3719
-    private function _should_use_default_where_conditions($default_where_conditions_value, $for_this_model = true)
3720
-    {
3721
-        return (
3722
-                   $for_this_model
3723
-                   && in_array(
3724
-                       $default_where_conditions_value,
3725
-                       array(
3726
-                           EEM_Base::default_where_conditions_all,
3727
-                           EEM_Base::default_where_conditions_this_only,
3728
-                           EEM_Base::default_where_conditions_minimum_others,
3729
-                       ),
3730
-                       true
3731
-                   )
3732
-               )
3733
-               || (
3734
-                   ! $for_this_model
3735
-                   && in_array(
3736
-                       $default_where_conditions_value,
3737
-                       array(
3738
-                           EEM_Base::default_where_conditions_all,
3739
-                           EEM_Base::default_where_conditions_others_only,
3740
-                       ),
3741
-                       true
3742
-                   )
3743
-               );
3744
-    }
3745
-
3746
-    /**
3747
-     * Determines whether or not we should use default minimum conditions for the model in question
3748
-     * (this model, or other related models).
3749
-     * Basically, we should use minimum where conditions on this model only if they requested all models to use minimum
3750
-     * where conditions.
3751
-     * We should use minimum where conditions on related models if they requested to use minimum where conditions
3752
-     * on this model or others
3753
-     * @param      $default_where_conditions_value
3754
-     * @param bool $for_this_model false means this is for OTHER related models
3755
-     * @return bool
3756
-     */
3757
-    private function _should_use_minimum_where_conditions($default_where_conditions_value, $for_this_model = true)
3758
-    {
3759
-        return (
3760
-                   $for_this_model
3761
-                   && $default_where_conditions_value === EEM_Base::default_where_conditions_minimum_all
3762
-               )
3763
-               || (
3764
-                   ! $for_this_model
3765
-                   && in_array(
3766
-                       $default_where_conditions_value,
3767
-                       array(
3768
-                           EEM_Base::default_where_conditions_minimum_others,
3769
-                           EEM_Base::default_where_conditions_minimum_all,
3770
-                       ),
3771
-                       true
3772
-                   )
3773
-               );
3774
-    }
3775
-
3776
-
3777
-    /**
3778
-     * Checks if any of the defaults have been overridden. If there are any that AREN'T overridden,
3779
-     * then we also add a special where condition which allows for that model's primary key
3780
-     * to be null (which is important for JOINs. Eg, if you want to see all Events ordered by Venue's name,
3781
-     * then Event's with NO Venue won't appear unless you allow VNU_ID to be NULL)
3782
-     *
3783
-     * @param array    $default_where_conditions
3784
-     * @param array    $provided_where_conditions
3785
-     * @param EEM_Base $model
3786
-     * @param string   $model_relation_path like 'Transaction.Payment.'
3787
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3788
-     * @throws EE_Error
3789
-     */
3790
-    private function _override_defaults_or_make_null_friendly(
3791
-        $default_where_conditions,
3792
-        $provided_where_conditions,
3793
-        $model,
3794
-        $model_relation_path
3795
-    ) {
3796
-        $null_friendly_where_conditions = array();
3797
-        $none_overridden = true;
3798
-        $or_condition_key_for_defaults = 'OR*' . get_class($model);
3799
-        foreach ($default_where_conditions as $key => $val) {
3800
-            if (isset($provided_where_conditions[ $key ])) {
3801
-                $none_overridden = false;
3802
-            } else {
3803
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ]['AND'][ $key ] = $val;
3804
-            }
3805
-        }
3806
-        if ($none_overridden && $default_where_conditions) {
3807
-            if ($model->has_primary_key_field()) {
3808
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ][ $model_relation_path
3809
-                                                                                . "."
3810
-                                                                                . $model->primary_key_name() ] = array('IS NULL');
3811
-            }/*else{
38
+	/**
39
+	 * Flag to indicate whether the values provided to EEM_Base have already been prepared
40
+	 * by the model object or not (ie, the model object has used the field's _prepare_for_set function on the values).
41
+	 * They almost always WILL NOT, but it's not necessarily a requirement.
42
+	 * For example, if you want to run EEM_Event::instance()->get_all(array(array('EVT_ID'=>$_GET['event_id'])));
43
+	 *
44
+	 * @var boolean
45
+	 */
46
+	private $_values_already_prepared_by_model_object = 0;
47
+
48
+	/**
49
+	 * when $_values_already_prepared_by_model_object equals this, we assume
50
+	 * the data is just like form input that needs to have the model fields'
51
+	 * prepare_for_set and prepare_for_use_in_db called on it
52
+	 */
53
+	const not_prepared_by_model_object = 0;
54
+
55
+	/**
56
+	 * when $_values_already_prepared_by_model_object equals this, we
57
+	 * assume this value is coming from a model object and doesn't need to have
58
+	 * prepare_for_set called on it, just prepare_for_use_in_db is used
59
+	 */
60
+	const prepared_by_model_object = 1;
61
+
62
+	/**
63
+	 * when $_values_already_prepared_by_model_object equals this, we assume
64
+	 * the values are already to be used in the database (ie no processing is done
65
+	 * on them by the model's fields)
66
+	 */
67
+	const prepared_for_use_in_db = 2;
68
+
69
+
70
+	protected $singular_item = 'Item';
71
+
72
+	protected $plural_item   = 'Items';
73
+
74
+	/**
75
+	 * @type \EE_Table_Base[] $_tables array of EE_Table objects for defining which tables comprise this model.
76
+	 */
77
+	protected $_tables;
78
+
79
+	/**
80
+	 * with two levels: top-level has array keys which are database table aliases (ie, keys in _tables)
81
+	 * and the value is an array. Each of those sub-arrays have keys of field names (eg 'ATT_ID', which should also be
82
+	 * variable names on the model objects (eg, EE_Attendee), and the keys should be children of EE_Model_Field
83
+	 *
84
+	 * @var \EE_Model_Field_Base[][] $_fields
85
+	 */
86
+	protected $_fields;
87
+
88
+	/**
89
+	 * array of different kinds of relations
90
+	 *
91
+	 * @var \EE_Model_Relation_Base[] $_model_relations
92
+	 */
93
+	protected $_model_relations;
94
+
95
+	/**
96
+	 * @var \EE_Index[] $_indexes
97
+	 */
98
+	protected $_indexes = array();
99
+
100
+	/**
101
+	 * Default strategy for getting where conditions on this model. This strategy is used to get default
102
+	 * where conditions which are added to get_all, update, and delete queries. They can be overridden
103
+	 * by setting the same columns as used in these queries in the query yourself.
104
+	 *
105
+	 * @var EE_Default_Where_Conditions
106
+	 */
107
+	protected $_default_where_conditions_strategy;
108
+
109
+	/**
110
+	 * Strategy for getting conditions on this model when 'default_where_conditions' equals 'minimum'.
111
+	 * This is particularly useful when you want something between 'none' and 'default'
112
+	 *
113
+	 * @var EE_Default_Where_Conditions
114
+	 */
115
+	protected $_minimum_where_conditions_strategy;
116
+
117
+	/**
118
+	 * String describing how to find the "owner" of this model's objects.
119
+	 * When there is a foreign key on this model to the wp_users table, this isn't needed.
120
+	 * But when there isn't, this indicates which related model, or transiently-related model,
121
+	 * has the foreign key to the wp_users table.
122
+	 * Eg, for EEM_Registration this would be 'Event' because registrations are directly
123
+	 * related to events, and events have a foreign key to wp_users.
124
+	 * On EEM_Transaction, this would be 'Transaction.Event'
125
+	 *
126
+	 * @var string
127
+	 */
128
+	protected $_model_chain_to_wp_user = '';
129
+
130
+	/**
131
+	 * String describing how to find the model with a password controlling access to this model. This property has the
132
+	 * same format as $_model_chain_to_wp_user. This is primarily used by the query param "exclude_protected".
133
+	 * This value is the path of models to follow to arrive at the model with the password field.
134
+	 * If it is an empty string, it means this model has the password field. If it is null, it means there is no
135
+	 * model with a password that should affect reading this on the front-end.
136
+	 * Eg this is an empty string for the Event model because it has a password.
137
+	 * This is null for the Registration model, because its event's password has no bearing on whether
138
+	 * you can read the registration or not on the front-end (it just depends on your capabilities.)
139
+	 * This is 'Datetime.Event' on the Ticket model, because model queries for tickets that set "exclude_protected"
140
+	 * should hide tickets for datetimes for events that have a password set.
141
+	 * @var string |null
142
+	 */
143
+	protected $model_chain_to_password = null;
144
+
145
+	/**
146
+	 * This is a flag typically set by updates so that we don't load the where strategy on updates because updates
147
+	 * don't need it (particularly CPT models)
148
+	 *
149
+	 * @var bool
150
+	 */
151
+	protected $_ignore_where_strategy = false;
152
+
153
+	/**
154
+	 * String used in caps relating to this model. Eg, if the caps relating to this
155
+	 * model are 'ee_edit_events', 'ee_read_events', etc, it would be 'events'.
156
+	 *
157
+	 * @var string. If null it hasn't been initialized yet. If false then we
158
+	 * have indicated capabilities don't apply to this
159
+	 */
160
+	protected $_caps_slug = null;
161
+
162
+	/**
163
+	 * 2d array where top-level keys are one of EEM_Base::valid_cap_contexts(),
164
+	 * and next-level keys are capability names, and each's value is a
165
+	 * EE_Default_Where_Condition. If the requester requests to apply caps to the query,
166
+	 * they specify which context to use (ie, frontend, backend, edit or delete)
167
+	 * and then each capability in the corresponding sub-array that they're missing
168
+	 * adds the where conditions onto the query.
169
+	 *
170
+	 * @var array
171
+	 */
172
+	protected $_cap_restrictions = array(
173
+		self::caps_read       => array(),
174
+		self::caps_read_admin => array(),
175
+		self::caps_edit       => array(),
176
+		self::caps_delete     => array(),
177
+	);
178
+
179
+	/**
180
+	 * Array defining which cap restriction generators to use to create default
181
+	 * cap restrictions to put in EEM_Base::_cap_restrictions.
182
+	 * Array-keys are one of EEM_Base::valid_cap_contexts(), and values are a child of
183
+	 * EE_Restriction_Generator_Base. If you don't want any cap restrictions generated
184
+	 * automatically set this to false (not just null).
185
+	 *
186
+	 * @var EE_Restriction_Generator_Base[]
187
+	 */
188
+	protected $_cap_restriction_generators = array();
189
+
190
+	/**
191
+	 * constants used to categorize capability restrictions on EEM_Base::_caps_restrictions
192
+	 */
193
+	const caps_read       = 'read';
194
+
195
+	const caps_read_admin = 'read_admin';
196
+
197
+	const caps_edit       = 'edit';
198
+
199
+	const caps_delete     = 'delete';
200
+
201
+	/**
202
+	 * Keys are all the cap contexts (ie constants EEM_Base::_caps_*) and values are their 'action'
203
+	 * as how they'd be used in capability names. Eg EEM_Base::caps_read ('read_frontend')
204
+	 * maps to 'read' because when looking for relevant permissions we're going to use
205
+	 * 'read' in teh capabilities names like 'ee_read_events' etc.
206
+	 *
207
+	 * @var array
208
+	 */
209
+	protected $_cap_contexts_to_cap_action_map = array(
210
+		self::caps_read       => 'read',
211
+		self::caps_read_admin => 'read',
212
+		self::caps_edit       => 'edit',
213
+		self::caps_delete     => 'delete',
214
+	);
215
+
216
+	/**
217
+	 * Timezone
218
+	 * This gets set via the constructor so that we know what timezone incoming strings|timestamps are in when there
219
+	 * are EE_Datetime_Fields in use.  This can also be used before a get to set what timezone you want strings coming
220
+	 * out of the created objects.  NOT all EEM_Base child classes use this property but any that use a
221
+	 * EE_Datetime_Field data type will have access to it.
222
+	 *
223
+	 * @var string
224
+	 */
225
+	protected $_timezone;
226
+
227
+
228
+	/**
229
+	 * This holds the id of the blog currently making the query.  Has no bearing on single site but is used for
230
+	 * multisite.
231
+	 *
232
+	 * @var int
233
+	 */
234
+	protected static $_model_query_blog_id;
235
+
236
+	/**
237
+	 * A copy of _fields, except the array keys are the model names pointed to by
238
+	 * the field
239
+	 *
240
+	 * @var EE_Model_Field_Base[]
241
+	 */
242
+	private $_cache_foreign_key_to_fields = array();
243
+
244
+	/**
245
+	 * Cached list of all the fields on the model, indexed by their name
246
+	 *
247
+	 * @var EE_Model_Field_Base[]
248
+	 */
249
+	private $_cached_fields = null;
250
+
251
+	/**
252
+	 * Cached list of all the fields on the model, except those that are
253
+	 * marked as only pertinent to the database
254
+	 *
255
+	 * @var EE_Model_Field_Base[]
256
+	 */
257
+	private $_cached_fields_non_db_only = null;
258
+
259
+	/**
260
+	 * A cached reference to the primary key for quick lookup
261
+	 *
262
+	 * @var EE_Model_Field_Base
263
+	 */
264
+	private $_primary_key_field = null;
265
+
266
+	/**
267
+	 * Flag indicating whether this model has a primary key or not
268
+	 *
269
+	 * @var boolean
270
+	 */
271
+	protected $_has_primary_key_field = null;
272
+
273
+	/**
274
+	 * array in the format:  [ FK alias => full PK ]
275
+	 * where keys are local column name aliases for foreign keys
276
+	 * and values are the fully qualified column name for the primary key they represent
277
+	 *  ex:
278
+	 *      [ 'Event.EVT_wp_user' => 'WP_User.ID' ]
279
+	 *
280
+	 * @var array $foreign_key_aliases
281
+	 */
282
+	protected $foreign_key_aliases = [];
283
+
284
+	/**
285
+	 * Whether or not this model is based off a table in WP core only (CPTs should set
286
+	 * this to FALSE, but if we were to make an EE_WP_Post model, it should set this to true).
287
+	 * This should be true for models that deal with data that should exist independent of EE.
288
+	 * For example, if the model can read and insert data that isn't used by EE, this should be true.
289
+	 * It would be false, however, if you could guarantee the model would only interact with EE data,
290
+	 * even if it uses a WP core table (eg event and venue models set this to false for that reason:
291
+	 * they can only read and insert events and venues custom post types, not arbitrary post types)
292
+	 * @var boolean
293
+	 */
294
+	protected $_wp_core_model = false;
295
+
296
+	/**
297
+	 * @var bool stores whether this model has a password field or not.
298
+	 * null until initialized by hasPasswordField()
299
+	 */
300
+	protected $has_password_field;
301
+
302
+	/**
303
+	 * @var EE_Password_Field|null Automatically set when calling getPasswordField()
304
+	 */
305
+	protected $password_field;
306
+
307
+	/**
308
+	 *    List of valid operators that can be used for querying.
309
+	 * The keys are all operators we'll accept, the values are the real SQL
310
+	 * operators used
311
+	 *
312
+	 * @var array
313
+	 */
314
+	protected $_valid_operators = array(
315
+		'='           => '=',
316
+		'<='          => '<=',
317
+		'<'           => '<',
318
+		'>='          => '>=',
319
+		'>'           => '>',
320
+		'!='          => '!=',
321
+		'LIKE'        => 'LIKE',
322
+		'like'        => 'LIKE',
323
+		'NOT_LIKE'    => 'NOT LIKE',
324
+		'not_like'    => 'NOT LIKE',
325
+		'NOT LIKE'    => 'NOT LIKE',
326
+		'not like'    => 'NOT LIKE',
327
+		'IN'          => 'IN',
328
+		'in'          => 'IN',
329
+		'NOT_IN'      => 'NOT IN',
330
+		'not_in'      => 'NOT IN',
331
+		'NOT IN'      => 'NOT IN',
332
+		'not in'      => 'NOT IN',
333
+		'between'     => 'BETWEEN',
334
+		'BETWEEN'     => 'BETWEEN',
335
+		'IS_NOT_NULL' => 'IS NOT NULL',
336
+		'is_not_null' => 'IS NOT NULL',
337
+		'IS NOT NULL' => 'IS NOT NULL',
338
+		'is not null' => 'IS NOT NULL',
339
+		'IS_NULL'     => 'IS NULL',
340
+		'is_null'     => 'IS NULL',
341
+		'IS NULL'     => 'IS NULL',
342
+		'is null'     => 'IS NULL',
343
+		'REGEXP'      => 'REGEXP',
344
+		'regexp'      => 'REGEXP',
345
+		'NOT_REGEXP'  => 'NOT REGEXP',
346
+		'not_regexp'  => 'NOT REGEXP',
347
+		'NOT REGEXP'  => 'NOT REGEXP',
348
+		'not regexp'  => 'NOT REGEXP',
349
+	);
350
+
351
+	/**
352
+	 * operators that work like 'IN', accepting a comma-separated list of values inside brackets. Eg '(1,2,3)'
353
+	 *
354
+	 * @var array
355
+	 */
356
+	protected $_in_style_operators = array('IN', 'NOT IN');
357
+
358
+	/**
359
+	 * operators that work like 'BETWEEN'.  Typically used for datetime calculations, i.e. "BETWEEN '12-1-2011' AND
360
+	 * '12-31-2012'"
361
+	 *
362
+	 * @var array
363
+	 */
364
+	protected $_between_style_operators = array('BETWEEN');
365
+
366
+	/**
367
+	 * Operators that work like SQL's like: input should be assumed to be a string, already prepared for a LIKE query.
368
+	 * @var array
369
+	 */
370
+	protected $_like_style_operators = array('LIKE', 'NOT LIKE');
371
+	/**
372
+	 * operators that are used for handling NUll and !NULL queries.  Typically used for when checking if a row exists
373
+	 * on a join table.
374
+	 *
375
+	 * @var array
376
+	 */
377
+	protected $_null_style_operators = array('IS NOT NULL', 'IS NULL');
378
+
379
+	/**
380
+	 * Allowed values for $query_params['order'] for ordering in queries
381
+	 *
382
+	 * @var array
383
+	 */
384
+	protected $_allowed_order_values = array('asc', 'desc', 'ASC', 'DESC');
385
+
386
+	/**
387
+	 * When these are keys in a WHERE or HAVING clause, they are handled much differently
388
+	 * than regular field names. It is assumed that their values are an array of WHERE conditions
389
+	 *
390
+	 * @var array
391
+	 */
392
+	private $_logic_query_param_keys = array('not', 'and', 'or', 'NOT', 'AND', 'OR');
393
+
394
+	/**
395
+	 * Allowed keys in $query_params arrays passed into queries. Note that 0 is meant to always be a
396
+	 * 'where', but 'where' clauses are so common that we thought we'd omit it
397
+	 *
398
+	 * @var array
399
+	 */
400
+	private $_allowed_query_params = array(
401
+		0,
402
+		'limit',
403
+		'order_by',
404
+		'group_by',
405
+		'having',
406
+		'force_join',
407
+		'order',
408
+		'on_join_limit',
409
+		'default_where_conditions',
410
+		'caps',
411
+		'extra_selects',
412
+		'exclude_protected',
413
+	);
414
+
415
+	/**
416
+	 * All the data types that can be used in $wpdb->prepare statements.
417
+	 *
418
+	 * @var array
419
+	 */
420
+	private $_valid_wpdb_data_types = array('%d', '%s', '%f');
421
+
422
+	/**
423
+	 * @var EE_Registry $EE
424
+	 */
425
+	protected $EE = null;
426
+
427
+
428
+	/**
429
+	 * Property which, when set, will have this model echo out the next X queries to the page for debugging.
430
+	 *
431
+	 * @var int
432
+	 */
433
+	protected $_show_next_x_db_queries = 0;
434
+
435
+	/**
436
+	 * When using _get_all_wpdb_results, you can specify a custom selection. If you do so,
437
+	 * it gets saved on this property as an instance of CustomSelects so those selections can be used in
438
+	 * WHERE, GROUP_BY, etc.
439
+	 *
440
+	 * @var CustomSelects
441
+	 */
442
+	protected $_custom_selections = array();
443
+
444
+	/**
445
+	 * key => value Entity Map using  array( EEM_Base::$_model_query_blog_id => array( ID => model object ) )
446
+	 * caches every model object we've fetched from the DB on this request
447
+	 *
448
+	 * @var array
449
+	 */
450
+	protected $_entity_map;
451
+
452
+	/**
453
+	 * @var LoaderInterface $loader
454
+	 */
455
+	private static $loader;
456
+
457
+
458
+	/**
459
+	 * constant used to show EEM_Base has not yet verified the db on this http request
460
+	 */
461
+	const db_verified_none = 0;
462
+
463
+	/**
464
+	 * constant used to show EEM_Base has verified the EE core db on this http request,
465
+	 * but not the addons' dbs
466
+	 */
467
+	const db_verified_core = 1;
468
+
469
+	/**
470
+	 * constant used to show EEM_Base has verified the addons' dbs (and implicitly
471
+	 * the EE core db too)
472
+	 */
473
+	const db_verified_addons = 2;
474
+
475
+	/**
476
+	 * indicates whether an EEM_Base child has already re-verified the DB
477
+	 * is ok (we don't want to do it repetitively). Should be set to one the constants
478
+	 * looking like EEM_Base::db_verified_*
479
+	 *
480
+	 * @var int - 0 = none, 1 = core, 2 = addons
481
+	 */
482
+	protected static $_db_verification_level = EEM_Base::db_verified_none;
483
+
484
+	/**
485
+	 * @const constant for 'default_where_conditions' to apply default where conditions to ALL queried models
486
+	 *        (eg, if retrieving registrations ordered by their datetimes, this will only return non-trashed
487
+	 *        registrations for non-trashed tickets for non-trashed datetimes)
488
+	 */
489
+	const default_where_conditions_all = 'all';
490
+
491
+	/**
492
+	 * @const constant for 'default_where_conditions' to apply default where conditions to THIS model only, but
493
+	 *        no other models which are joined to (eg, if retrieving registrations ordered by their datetimes, this will
494
+	 *        return non-trashed registrations, regardless of the related datetimes and tickets' statuses).
495
+	 *        It is preferred to use EEM_Base::default_where_conditions_minimum_others because, when joining to
496
+	 *        models which share tables with other models, this can return data for the wrong model.
497
+	 */
498
+	const default_where_conditions_this_only = 'this_model_only';
499
+
500
+	/**
501
+	 * @const constant for 'default_where_conditions' to apply default where conditions to other models queried,
502
+	 *        but not the current model (eg, if retrieving registrations ordered by their datetimes, this will
503
+	 *        return all registrations related to non-trashed tickets and non-trashed datetimes)
504
+	 */
505
+	const default_where_conditions_others_only = 'other_models_only';
506
+
507
+	/**
508
+	 * @const constant for 'default_where_conditions' to apply minimum where conditions to all models queried.
509
+	 *        For most models this the same as EEM_Base::default_where_conditions_none, except for models which share
510
+	 *        their table with other models, like the Event and Venue models. For example, when querying for events
511
+	 *        ordered by their venues' name, this will be sure to only return real events with associated real venues
512
+	 *        (regardless of whether those events and venues are trashed)
513
+	 *        In contrast, using EEM_Base::default_where_conditions_none would could return WP posts other than EE
514
+	 *        events.
515
+	 */
516
+	const default_where_conditions_minimum_all = 'minimum';
517
+
518
+	/**
519
+	 * @const constant for 'default_where_conditions' to apply apply where conditions to other models, and full default
520
+	 *        where conditions for the queried model (eg, when querying events ordered by venues' names, this will
521
+	 *        return non-trashed events for any venues, regardless of whether those associated venues are trashed or
522
+	 *        not)
523
+	 */
524
+	const default_where_conditions_minimum_others = 'full_this_minimum_others';
525
+
526
+	/**
527
+	 * @const constant for 'default_where_conditions' to NOT apply any where conditions. This should very rarely be
528
+	 *        used, because when querying from a model which shares its table with another model (eg Events and Venues)
529
+	 *        it's possible it will return table entries for other models. You should use
530
+	 *        EEM_Base::default_where_conditions_minimum_all instead.
531
+	 */
532
+	const default_where_conditions_none = 'none';
533
+
534
+
535
+
536
+	/**
537
+	 * About all child constructors:
538
+	 * they should define the _tables, _fields and _model_relations arrays.
539
+	 * Should ALWAYS be called after child constructor.
540
+	 * In order to make the child constructors to be as simple as possible, this parent constructor
541
+	 * finalizes constructing all the object's attributes.
542
+	 * Generally, rather than requiring a child to code
543
+	 * $this->_tables = array(
544
+	 *        'Event_Post_Table' => new EE_Table('Event_Post_Table','wp_posts')
545
+	 *        ...);
546
+	 *  (thus repeating itself in the array key and in the constructor of the new EE_Table,)
547
+	 * each EE_Table has a function to set the table's alias after the constructor, using
548
+	 * the array key ('Event_Post_Table'), instead of repeating it. The model fields and model relations
549
+	 * do something similar.
550
+	 *
551
+	 * @param null $timezone
552
+	 * @throws EE_Error
553
+	 */
554
+	protected function __construct($timezone = null)
555
+	{
556
+		// check that the model has not been loaded too soon
557
+		if (! did_action('AHEE__EE_System__load_espresso_addons')) {
558
+			throw new EE_Error(
559
+				sprintf(
560
+					__(
561
+						'The %1$s model can not be loaded before the "AHEE__EE_System__load_espresso_addons" hook has been called. This gives other addons a chance to extend this model.',
562
+						'event_espresso'
563
+					),
564
+					get_class($this)
565
+				)
566
+			);
567
+		}
568
+		/**
569
+		 * Set blogid for models to current blog. However we ONLY do this if $_model_query_blog_id is not already set.
570
+		 */
571
+		if (empty(EEM_Base::$_model_query_blog_id)) {
572
+			EEM_Base::set_model_query_blog_id();
573
+		}
574
+		/**
575
+		 * Filters the list of tables on a model. It is best to NOT use this directly and instead
576
+		 * just use EE_Register_Model_Extension
577
+		 *
578
+		 * @var EE_Table_Base[] $_tables
579
+		 */
580
+		$this->_tables = (array) apply_filters('FHEE__' . get_class($this) . '__construct__tables', $this->_tables);
581
+		foreach ($this->_tables as $table_alias => $table_obj) {
582
+			/** @var $table_obj EE_Table_Base */
583
+			$table_obj->_construct_finalize_with_alias($table_alias);
584
+			if ($table_obj instanceof EE_Secondary_Table) {
585
+				/** @var $table_obj EE_Secondary_Table */
586
+				$table_obj->_construct_finalize_set_table_to_join_with($this->_get_main_table());
587
+			}
588
+		}
589
+		/**
590
+		 * Filters the list of fields on a model. It is best to NOT use this directly and instead just use
591
+		 * EE_Register_Model_Extension
592
+		 *
593
+		 * @param EE_Model_Field_Base[] $_fields
594
+		 */
595
+		$this->_fields = (array) apply_filters('FHEE__' . get_class($this) . '__construct__fields', $this->_fields);
596
+		$this->_invalidate_field_caches();
597
+		foreach ($this->_fields as $table_alias => $fields_for_table) {
598
+			if (! array_key_exists($table_alias, $this->_tables)) {
599
+				throw new EE_Error(sprintf(__(
600
+					"Table alias %s does not exist in EEM_Base child's _tables array. Only tables defined are %s",
601
+					'event_espresso'
602
+				), $table_alias, implode(",", $this->_fields)));
603
+			}
604
+			foreach ($fields_for_table as $field_name => $field_obj) {
605
+				/** @var $field_obj EE_Model_Field_Base | EE_Primary_Key_Field_Base */
606
+				// primary key field base has a slightly different _construct_finalize
607
+				/** @var $field_obj EE_Model_Field_Base */
608
+				$field_obj->_construct_finalize($table_alias, $field_name, $this->get_this_model_name());
609
+			}
610
+		}
611
+		// everything is related to Extra_Meta
612
+		if (get_class($this) !== 'EEM_Extra_Meta') {
613
+			// make extra meta related to everything, but don't block deleting things just
614
+			// because they have related extra meta info. For now just orphan those extra meta
615
+			// in the future we should automatically delete them
616
+			$this->_model_relations['Extra_Meta'] = new EE_Has_Many_Any_Relation(false);
617
+		}
618
+		// and change logs
619
+		if (get_class($this) !== 'EEM_Change_Log') {
620
+			$this->_model_relations['Change_Log'] = new EE_Has_Many_Any_Relation(false);
621
+		}
622
+		/**
623
+		 * Filters the list of relations on a model. It is best to NOT use this directly and instead just use
624
+		 * EE_Register_Model_Extension
625
+		 *
626
+		 * @param EE_Model_Relation_Base[] $_model_relations
627
+		 */
628
+		$this->_model_relations = (array) apply_filters(
629
+			'FHEE__' . get_class($this) . '__construct__model_relations',
630
+			$this->_model_relations
631
+		);
632
+		foreach ($this->_model_relations as $model_name => $relation_obj) {
633
+			/** @var $relation_obj EE_Model_Relation_Base */
634
+			$relation_obj->_construct_finalize_set_models($this->get_this_model_name(), $model_name);
635
+		}
636
+		foreach ($this->_indexes as $index_name => $index_obj) {
637
+			/** @var $index_obj EE_Index */
638
+			$index_obj->_construct_finalize($index_name, $this->get_this_model_name());
639
+		}
640
+		$this->set_timezone($timezone);
641
+		// finalize default where condition strategy, or set default
642
+		if (! $this->_default_where_conditions_strategy) {
643
+			// nothing was set during child constructor, so set default
644
+			$this->_default_where_conditions_strategy = new EE_Default_Where_Conditions();
645
+		}
646
+		$this->_default_where_conditions_strategy->_finalize_construct($this);
647
+		if (! $this->_minimum_where_conditions_strategy) {
648
+			// nothing was set during child constructor, so set default
649
+			$this->_minimum_where_conditions_strategy = new EE_Default_Where_Conditions();
650
+		}
651
+		$this->_minimum_where_conditions_strategy->_finalize_construct($this);
652
+		// if the cap slug hasn't been set, and we haven't set it to false on purpose
653
+		// to indicate to NOT set it, set it to the logical default
654
+		if ($this->_caps_slug === null) {
655
+			$this->_caps_slug = EEH_Inflector::pluralize_and_lower($this->get_this_model_name());
656
+		}
657
+		// initialize the standard cap restriction generators if none were specified by the child constructor
658
+		if ($this->_cap_restriction_generators !== false) {
659
+			foreach ($this->cap_contexts_to_cap_action_map() as $cap_context => $action) {
660
+				if (! isset($this->_cap_restriction_generators[ $cap_context ])) {
661
+					$this->_cap_restriction_generators[ $cap_context ] = apply_filters(
662
+						'FHEE__EEM_Base___construct__standard_cap_restriction_generator',
663
+						new EE_Restriction_Generator_Protected(),
664
+						$cap_context,
665
+						$this
666
+					);
667
+				}
668
+			}
669
+		}
670
+		// if there are cap restriction generators, use them to make the default cap restrictions
671
+		if ($this->_cap_restriction_generators !== false) {
672
+			foreach ($this->_cap_restriction_generators as $context => $generator_object) {
673
+				if (! $generator_object) {
674
+					continue;
675
+				}
676
+				if (! $generator_object instanceof EE_Restriction_Generator_Base) {
677
+					throw new EE_Error(
678
+						sprintf(
679
+							__(
680
+								'Index "%1$s" in the model %2$s\'s _cap_restriction_generators is not a child of EE_Restriction_Generator_Base. It should be that or NULL.',
681
+								'event_espresso'
682
+							),
683
+							$context,
684
+							$this->get_this_model_name()
685
+						)
686
+					);
687
+				}
688
+				$action = $this->cap_action_for_context($context);
689
+				if (! $generator_object->construction_finalized()) {
690
+					$generator_object->_construct_finalize($this, $action);
691
+				}
692
+			}
693
+		}
694
+		do_action('AHEE__' . get_class($this) . '__construct__end');
695
+	}
696
+
697
+
698
+
699
+	/**
700
+	 * Used to set the $_model_query_blog_id static property.
701
+	 *
702
+	 * @param int $blog_id  If provided then will set the blog_id for the models to this id.  If not provided then the
703
+	 *                      value for get_current_blog_id() will be used.
704
+	 */
705
+	public static function set_model_query_blog_id($blog_id = 0)
706
+	{
707
+		EEM_Base::$_model_query_blog_id = $blog_id > 0 ? (int) $blog_id : get_current_blog_id();
708
+	}
709
+
710
+
711
+
712
+	/**
713
+	 * Returns whatever is set as the internal $model_query_blog_id.
714
+	 *
715
+	 * @return int
716
+	 */
717
+	public static function get_model_query_blog_id()
718
+	{
719
+		return EEM_Base::$_model_query_blog_id;
720
+	}
721
+
722
+
723
+
724
+	/**
725
+	 * This function is a singleton method used to instantiate the Espresso_model object
726
+	 *
727
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings
728
+	 *                                (and any incoming timezone data that gets saved).
729
+	 *                                Note this just sends the timezone info to the date time model field objects.
730
+	 *                                Default is NULL
731
+	 *                                (and will be assumed using the set timezone in the 'timezone_string' wp option)
732
+	 * @return static (as in the concrete child class)
733
+	 * @throws EE_Error
734
+	 * @throws InvalidArgumentException
735
+	 * @throws InvalidDataTypeException
736
+	 * @throws InvalidInterfaceException
737
+	 */
738
+	public static function instance($timezone = null)
739
+	{
740
+		// check if instance of Espresso_model already exists
741
+		if (! static::$_instance instanceof static) {
742
+			// instantiate Espresso_model
743
+			static::$_instance = new static(
744
+				$timezone,
745
+				LoaderFactory::getLoader()->load('EventEspresso\core\services\orm\ModelFieldFactory')
746
+			);
747
+		}
748
+		// we might have a timezone set, let set_timezone decide what to do with it
749
+		static::$_instance->set_timezone($timezone);
750
+		// Espresso_model object
751
+		return static::$_instance;
752
+	}
753
+
754
+
755
+
756
+	/**
757
+	 * resets the model and returns it
758
+	 *
759
+	 * @param null | string $timezone
760
+	 * @return EEM_Base|null (if the model was already instantiated, returns it, with
761
+	 * all its properties reset; if it wasn't instantiated, returns null)
762
+	 * @throws EE_Error
763
+	 * @throws ReflectionException
764
+	 * @throws InvalidArgumentException
765
+	 * @throws InvalidDataTypeException
766
+	 * @throws InvalidInterfaceException
767
+	 */
768
+	public static function reset($timezone = null)
769
+	{
770
+		if (static::$_instance instanceof EEM_Base) {
771
+			// let's try to NOT swap out the current instance for a new one
772
+			// because if someone has a reference to it, we can't remove their reference
773
+			// so it's best to keep using the same reference, but change the original object
774
+			// reset all its properties to their original values as defined in the class
775
+			$r = new ReflectionClass(get_class(static::$_instance));
776
+			$static_properties = $r->getStaticProperties();
777
+			foreach ($r->getDefaultProperties() as $property => $value) {
778
+				// don't set instance to null like it was originally,
779
+				// but it's static anyways, and we're ignoring static properties (for now at least)
780
+				if (! isset($static_properties[ $property ])) {
781
+					static::$_instance->{$property} = $value;
782
+				}
783
+			}
784
+			// and then directly call its constructor again, like we would if we were creating a new one
785
+			static::$_instance->__construct(
786
+				$timezone,
787
+				LoaderFactory::getLoader()->load('EventEspresso\core\services\orm\ModelFieldFactory')
788
+			);
789
+			return self::instance();
790
+		}
791
+		return null;
792
+	}
793
+
794
+
795
+
796
+	/**
797
+	 * @return LoaderInterface
798
+	 * @throws InvalidArgumentException
799
+	 * @throws InvalidDataTypeException
800
+	 * @throws InvalidInterfaceException
801
+	 */
802
+	private static function getLoader()
803
+	{
804
+		if (! EEM_Base::$loader instanceof LoaderInterface) {
805
+			EEM_Base::$loader = LoaderFactory::getLoader();
806
+		}
807
+		return EEM_Base::$loader;
808
+	}
809
+
810
+
811
+
812
+	/**
813
+	 * retrieve the status details from esp_status table as an array IF this model has the status table as a relation.
814
+	 *
815
+	 * @param  boolean $translated return localized strings or JUST the array.
816
+	 * @return array
817
+	 * @throws EE_Error
818
+	 * @throws InvalidArgumentException
819
+	 * @throws InvalidDataTypeException
820
+	 * @throws InvalidInterfaceException
821
+	 */
822
+	public function status_array($translated = false)
823
+	{
824
+		if (! array_key_exists('Status', $this->_model_relations)) {
825
+			return array();
826
+		}
827
+		$model_name = $this->get_this_model_name();
828
+		$status_type = str_replace(' ', '_', strtolower(str_replace('_', ' ', $model_name)));
829
+		$stati = EEM_Status::instance()->get_all(array(array('STS_type' => $status_type)));
830
+		$status_array = array();
831
+		foreach ($stati as $status) {
832
+			$status_array[ $status->ID() ] = $status->get('STS_code');
833
+		}
834
+		return $translated
835
+			? EEM_Status::instance()->localized_status($status_array, false, 'sentence')
836
+			: $status_array;
837
+	}
838
+
839
+
840
+
841
+	/**
842
+	 * Gets all the EE_Base_Class objects which match the $query_params, by querying the DB.
843
+	 *
844
+	 * @param array $query_params  @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
845
+	 *                             or if you have the development copy of EE you can view this at the path:
846
+	 *                             /docs/G--Model-System/model-query-params.md
847
+	 * @return EE_Base_Class[]  *note that there is NO option to pass the output type. If you want results different
848
+	 *                                        from EE_Base_Class[], use get_all_wpdb_results(). Array keys are object IDs (if there is a primary key on the model.
849
+	 *                                        if not, numerically indexed) Some full examples: get 10 transactions
850
+	 *                                        which have Scottish attendees: EEM_Transaction::instance()->get_all(
851
+	 *                                        array( array(
852
+	 *                                        'OR'=>array(
853
+	 *                                        'Registration.Attendee.ATT_fname'=>array('like','Mc%'),
854
+	 *                                        'Registration.Attendee.ATT_fname*other'=>array('like','Mac%')
855
+	 *                                        )
856
+	 *                                        ),
857
+	 *                                        'limit'=>10,
858
+	 *                                        'group_by'=>'TXN_ID'
859
+	 *                                        ));
860
+	 *                                        get all the answers to the question titled "shirt size" for event with id
861
+	 *                                        12, ordered by their answer EEM_Answer::instance()->get_all(array( array(
862
+	 *                                        'Question.QST_display_text'=>'shirt size',
863
+	 *                                        'Registration.Event.EVT_ID'=>12
864
+	 *                                        ),
865
+	 *                                        'order_by'=>array('ANS_value'=>'ASC')
866
+	 *                                        ));
867
+	 * @throws EE_Error
868
+	 */
869
+	public function get_all($query_params = array())
870
+	{
871
+		if (
872
+			isset($query_params['limit'])
873
+			&& ! isset($query_params['group_by'])
874
+		) {
875
+			$query_params['group_by'] = array_keys($this->get_combined_primary_key_fields());
876
+		}
877
+		return $this->_create_objects($this->_get_all_wpdb_results($query_params, ARRAY_A, null));
878
+	}
879
+
880
+
881
+
882
+	/**
883
+	 * Modifies the query parameters so we only get back model objects
884
+	 * that "belong" to the current user
885
+	 *
886
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
887
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
888
+	 */
889
+	public function alter_query_params_to_only_include_mine($query_params = array())
890
+	{
891
+		$wp_user_field_name = $this->wp_user_field_name();
892
+		if ($wp_user_field_name) {
893
+			$query_params[0][ $wp_user_field_name ] = get_current_user_id();
894
+		}
895
+		return $query_params;
896
+	}
897
+
898
+
899
+
900
+	/**
901
+	 * Returns the name of the field's name that points to the WP_User table
902
+	 *  on this model (or follows the _model_chain_to_wp_user and uses that model's
903
+	 * foreign key to the WP_User table)
904
+	 *
905
+	 * @return string|boolean string on success, boolean false when there is no
906
+	 * foreign key to the WP_User table
907
+	 */
908
+	public function wp_user_field_name()
909
+	{
910
+		try {
911
+			if (! empty($this->_model_chain_to_wp_user)) {
912
+				$models_to_follow_to_wp_users = explode('.', $this->_model_chain_to_wp_user);
913
+				$last_model_name = end($models_to_follow_to_wp_users);
914
+				$model_with_fk_to_wp_users = EE_Registry::instance()->load_model($last_model_name);
915
+				$model_chain_to_wp_user = $this->_model_chain_to_wp_user . '.';
916
+			} else {
917
+				$model_with_fk_to_wp_users = $this;
918
+				$model_chain_to_wp_user = '';
919
+			}
920
+			$wp_user_field = $model_with_fk_to_wp_users->get_foreign_key_to('WP_User');
921
+			return $model_chain_to_wp_user . $wp_user_field->get_name();
922
+		} catch (EE_Error $e) {
923
+			return false;
924
+		}
925
+	}
926
+
927
+
928
+
929
+	/**
930
+	 * Returns the _model_chain_to_wp_user string, which indicates which related model
931
+	 * (or transiently-related model) has a foreign key to the wp_users table;
932
+	 * useful for finding if model objects of this type are 'owned' by the current user.
933
+	 * This is an empty string when the foreign key is on this model and when it isn't,
934
+	 * but is only non-empty when this model's ownership is indicated by a RELATED model
935
+	 * (or transiently-related model)
936
+	 *
937
+	 * @return string
938
+	 */
939
+	public function model_chain_to_wp_user()
940
+	{
941
+		return $this->_model_chain_to_wp_user;
942
+	}
943
+
944
+
945
+
946
+	/**
947
+	 * Whether this model is 'owned' by a specific wordpress user (even indirectly,
948
+	 * like how registrations don't have a foreign key to wp_users, but the
949
+	 * events they are for are), or is unrelated to wp users.
950
+	 * generally available
951
+	 *
952
+	 * @return boolean
953
+	 */
954
+	public function is_owned()
955
+	{
956
+		if ($this->model_chain_to_wp_user()) {
957
+			return true;
958
+		}
959
+		try {
960
+			$this->get_foreign_key_to('WP_User');
961
+			return true;
962
+		} catch (EE_Error $e) {
963
+			return false;
964
+		}
965
+	}
966
+
967
+
968
+	/**
969
+	 * Used internally to get WPDB results, because other functions, besides get_all, may want to do some queries, but
970
+	 * may want to preserve the WPDB results (eg, update, which first queries to make sure we have all the tables on
971
+	 * the model)
972
+	 *
973
+	 * @param array  $query_params      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
974
+	 * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
975
+	 * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
976
+	 *                                  fields on the model, and the models we joined to in the query. However, you can
977
+	 *                                  override this and set the select to "*", or a specific column name, like
978
+	 *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
979
+	 *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
980
+	 *                                  the aliases used to refer to this selection, and values are to be
981
+	 *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
982
+	 *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
983
+	 * @return array | stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
984
+	 * @throws EE_Error
985
+	 * @throws InvalidArgumentException
986
+	 */
987
+	protected function _get_all_wpdb_results($query_params = array(), $output = ARRAY_A, $columns_to_select = null)
988
+	{
989
+		$this->_custom_selections = $this->getCustomSelection($query_params, $columns_to_select);
990
+		;
991
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
992
+		$select_expressions = $columns_to_select === null
993
+			? $this->_construct_default_select_sql($model_query_info)
994
+			: '';
995
+		if ($this->_custom_selections instanceof CustomSelects) {
996
+			$custom_expressions = $this->_custom_selections->columnsToSelectExpression();
997
+			$select_expressions .= $select_expressions
998
+				? ', ' . $custom_expressions
999
+				: $custom_expressions;
1000
+		}
1001
+
1002
+		$SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1003
+		return $this->_do_wpdb_query('get_results', array($SQL, $output));
1004
+	}
1005
+
1006
+
1007
+	/**
1008
+	 * Get a CustomSelects object if the $query_params or $columns_to_select allows for it.
1009
+	 * Note: $query_params['extra_selects'] will always override any $columns_to_select values. It is the preferred
1010
+	 * method of including extra select information.
1011
+	 *
1012
+	 * @param array             $query_params
1013
+	 * @param null|array|string $columns_to_select
1014
+	 * @return null|CustomSelects
1015
+	 * @throws InvalidArgumentException
1016
+	 */
1017
+	protected function getCustomSelection(array $query_params, $columns_to_select = null)
1018
+	{
1019
+		if (! isset($query_params['extra_selects']) && $columns_to_select === null) {
1020
+			return null;
1021
+		}
1022
+		$selects = isset($query_params['extra_selects']) ? $query_params['extra_selects'] : $columns_to_select;
1023
+		$selects = is_string($selects) ? explode(',', $selects) : $selects;
1024
+		return new CustomSelects($selects);
1025
+	}
1026
+
1027
+
1028
+
1029
+	/**
1030
+	 * Gets an array of rows from the database just like $wpdb->get_results would,
1031
+	 * but you can use the model query params to more easily
1032
+	 * take care of joins, field preparation etc.
1033
+	 *
1034
+	 * @param array  $query_params      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1035
+	 * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
1036
+	 * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
1037
+	 *                                  fields on the model, and the models we joined to in the query. However, you can
1038
+	 *                                  override this and set the select to "*", or a specific column name, like
1039
+	 *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
1040
+	 *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
1041
+	 *                                  the aliases used to refer to this selection, and values are to be
1042
+	 *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
1043
+	 *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
1044
+	 * @return array|stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
1045
+	 * @throws EE_Error
1046
+	 */
1047
+	public function get_all_wpdb_results($query_params = array(), $output = ARRAY_A, $columns_to_select = null)
1048
+	{
1049
+		return $this->_get_all_wpdb_results($query_params, $output, $columns_to_select);
1050
+	}
1051
+
1052
+
1053
+
1054
+	/**
1055
+	 * For creating a custom select statement
1056
+	 *
1057
+	 * @param mixed $columns_to_select either a string to be inserted directly as the select statement,
1058
+	 *                                 or an array where keys are aliases, and values are arrays where 0=>the selection
1059
+	 *                                 SQL, and 1=>is the datatype
1060
+	 * @throws EE_Error
1061
+	 * @return string
1062
+	 */
1063
+	private function _construct_select_from_input($columns_to_select)
1064
+	{
1065
+		if (is_array($columns_to_select)) {
1066
+			$select_sql_array = array();
1067
+			foreach ($columns_to_select as $alias => $selection_and_datatype) {
1068
+				if (! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1069
+					throw new EE_Error(
1070
+						sprintf(
1071
+							__(
1072
+								"Custom selection %s (alias %s) needs to be an array like array('COUNT(REG_ID)','%%d')",
1073
+								'event_espresso'
1074
+							),
1075
+							$selection_and_datatype,
1076
+							$alias
1077
+						)
1078
+					);
1079
+				}
1080
+				if (! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1081
+					throw new EE_Error(
1082
+						sprintf(
1083
+							esc_html__(
1084
+								"Datatype %s (for selection '%s' and alias '%s') is not a valid wpdb datatype (eg %%s)",
1085
+								'event_espresso'
1086
+							),
1087
+							$selection_and_datatype[1],
1088
+							$selection_and_datatype[0],
1089
+							$alias,
1090
+							implode(', ', $this->_valid_wpdb_data_types)
1091
+						)
1092
+					);
1093
+				}
1094
+				$select_sql_array[] = "{$selection_and_datatype[0]} AS $alias";
1095
+			}
1096
+			$columns_to_select_string = implode(', ', $select_sql_array);
1097
+		} else {
1098
+			$columns_to_select_string = $columns_to_select;
1099
+		}
1100
+		return $columns_to_select_string;
1101
+	}
1102
+
1103
+
1104
+
1105
+	/**
1106
+	 * Convenient wrapper for getting the primary key field's name. Eg, on Registration, this would be 'REG_ID'
1107
+	 *
1108
+	 * @return string
1109
+	 * @throws EE_Error
1110
+	 */
1111
+	public function primary_key_name()
1112
+	{
1113
+		return $this->get_primary_key_field()->get_name();
1114
+	}
1115
+
1116
+
1117
+	/**
1118
+	 * Gets a single item for this model from the DB, given only its ID (or null if none is found).
1119
+	 * If there is no primary key on this model, $id is treated as primary key string
1120
+	 *
1121
+	 * @param mixed $id int or string, depending on the type of the model's primary key
1122
+	 * @return EE_Base_Class
1123
+	 * @throws EE_Error
1124
+	 */
1125
+	public function get_one_by_ID($id)
1126
+	{
1127
+		if ($this->get_from_entity_map($id)) {
1128
+			return $this->get_from_entity_map($id);
1129
+		}
1130
+		$model_object = $this->get_one(
1131
+			$this->alter_query_params_to_restrict_by_ID(
1132
+				$id,
1133
+				array('default_where_conditions' => EEM_Base::default_where_conditions_minimum_all)
1134
+			)
1135
+		);
1136
+		$className = $this->_get_class_name();
1137
+		if ($model_object instanceof $className) {
1138
+			// make sure valid objects get added to the entity map
1139
+			// so that the next call to this method doesn't trigger another trip to the db
1140
+			$this->add_to_entity_map($model_object);
1141
+		}
1142
+		return $model_object;
1143
+	}
1144
+
1145
+
1146
+
1147
+	/**
1148
+	 * Alters query parameters to only get items with this ID are returned.
1149
+	 * Takes into account that the ID might be a string produced by EEM_Base::get_index_primary_key_string(),
1150
+	 * or could just be a simple primary key ID
1151
+	 *
1152
+	 * @param int   $id
1153
+	 * @param array $query_params
1154
+	 * @return array of normal query params, @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1155
+	 * @throws EE_Error
1156
+	 */
1157
+	public function alter_query_params_to_restrict_by_ID($id, $query_params = array())
1158
+	{
1159
+		if (! isset($query_params[0])) {
1160
+			$query_params[0] = array();
1161
+		}
1162
+		$conditions_from_id = $this->parse_index_primary_key_string($id);
1163
+		if ($conditions_from_id === null) {
1164
+			$query_params[0][ $this->primary_key_name() ] = $id;
1165
+		} else {
1166
+			// no primary key, so the $id must be from the get_index_primary_key_string()
1167
+			$query_params[0] = array_replace_recursive($query_params[0], $this->parse_index_primary_key_string($id));
1168
+		}
1169
+		return $query_params;
1170
+	}
1171
+
1172
+
1173
+
1174
+	/**
1175
+	 * Gets a single item for this model from the DB, given the $query_params. Only returns a single class, not an
1176
+	 * array. If no item is found, null is returned.
1177
+	 *
1178
+	 * @param array $query_params like EEM_Base's $query_params variable.
1179
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1180
+	 * @throws EE_Error
1181
+	 */
1182
+	public function get_one($query_params = array())
1183
+	{
1184
+		if (! is_array($query_params)) {
1185
+			EE_Error::doing_it_wrong(
1186
+				'EEM_Base::get_one',
1187
+				sprintf(
1188
+					__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1189
+					gettype($query_params)
1190
+				),
1191
+				'4.6.0'
1192
+			);
1193
+			$query_params = array();
1194
+		}
1195
+		$query_params['limit'] = 1;
1196
+		$items = $this->get_all($query_params);
1197
+		if (empty($items)) {
1198
+			return null;
1199
+		}
1200
+		return array_shift($items);
1201
+	}
1202
+
1203
+
1204
+
1205
+	/**
1206
+	 * Returns the next x number of items in sequence from the given value as
1207
+	 * found in the database matching the given query conditions.
1208
+	 *
1209
+	 * @param mixed $current_field_value    Value used for the reference point.
1210
+	 * @param null  $field_to_order_by      What field is used for the
1211
+	 *                                      reference point.
1212
+	 * @param int   $limit                  How many to return.
1213
+	 * @param array $query_params           Extra conditions on the query.
1214
+	 * @param null  $columns_to_select      If left null, then an array of
1215
+	 *                                      EE_Base_Class objects is returned,
1216
+	 *                                      otherwise you can indicate just the
1217
+	 *                                      columns you want returned.
1218
+	 * @return EE_Base_Class[]|array
1219
+	 * @throws EE_Error
1220
+	 */
1221
+	public function next_x(
1222
+		$current_field_value,
1223
+		$field_to_order_by = null,
1224
+		$limit = 1,
1225
+		$query_params = array(),
1226
+		$columns_to_select = null
1227
+	) {
1228
+		return $this->_get_consecutive(
1229
+			$current_field_value,
1230
+			'>',
1231
+			$field_to_order_by,
1232
+			$limit,
1233
+			$query_params,
1234
+			$columns_to_select
1235
+		);
1236
+	}
1237
+
1238
+
1239
+
1240
+	/**
1241
+	 * Returns the previous x number of items in sequence from the given value
1242
+	 * as found in the database matching the given query conditions.
1243
+	 *
1244
+	 * @param mixed $current_field_value    Value used for the reference point.
1245
+	 * @param null  $field_to_order_by      What field is used for the
1246
+	 *                                      reference point.
1247
+	 * @param int   $limit                  How many to return.
1248
+	 * @param array $query_params           Extra conditions on the query.
1249
+	 * @param null  $columns_to_select      If left null, then an array of
1250
+	 *                                      EE_Base_Class objects is returned,
1251
+	 *                                      otherwise you can indicate just the
1252
+	 *                                      columns you want returned.
1253
+	 * @return EE_Base_Class[]|array
1254
+	 * @throws EE_Error
1255
+	 */
1256
+	public function previous_x(
1257
+		$current_field_value,
1258
+		$field_to_order_by = null,
1259
+		$limit = 1,
1260
+		$query_params = array(),
1261
+		$columns_to_select = null
1262
+	) {
1263
+		return $this->_get_consecutive(
1264
+			$current_field_value,
1265
+			'<',
1266
+			$field_to_order_by,
1267
+			$limit,
1268
+			$query_params,
1269
+			$columns_to_select
1270
+		);
1271
+	}
1272
+
1273
+
1274
+
1275
+	/**
1276
+	 * Returns the next item in sequence from the given value as found in the
1277
+	 * database matching the given query conditions.
1278
+	 *
1279
+	 * @param mixed $current_field_value    Value used for the reference point.
1280
+	 * @param null  $field_to_order_by      What field is used for the
1281
+	 *                                      reference point.
1282
+	 * @param array $query_params           Extra conditions on the query.
1283
+	 * @param null  $columns_to_select      If left null, then an EE_Base_Class
1284
+	 *                                      object is returned, otherwise you
1285
+	 *                                      can indicate just the columns you
1286
+	 *                                      want and a single array indexed by
1287
+	 *                                      the columns will be returned.
1288
+	 * @return EE_Base_Class|null|array()
1289
+	 * @throws EE_Error
1290
+	 */
1291
+	public function next(
1292
+		$current_field_value,
1293
+		$field_to_order_by = null,
1294
+		$query_params = array(),
1295
+		$columns_to_select = null
1296
+	) {
1297
+		$results = $this->_get_consecutive(
1298
+			$current_field_value,
1299
+			'>',
1300
+			$field_to_order_by,
1301
+			1,
1302
+			$query_params,
1303
+			$columns_to_select
1304
+		);
1305
+		return empty($results) ? null : reset($results);
1306
+	}
1307
+
1308
+
1309
+
1310
+	/**
1311
+	 * Returns the previous item in sequence from the given value as found in
1312
+	 * the database matching the given query conditions.
1313
+	 *
1314
+	 * @param mixed $current_field_value    Value used for the reference point.
1315
+	 * @param null  $field_to_order_by      What field is used for the
1316
+	 *                                      reference point.
1317
+	 * @param array $query_params           Extra conditions on the query.
1318
+	 * @param null  $columns_to_select      If left null, then an EE_Base_Class
1319
+	 *                                      object is returned, otherwise you
1320
+	 *                                      can indicate just the columns you
1321
+	 *                                      want and a single array indexed by
1322
+	 *                                      the columns will be returned.
1323
+	 * @return EE_Base_Class|null|array()
1324
+	 * @throws EE_Error
1325
+	 */
1326
+	public function previous(
1327
+		$current_field_value,
1328
+		$field_to_order_by = null,
1329
+		$query_params = array(),
1330
+		$columns_to_select = null
1331
+	) {
1332
+		$results = $this->_get_consecutive(
1333
+			$current_field_value,
1334
+			'<',
1335
+			$field_to_order_by,
1336
+			1,
1337
+			$query_params,
1338
+			$columns_to_select
1339
+		);
1340
+		return empty($results) ? null : reset($results);
1341
+	}
1342
+
1343
+
1344
+
1345
+	/**
1346
+	 * Returns the a consecutive number of items in sequence from the given
1347
+	 * value as found in the database matching the given query conditions.
1348
+	 *
1349
+	 * @param mixed  $current_field_value   Value used for the reference point.
1350
+	 * @param string $operand               What operand is used for the sequence.
1351
+	 * @param string $field_to_order_by     What field is used for the reference point.
1352
+	 * @param int    $limit                 How many to return.
1353
+	 * @param array  $query_params          Extra conditions on the query.
1354
+	 * @param null   $columns_to_select     If left null, then an array of EE_Base_Class objects is returned,
1355
+	 *                                      otherwise you can indicate just the columns you want returned.
1356
+	 * @return EE_Base_Class[]|array
1357
+	 * @throws EE_Error
1358
+	 */
1359
+	protected function _get_consecutive(
1360
+		$current_field_value,
1361
+		$operand = '>',
1362
+		$field_to_order_by = null,
1363
+		$limit = 1,
1364
+		$query_params = array(),
1365
+		$columns_to_select = null
1366
+	) {
1367
+		// if $field_to_order_by is empty then let's assume we're ordering by the primary key.
1368
+		if (empty($field_to_order_by)) {
1369
+			if ($this->has_primary_key_field()) {
1370
+				$field_to_order_by = $this->get_primary_key_field()->get_name();
1371
+			} else {
1372
+				if (WP_DEBUG) {
1373
+					throw new EE_Error(__(
1374
+						'EEM_Base::_get_consecutive() has been called with no $field_to_order_by argument and there is no primary key on the field.  Please provide the field you would like to use as the base for retrieving the next item(s).',
1375
+						'event_espresso'
1376
+					));
1377
+				}
1378
+				EE_Error::add_error(__('There was an error with the query.', 'event_espresso'));
1379
+				return array();
1380
+			}
1381
+		}
1382
+		if (! is_array($query_params)) {
1383
+			EE_Error::doing_it_wrong(
1384
+				'EEM_Base::_get_consecutive',
1385
+				sprintf(
1386
+					__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1387
+					gettype($query_params)
1388
+				),
1389
+				'4.6.0'
1390
+			);
1391
+			$query_params = array();
1392
+		}
1393
+		// let's add the where query param for consecutive look up.
1394
+		$query_params[0][ $field_to_order_by ] = array($operand, $current_field_value);
1395
+		$query_params['limit'] = $limit;
1396
+		// set direction
1397
+		$incoming_orderby = isset($query_params['order_by']) ? (array) $query_params['order_by'] : array();
1398
+		$query_params['order_by'] = $operand === '>'
1399
+			? array($field_to_order_by => 'ASC') + $incoming_orderby
1400
+			: array($field_to_order_by => 'DESC') + $incoming_orderby;
1401
+		// if $columns_to_select is empty then that means we're returning EE_Base_Class objects
1402
+		if (empty($columns_to_select)) {
1403
+			return $this->get_all($query_params);
1404
+		}
1405
+		// getting just the fields
1406
+		return $this->_get_all_wpdb_results($query_params, ARRAY_A, $columns_to_select);
1407
+	}
1408
+
1409
+
1410
+
1411
+	/**
1412
+	 * This sets the _timezone property after model object has been instantiated.
1413
+	 *
1414
+	 * @param null | string $timezone valid PHP DateTimeZone timezone string
1415
+	 */
1416
+	public function set_timezone($timezone)
1417
+	{
1418
+		if ($timezone !== null) {
1419
+			$this->_timezone = $timezone;
1420
+		}
1421
+		// note we need to loop through relations and set the timezone on those objects as well.
1422
+		foreach ($this->_model_relations as $relation) {
1423
+			$relation->set_timezone($timezone);
1424
+		}
1425
+		// and finally we do the same for any datetime fields
1426
+		foreach ($this->_fields as $field) {
1427
+			if ($field instanceof EE_Datetime_Field) {
1428
+				$field->set_timezone($timezone);
1429
+			}
1430
+		}
1431
+	}
1432
+
1433
+
1434
+
1435
+	/**
1436
+	 * This just returns whatever is set for the current timezone.
1437
+	 *
1438
+	 * @access public
1439
+	 * @return string
1440
+	 */
1441
+	public function get_timezone()
1442
+	{
1443
+		// first validate if timezone is set.  If not, then let's set it be whatever is set on the model fields.
1444
+		if (empty($this->_timezone)) {
1445
+			foreach ($this->_fields as $field) {
1446
+				if ($field instanceof EE_Datetime_Field) {
1447
+					$this->set_timezone($field->get_timezone());
1448
+					break;
1449
+				}
1450
+			}
1451
+		}
1452
+		// if timezone STILL empty then return the default timezone for the site.
1453
+		if (empty($this->_timezone)) {
1454
+			$this->set_timezone(EEH_DTT_Helper::get_timezone());
1455
+		}
1456
+		return $this->_timezone;
1457
+	}
1458
+
1459
+
1460
+
1461
+	/**
1462
+	 * This returns the date formats set for the given field name and also ensures that
1463
+	 * $this->_timezone property is set correctly.
1464
+	 *
1465
+	 * @since 4.6.x
1466
+	 * @param string $field_name The name of the field the formats are being retrieved for.
1467
+	 * @param bool   $pretty     Whether to return the pretty formats (true) or not (false).
1468
+	 * @throws EE_Error   If the given field_name is not of the EE_Datetime_Field type.
1469
+	 * @return array formats in an array with the date format first, and the time format last.
1470
+	 */
1471
+	public function get_formats_for($field_name, $pretty = false)
1472
+	{
1473
+		$field_settings = $this->field_settings_for($field_name);
1474
+		// if not a valid EE_Datetime_Field then throw error
1475
+		if (! $field_settings instanceof EE_Datetime_Field) {
1476
+			throw new EE_Error(sprintf(__(
1477
+				'The field sent into EEM_Base::get_formats_for (%s) is not registered as a EE_Datetime_Field. Please check the spelling and make sure you are submitting the right field name to retrieve date_formats for.',
1478
+				'event_espresso'
1479
+			), $field_name));
1480
+		}
1481
+		// while we are here, let's make sure the timezone internally in EEM_Base matches what is stored on
1482
+		// the field.
1483
+		$this->_timezone = $field_settings->get_timezone();
1484
+		return array($field_settings->get_date_format($pretty), $field_settings->get_time_format($pretty));
1485
+	}
1486
+
1487
+
1488
+
1489
+	/**
1490
+	 * This returns the current time in a format setup for a query on this model.
1491
+	 * Usage of this method makes it easier to setup queries against EE_Datetime_Field columns because
1492
+	 * it will return:
1493
+	 *  - a formatted string in the timezone and format currently set on the EE_Datetime_Field for the given field for
1494
+	 *  NOW
1495
+	 *  - or a unix timestamp (equivalent to time())
1496
+	 * Note: When requesting a formatted string, if the date or time format doesn't include seconds, for example,
1497
+	 * the time returned, because it uses that format, will also NOT include seconds. For this reason, if you want
1498
+	 * the time returned to be the current time down to the exact second, set $timestamp to true.
1499
+	 * @since 4.6.x
1500
+	 * @param string $field_name       The field the current time is needed for.
1501
+	 * @param bool   $timestamp        True means to return a unix timestamp. Otherwise a
1502
+	 *                                 formatted string matching the set format for the field in the set timezone will
1503
+	 *                                 be returned.
1504
+	 * @param string $what             Whether to return the string in just the time format, the date format, or both.
1505
+	 * @throws EE_Error    If the given field_name is not of the EE_Datetime_Field type.
1506
+	 * @return int|string  If the given field_name is not of the EE_Datetime_Field type, then an EE_Error
1507
+	 *                                 exception is triggered.
1508
+	 */
1509
+	public function current_time_for_query($field_name, $timestamp = false, $what = 'both')
1510
+	{
1511
+		$formats = $this->get_formats_for($field_name);
1512
+		$DateTime = new DateTime("now", new DateTimeZone($this->_timezone));
1513
+		if ($timestamp) {
1514
+			return $DateTime->format('U');
1515
+		}
1516
+		// not returning timestamp, so return formatted string in timezone.
1517
+		switch ($what) {
1518
+			case 'time':
1519
+				return $DateTime->format($formats[1]);
1520
+				break;
1521
+			case 'date':
1522
+				return $DateTime->format($formats[0]);
1523
+				break;
1524
+			default:
1525
+				return $DateTime->format(implode(' ', $formats));
1526
+				break;
1527
+		}
1528
+	}
1529
+
1530
+
1531
+
1532
+	/**
1533
+	 * This receives a time string for a given field and ensures that it is setup to match what the internal settings
1534
+	 * for the model are.  Returns a DateTime object.
1535
+	 * Note: a gotcha for when you send in unix timestamp.  Remember a unix timestamp is already timezone agnostic,
1536
+	 * (functionally the equivalent of UTC+0).  So when you send it in, whatever timezone string you include is
1537
+	 * ignored.
1538
+	 *
1539
+	 * @param string $field_name      The field being setup.
1540
+	 * @param string $timestring      The date time string being used.
1541
+	 * @param string $incoming_format The format for the time string.
1542
+	 * @param string $timezone        By default, it is assumed the incoming time string is in timezone for
1543
+	 *                                the blog.  If this is not the case, then it can be specified here.  If incoming
1544
+	 *                                format is
1545
+	 *                                'U', this is ignored.
1546
+	 * @return DateTime
1547
+	 * @throws EE_Error
1548
+	 */
1549
+	public function convert_datetime_for_query($field_name, $timestring, $incoming_format, $timezone = '')
1550
+	{
1551
+		// just using this to ensure the timezone is set correctly internally
1552
+		$this->get_formats_for($field_name);
1553
+		// load EEH_DTT_Helper
1554
+		$set_timezone = empty($timezone) ? EEH_DTT_Helper::get_timezone() : $timezone;
1555
+		$incomingDateTime = date_create_from_format($incoming_format, $timestring, new DateTimeZone($set_timezone));
1556
+		EEH_DTT_Helper::setTimezone($incomingDateTime, new DateTimeZone($this->_timezone));
1557
+		return \EventEspresso\core\domain\entities\DbSafeDateTime::createFromDateTime($incomingDateTime);
1558
+	}
1559
+
1560
+
1561
+
1562
+	/**
1563
+	 * Gets all the tables comprising this model. Array keys are the table aliases, and values are EE_Table objects
1564
+	 *
1565
+	 * @return EE_Table_Base[]
1566
+	 */
1567
+	public function get_tables()
1568
+	{
1569
+		return $this->_tables;
1570
+	}
1571
+
1572
+
1573
+
1574
+	/**
1575
+	 * Updates all the database entries (in each table for this model) according to $fields_n_values and optionally
1576
+	 * also updates all the model objects, where the criteria expressed in $query_params are met..
1577
+	 * Also note: if this model has multiple tables, this update verifies all the secondary tables have an entry for
1578
+	 * each row (in the primary table) we're trying to update; if not, it inserts an entry in the secondary table. Eg:
1579
+	 * if our model has 2 tables: wp_posts (primary), and wp_esp_event (secondary). Let's say we are trying to update a
1580
+	 * model object with EVT_ID = 1
1581
+	 * (which means where wp_posts has ID = 1, because wp_posts.ID is the primary key's column), which exists, but
1582
+	 * there is no entry in wp_esp_event for this entry in wp_posts. So, this update script will insert a row into
1583
+	 * wp_esp_event, using any available parameters from $fields_n_values (eg, if "EVT_limit" => 40 is in
1584
+	 * $fields_n_values, the new entry in wp_esp_event will set EVT_limit = 40, and use default for other columns which
1585
+	 * are not specified)
1586
+	 *
1587
+	 * @param array   $fields_n_values         keys are model fields (exactly like keys in EEM_Base::_fields, NOT db
1588
+	 *                                         columns!), values are strings, ints, floats, and maybe arrays if they
1589
+	 *                                         are to be serialized. Basically, the values are what you'd expect to be
1590
+	 *                                         values on the model, NOT necessarily what's in the DB. For example, if
1591
+	 *                                         we wanted to update only the TXN_details on any Transactions where its
1592
+	 *                                         ID=34, we'd use this method as follows:
1593
+	 *                                         EEM_Transaction::instance()->update(
1594
+	 *                                         array('TXN_details'=>array('detail1'=>'monkey','detail2'=>'banana'),
1595
+	 *                                         array(array('TXN_ID'=>34)));
1596
+	 * @param array   $query_params            @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1597
+	 *                                         Eg, consider updating Question's QST_admin_label field is of type
1598
+	 *                                         Simple_HTML. If you use this function to update that field to $new_value
1599
+	 *                                         = (note replace 8's with appropriate opening and closing tags in the
1600
+	 *                                         following example)"8script8alert('I hack all');8/script88b8boom
1601
+	 *                                         baby8/b8", then if you set $values_already_prepared_by_model_object to
1602
+	 *                                         TRUE, it is assumed that you've already called
1603
+	 *                                         EE_Simple_HTML_Field->prepare_for_set($new_value), which removes the
1604
+	 *                                         malicious javascript. However, if
1605
+	 *                                         $values_already_prepared_by_model_object is left as FALSE, then
1606
+	 *                                         EE_Simple_HTML_Field->prepare_for_set($new_value) will be called on it,
1607
+	 *                                         and every other field, before insertion. We provide this parameter
1608
+	 *                                         because model objects perform their prepare_for_set function on all
1609
+	 *                                         their values, and so don't need to be called again (and in many cases,
1610
+	 *                                         shouldn't be called again. Eg: if we escape HTML characters in the
1611
+	 *                                         prepare_for_set method...)
1612
+	 * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
1613
+	 *                                         in this model's entity map according to $fields_n_values that match
1614
+	 *                                         $query_params. This obviously has some overhead, so you can disable it
1615
+	 *                                         by setting this to FALSE, but be aware that model objects being used
1616
+	 *                                         could get out-of-sync with the database
1617
+	 * @return int how many rows got updated or FALSE if something went wrong with the query (wp returns FALSE or num
1618
+	 *                                         rows affected which *could* include 0 which DOES NOT mean the query was
1619
+	 *                                         bad)
1620
+	 * @throws EE_Error
1621
+	 */
1622
+	public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
1623
+	{
1624
+		if (! is_array($query_params)) {
1625
+			EE_Error::doing_it_wrong(
1626
+				'EEM_Base::update',
1627
+				sprintf(
1628
+					__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1629
+					gettype($query_params)
1630
+				),
1631
+				'4.6.0'
1632
+			);
1633
+			$query_params = array();
1634
+		}
1635
+		/**
1636
+		 * Action called before a model update call has been made.
1637
+		 *
1638
+		 * @param EEM_Base $model
1639
+		 * @param array    $fields_n_values the updated fields and their new values
1640
+		 * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1641
+		 */
1642
+		do_action('AHEE__EEM_Base__update__begin', $this, $fields_n_values, $query_params);
1643
+		/**
1644
+		 * Filters the fields about to be updated given the query parameters. You can provide the
1645
+		 * $query_params to $this->get_all() to find exactly which records will be updated
1646
+		 *
1647
+		 * @param array    $fields_n_values fields and their new values
1648
+		 * @param EEM_Base $model           the model being queried
1649
+		 * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1650
+		 */
1651
+		$fields_n_values = (array) apply_filters(
1652
+			'FHEE__EEM_Base__update__fields_n_values',
1653
+			$fields_n_values,
1654
+			$this,
1655
+			$query_params
1656
+		);
1657
+		// need to verify that, for any entry we want to update, there are entries in each secondary table.
1658
+		// to do that, for each table, verify that it's PK isn't null.
1659
+		$tables = $this->get_tables();
1660
+		// and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1661
+		// NOTE: we should make this code more efficient by NOT querying twice
1662
+		// before the real update, but that needs to first go through ALPHA testing
1663
+		// as it's dangerous. says Mike August 8 2014
1664
+		// we want to make sure the default_where strategy is ignored
1665
+		$this->_ignore_where_strategy = true;
1666
+		$wpdb_select_results = $this->_get_all_wpdb_results($query_params);
1667
+		foreach ($wpdb_select_results as $wpdb_result) {
1668
+			// type cast stdClass as array
1669
+			$wpdb_result = (array) $wpdb_result;
1670
+			// get the model object's PK, as we'll want this if we need to insert a row into secondary tables
1671
+			if ($this->has_primary_key_field()) {
1672
+				$main_table_pk_value = $wpdb_result[ $this->get_primary_key_field()->get_qualified_column() ];
1673
+			} else {
1674
+				// if there's no primary key, we basically can't support having a 2nd table on the model (we could but it would be lots of work)
1675
+				$main_table_pk_value = null;
1676
+			}
1677
+			// if there are more than 1 tables, we'll want to verify that each table for this model has an entry in the other tables
1678
+			// and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1679
+			if (count($tables) > 1) {
1680
+				// foreach matching row in the DB, ensure that each table's PK isn't null. If so, there must not be an entry
1681
+				// in that table, and so we'll want to insert one
1682
+				foreach ($tables as $table_obj) {
1683
+					$this_table_pk_column = $table_obj->get_fully_qualified_pk_column();
1684
+					// if there is no private key for this table on the results, it means there's no entry
1685
+					// in this table, right? so insert a row in the current table, using any fields available
1686
+					if (
1687
+						! (array_key_exists($this_table_pk_column, $wpdb_result)
1688
+						   && $wpdb_result[ $this_table_pk_column ])
1689
+					) {
1690
+						$success = $this->_insert_into_specific_table(
1691
+							$table_obj,
1692
+							$fields_n_values,
1693
+							$main_table_pk_value
1694
+						);
1695
+						// if we died here, report the error
1696
+						if (! $success) {
1697
+							return false;
1698
+						}
1699
+					}
1700
+				}
1701
+			}
1702
+			//              //and now check that if we have cached any models by that ID on the model, that
1703
+			//              //they also get updated properly
1704
+			//              $model_object = $this->get_from_entity_map( $main_table_pk_value );
1705
+			//              if( $model_object ){
1706
+			//                  foreach( $fields_n_values as $field => $value ){
1707
+			//                      $model_object->set($field, $value);
1708
+			// let's make sure default_where strategy is followed now
1709
+			$this->_ignore_where_strategy = false;
1710
+		}
1711
+		// if we want to keep model objects in sync, AND
1712
+		// if this wasn't called from a model object (to update itself)
1713
+		// then we want to make sure we keep all the existing
1714
+		// model objects in sync with the db
1715
+		if ($keep_model_objs_in_sync && ! $this->_values_already_prepared_by_model_object) {
1716
+			if ($this->has_primary_key_field()) {
1717
+				$model_objs_affected_ids = $this->get_col($query_params);
1718
+			} else {
1719
+				// we need to select a bunch of columns and then combine them into the the "index primary key string"s
1720
+				$models_affected_key_columns = $this->_get_all_wpdb_results($query_params, ARRAY_A);
1721
+				$model_objs_affected_ids = array();
1722
+				foreach ($models_affected_key_columns as $row) {
1723
+					$combined_index_key = $this->get_index_primary_key_string($row);
1724
+					$model_objs_affected_ids[ $combined_index_key ] = $combined_index_key;
1725
+				}
1726
+			}
1727
+			if (! $model_objs_affected_ids) {
1728
+				// wait wait wait- if nothing was affected let's stop here
1729
+				return 0;
1730
+			}
1731
+			foreach ($model_objs_affected_ids as $id) {
1732
+				$model_obj_in_entity_map = $this->get_from_entity_map($id);
1733
+				if ($model_obj_in_entity_map) {
1734
+					foreach ($fields_n_values as $field => $new_value) {
1735
+						$model_obj_in_entity_map->set($field, $new_value);
1736
+					}
1737
+				}
1738
+			}
1739
+			// if there is a primary key on this model, we can now do a slight optimization
1740
+			if ($this->has_primary_key_field()) {
1741
+				// we already know what we want to update. So let's make the query simpler so it's a little more efficient
1742
+				$query_params = array(
1743
+					array($this->primary_key_name() => array('IN', $model_objs_affected_ids)),
1744
+					'limit'                    => count($model_objs_affected_ids),
1745
+					'default_where_conditions' => EEM_Base::default_where_conditions_none,
1746
+				);
1747
+			}
1748
+		}
1749
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
1750
+		$SQL = "UPDATE "
1751
+			   . $model_query_info->get_full_join_sql()
1752
+			   . " SET "
1753
+			   . $this->_construct_update_sql($fields_n_values)
1754
+			   . $model_query_info->get_where_sql();// note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1755
+		$rows_affected = $this->_do_wpdb_query('query', array($SQL));
1756
+		/**
1757
+		 * Action called after a model update call has been made.
1758
+		 *
1759
+		 * @param EEM_Base $model
1760
+		 * @param array    $fields_n_values the updated fields and their new values
1761
+		 * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1762
+		 * @param int      $rows_affected
1763
+		 */
1764
+		do_action('AHEE__EEM_Base__update__end', $this, $fields_n_values, $query_params, $rows_affected);
1765
+		return $rows_affected;// how many supposedly got updated
1766
+	}
1767
+
1768
+
1769
+
1770
+	/**
1771
+	 * Analogous to $wpdb->get_col, returns a 1-dimensional array where teh values
1772
+	 * are teh values of the field specified (or by default the primary key field)
1773
+	 * that matched the query params. Note that you should pass the name of the
1774
+	 * model FIELD, not the database table's column name.
1775
+	 *
1776
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1777
+	 * @param string $field_to_select
1778
+	 * @return array just like $wpdb->get_col()
1779
+	 * @throws EE_Error
1780
+	 */
1781
+	public function get_col($query_params = array(), $field_to_select = null)
1782
+	{
1783
+		if ($field_to_select) {
1784
+			$field = $this->field_settings_for($field_to_select);
1785
+		} elseif ($this->has_primary_key_field()) {
1786
+			$field = $this->get_primary_key_field();
1787
+		} else {
1788
+			// no primary key, just grab the first column
1789
+			$field = reset($this->field_settings());
1790
+		}
1791
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
1792
+		$select_expressions = $field->get_qualified_column();
1793
+		$SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1794
+		return $this->_do_wpdb_query('get_col', array($SQL));
1795
+	}
1796
+
1797
+
1798
+
1799
+	/**
1800
+	 * Returns a single column value for a single row from the database
1801
+	 *
1802
+	 * @param array  $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1803
+	 * @param string $field_to_select @see EEM_Base::get_col()
1804
+	 * @return string
1805
+	 * @throws EE_Error
1806
+	 */
1807
+	public function get_var($query_params = array(), $field_to_select = null)
1808
+	{
1809
+		$query_params['limit'] = 1;
1810
+		$col = $this->get_col($query_params, $field_to_select);
1811
+		if (! empty($col)) {
1812
+			return reset($col);
1813
+		}
1814
+		return null;
1815
+	}
1816
+
1817
+
1818
+
1819
+	/**
1820
+	 * Makes the SQL for after "UPDATE table_X inner join table_Y..." and before "...WHERE". Eg "Question.name='party
1821
+	 * time?', Question.desc='what do you think?',..." Values are filtered through wpdb->prepare to avoid against SQL
1822
+	 * injection, but currently no further filtering is done
1823
+	 *
1824
+	 * @global      $wpdb
1825
+	 * @param array $fields_n_values array keys are field names on this model, and values are what those fields should
1826
+	 *                               be updated to in the DB
1827
+	 * @return string of SQL
1828
+	 * @throws EE_Error
1829
+	 */
1830
+	public function _construct_update_sql($fields_n_values)
1831
+	{
1832
+		/** @type WPDB $wpdb */
1833
+		global $wpdb;
1834
+		$cols_n_values = array();
1835
+		foreach ($fields_n_values as $field_name => $value) {
1836
+			$field_obj = $this->field_settings_for($field_name);
1837
+			// if the value is NULL, we want to assign the value to that.
1838
+			// wpdb->prepare doesn't really handle that properly
1839
+			$prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
1840
+			$value_sql = $prepared_value === null ? 'NULL'
1841
+				: $wpdb->prepare($field_obj->get_wpdb_data_type(), $prepared_value);
1842
+			$cols_n_values[] = $field_obj->get_qualified_column() . "=" . $value_sql;
1843
+		}
1844
+		return implode(",", $cols_n_values);
1845
+	}
1846
+
1847
+
1848
+
1849
+	/**
1850
+	 * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1851
+	 * Performs a HARD delete, meaning the database row should always be removed,
1852
+	 * not just have a flag field on it switched
1853
+	 * Wrapper for EEM_Base::delete_permanently()
1854
+	 *
1855
+	 * @param mixed $id
1856
+	 * @param boolean $allow_blocking
1857
+	 * @return int the number of rows deleted
1858
+	 * @throws EE_Error
1859
+	 */
1860
+	public function delete_permanently_by_ID($id, $allow_blocking = true)
1861
+	{
1862
+		return $this->delete_permanently(
1863
+			array(
1864
+				array($this->get_primary_key_field()->get_name() => $id),
1865
+				'limit' => 1,
1866
+			),
1867
+			$allow_blocking
1868
+		);
1869
+	}
1870
+
1871
+
1872
+
1873
+	/**
1874
+	 * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1875
+	 * Wrapper for EEM_Base::delete()
1876
+	 *
1877
+	 * @param mixed $id
1878
+	 * @param boolean $allow_blocking
1879
+	 * @return int the number of rows deleted
1880
+	 * @throws EE_Error
1881
+	 */
1882
+	public function delete_by_ID($id, $allow_blocking = true)
1883
+	{
1884
+		return $this->delete(
1885
+			array(
1886
+				array($this->get_primary_key_field()->get_name() => $id),
1887
+				'limit' => 1,
1888
+			),
1889
+			$allow_blocking
1890
+		);
1891
+	}
1892
+
1893
+
1894
+
1895
+	/**
1896
+	 * Identical to delete_permanently, but does a "soft" delete if possible,
1897
+	 * meaning if the model has a field that indicates its been "trashed" or
1898
+	 * "soft deleted", we will just set that instead of actually deleting the rows.
1899
+	 *
1900
+	 * @see EEM_Base::delete_permanently
1901
+	 * @param array   $query_params
1902
+	 * @param boolean $allow_blocking
1903
+	 * @return int how many rows got deleted
1904
+	 * @throws EE_Error
1905
+	 */
1906
+	public function delete($query_params, $allow_blocking = true)
1907
+	{
1908
+		return $this->delete_permanently($query_params, $allow_blocking);
1909
+	}
1910
+
1911
+
1912
+
1913
+	/**
1914
+	 * Deletes the model objects that meet the query params. Note: this method is overridden
1915
+	 * in EEM_Soft_Delete_Base so that soft-deleted model objects are instead only flagged
1916
+	 * as archived, not actually deleted
1917
+	 *
1918
+	 * @param array   $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1919
+	 * @param boolean $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
1920
+	 *                                that blocks it (ie, there' sno other data that depends on this data); if false,
1921
+	 *                                deletes regardless of other objects which may depend on it. Its generally
1922
+	 *                                advisable to always leave this as TRUE, otherwise you could easily corrupt your
1923
+	 *                                DB
1924
+	 * @return int how many rows got deleted
1925
+	 * @throws EE_Error
1926
+	 */
1927
+	public function delete_permanently($query_params, $allow_blocking = true)
1928
+	{
1929
+		/**
1930
+		 * Action called just before performing a real deletion query. You can use the
1931
+		 * model and its $query_params to find exactly which items will be deleted
1932
+		 *
1933
+		 * @param EEM_Base $model
1934
+		 * @param array    $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1935
+		 * @param boolean  $allow_blocking whether or not to allow related model objects
1936
+		 *                                 to block (prevent) this deletion
1937
+		 */
1938
+		do_action('AHEE__EEM_Base__delete__begin', $this, $query_params, $allow_blocking);
1939
+		// some MySQL databases may be running safe mode, which may restrict
1940
+		// deletion if there is no KEY column used in the WHERE statement of a deletion.
1941
+		// to get around this, we first do a SELECT, get all the IDs, and then run another query
1942
+		// to delete them
1943
+		$items_for_deletion = $this->_get_all_wpdb_results($query_params);
1944
+		$columns_and_ids_for_deleting = $this->_get_ids_for_delete($items_for_deletion, $allow_blocking);
1945
+		$deletion_where_query_part = $this->_build_query_part_for_deleting_from_columns_and_values(
1946
+			$columns_and_ids_for_deleting
1947
+		);
1948
+		/**
1949
+		 * Allows client code to act on the items being deleted before the query is actually executed.
1950
+		 *
1951
+		 * @param EEM_Base $this  The model instance being acted on.
1952
+		 * @param array    $query_params  The incoming array of query parameters influencing what gets deleted.
1953
+		 * @param bool     $allow_blocking @see param description in method phpdoc block.
1954
+		 * @param array $columns_and_ids_for_deleting       An array indicating what entities will get removed as
1955
+		 *                                                  derived from the incoming query parameters.
1956
+		 *                                                  @see details on the structure of this array in the phpdocs
1957
+		 *                                                  for the `_get_ids_for_delete_method`
1958
+		 *
1959
+		 */
1960
+		do_action(
1961
+			'AHEE__EEM_Base__delete__before_query',
1962
+			$this,
1963
+			$query_params,
1964
+			$allow_blocking,
1965
+			$columns_and_ids_for_deleting
1966
+		);
1967
+		if ($deletion_where_query_part) {
1968
+			$model_query_info = $this->_create_model_query_info_carrier($query_params);
1969
+			$table_aliases = array_keys($this->_tables);
1970
+			$SQL = "DELETE "
1971
+				   . implode(", ", $table_aliases)
1972
+				   . " FROM "
1973
+				   . $model_query_info->get_full_join_sql()
1974
+				   . " WHERE "
1975
+				   . $deletion_where_query_part;
1976
+			$rows_deleted = $this->_do_wpdb_query('query', array($SQL));
1977
+		} else {
1978
+			$rows_deleted = 0;
1979
+		}
1980
+
1981
+		// Next, make sure those items are removed from the entity map; if they could be put into it at all; and if
1982
+		// there was no error with the delete query.
1983
+		if (
1984
+			$this->has_primary_key_field()
1985
+			&& $rows_deleted !== false
1986
+			&& isset($columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ])
1987
+		) {
1988
+			$ids_for_removal = $columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ];
1989
+			foreach ($ids_for_removal as $id) {
1990
+				if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
1991
+					unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
1992
+				}
1993
+			}
1994
+
1995
+			// delete any extra meta attached to the deleted entities but ONLY if this model is not an instance of
1996
+			// `EEM_Extra_Meta`.  In other words we want to prevent recursion on EEM_Extra_Meta::delete_permanently calls
1997
+			// unnecessarily.  It's very unlikely that users will have assigned Extra Meta to Extra Meta
1998
+			// (although it is possible).
1999
+			// Note this can be skipped by using the provided filter and returning false.
2000
+			if (
2001
+				apply_filters(
2002
+					'FHEE__EEM_Base__delete_permanently__dont_delete_extra_meta_for_extra_meta',
2003
+					! $this instanceof EEM_Extra_Meta,
2004
+					$this
2005
+				)
2006
+			) {
2007
+				EEM_Extra_Meta::instance()->delete_permanently(array(
2008
+					0 => array(
2009
+						'EXM_type' => $this->get_this_model_name(),
2010
+						'OBJ_ID'   => array(
2011
+							'IN',
2012
+							$ids_for_removal
2013
+						)
2014
+					)
2015
+				));
2016
+			}
2017
+		}
2018
+
2019
+		/**
2020
+		 * Action called just after performing a real deletion query. Although at this point the
2021
+		 * items should have been deleted
2022
+		 *
2023
+		 * @param EEM_Base $model
2024
+		 * @param array    $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2025
+		 * @param int      $rows_deleted
2026
+		 */
2027
+		do_action('AHEE__EEM_Base__delete__end', $this, $query_params, $rows_deleted, $columns_and_ids_for_deleting);
2028
+		return $rows_deleted;// how many supposedly got deleted
2029
+	}
2030
+
2031
+
2032
+
2033
+	/**
2034
+	 * Checks all the relations that throw error messages when there are blocking related objects
2035
+	 * for related model objects. If there are any related model objects on those relations,
2036
+	 * adds an EE_Error, and return true
2037
+	 *
2038
+	 * @param EE_Base_Class|int $this_model_obj_or_id
2039
+	 * @param EE_Base_Class     $ignore_this_model_obj a model object like 'EE_Event', or 'EE_Term_Taxonomy', which
2040
+	 *                                                 should be ignored when determining whether there are related
2041
+	 *                                                 model objects which block this model object's deletion. Useful
2042
+	 *                                                 if you know A is related to B and are considering deleting A,
2043
+	 *                                                 but want to see if A has any other objects blocking its deletion
2044
+	 *                                                 before removing the relation between A and B
2045
+	 * @return boolean
2046
+	 * @throws EE_Error
2047
+	 */
2048
+	public function delete_is_blocked_by_related_models($this_model_obj_or_id, $ignore_this_model_obj = null)
2049
+	{
2050
+		// first, if $ignore_this_model_obj was supplied, get its model
2051
+		if ($ignore_this_model_obj && $ignore_this_model_obj instanceof EE_Base_Class) {
2052
+			$ignored_model = $ignore_this_model_obj->get_model();
2053
+		} else {
2054
+			$ignored_model = null;
2055
+		}
2056
+		// now check all the relations of $this_model_obj_or_id and see if there
2057
+		// are any related model objects blocking it?
2058
+		$is_blocked = false;
2059
+		foreach ($this->_model_relations as $relation_name => $relation_obj) {
2060
+			if ($relation_obj->block_delete_if_related_models_exist()) {
2061
+				// if $ignore_this_model_obj was supplied, then for the query
2062
+				// on that model needs to be told to ignore $ignore_this_model_obj
2063
+				if ($ignored_model && $relation_name === $ignored_model->get_this_model_name()) {
2064
+					$related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id, array(
2065
+						array(
2066
+							$ignored_model->get_primary_key_field()->get_name() => array(
2067
+								'!=',
2068
+								$ignore_this_model_obj->ID(),
2069
+							),
2070
+						),
2071
+					));
2072
+				} else {
2073
+					$related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id);
2074
+				}
2075
+				if ($related_model_objects) {
2076
+					EE_Error::add_error($relation_obj->get_deletion_error_message(), __FILE__, __FUNCTION__, __LINE__);
2077
+					$is_blocked = true;
2078
+				}
2079
+			}
2080
+		}
2081
+		return $is_blocked;
2082
+	}
2083
+
2084
+
2085
+	/**
2086
+	 * Builds the columns and values for items to delete from the incoming $row_results_for_deleting array.
2087
+	 * @param array $row_results_for_deleting
2088
+	 * @param bool  $allow_blocking
2089
+	 * @return array   The shape of this array depends on whether the model `has_primary_key_field` or not.  If the
2090
+	 *                 model DOES have a primary_key_field, then the array will be a simple single dimension array where
2091
+	 *                 the key is the fully qualified primary key column and the value is an array of ids that will be
2092
+	 *                 deleted. Example:
2093
+	 *                      array('Event.EVT_ID' => array( 1,2,3))
2094
+	 *                 If the model DOES NOT have a primary_key_field, then the array will be a two dimensional array
2095
+	 *                 where each element is a group of columns and values that get deleted. Example:
2096
+	 *                      array(
2097
+	 *                          0 => array(
2098
+	 *                              'Term_Relationship.object_id' => 1
2099
+	 *                              'Term_Relationship.term_taxonomy_id' => 5
2100
+	 *                          ),
2101
+	 *                          1 => array(
2102
+	 *                              'Term_Relationship.object_id' => 1
2103
+	 *                              'Term_Relationship.term_taxonomy_id' => 6
2104
+	 *                          )
2105
+	 *                      )
2106
+	 * @throws EE_Error
2107
+	 */
2108
+	protected function _get_ids_for_delete(array $row_results_for_deleting, $allow_blocking = true)
2109
+	{
2110
+		$ids_to_delete_indexed_by_column = array();
2111
+		if ($this->has_primary_key_field()) {
2112
+			$primary_table = $this->_get_main_table();
2113
+			$primary_table_pk_field = $this->get_field_by_column($primary_table->get_fully_qualified_pk_column());
2114
+			$other_tables = $this->_get_other_tables();
2115
+			$ids_to_delete_indexed_by_column = $query = array();
2116
+			foreach ($row_results_for_deleting as $item_to_delete) {
2117
+				// before we mark this item for deletion,
2118
+				// make sure there's no related entities blocking its deletion (if we're checking)
2119
+				if (
2120
+					$allow_blocking
2121
+					&& $this->delete_is_blocked_by_related_models(
2122
+						$item_to_delete[ $primary_table->get_fully_qualified_pk_column() ]
2123
+					)
2124
+				) {
2125
+					continue;
2126
+				}
2127
+				// primary table deletes
2128
+				if (isset($item_to_delete[ $primary_table->get_fully_qualified_pk_column() ])) {
2129
+					$ids_to_delete_indexed_by_column[ $primary_table->get_fully_qualified_pk_column() ][] =
2130
+						$item_to_delete[ $primary_table->get_fully_qualified_pk_column() ];
2131
+				}
2132
+			}
2133
+		} elseif (count($this->get_combined_primary_key_fields()) > 1) {
2134
+			$fields = $this->get_combined_primary_key_fields();
2135
+			foreach ($row_results_for_deleting as $item_to_delete) {
2136
+				$ids_to_delete_indexed_by_column_for_row = array();
2137
+				foreach ($fields as $cpk_field) {
2138
+					if ($cpk_field instanceof EE_Model_Field_Base) {
2139
+						$ids_to_delete_indexed_by_column_for_row[ $cpk_field->get_qualified_column() ] =
2140
+							$item_to_delete[ $cpk_field->get_qualified_column() ];
2141
+					}
2142
+				}
2143
+				$ids_to_delete_indexed_by_column[] = $ids_to_delete_indexed_by_column_for_row;
2144
+			}
2145
+		} else {
2146
+			// so there's no primary key and no combined key...
2147
+			// sorry, can't help you
2148
+			throw new EE_Error(
2149
+				sprintf(
2150
+					__(
2151
+						"Cannot delete objects of type %s because there is no primary key NOR combined key",
2152
+						"event_espresso"
2153
+					),
2154
+					get_class($this)
2155
+				)
2156
+			);
2157
+		}
2158
+		return $ids_to_delete_indexed_by_column;
2159
+	}
2160
+
2161
+
2162
+	/**
2163
+	 * This receives an array of columns and values set to be deleted (as prepared by _get_ids_for_delete) and prepares
2164
+	 * the corresponding query_part for the query performing the delete.
2165
+	 *
2166
+	 * @param array $ids_to_delete_indexed_by_column @see _get_ids_for_delete for how this array might be shaped.
2167
+	 * @return string
2168
+	 * @throws EE_Error
2169
+	 */
2170
+	protected function _build_query_part_for_deleting_from_columns_and_values(array $ids_to_delete_indexed_by_column)
2171
+	{
2172
+		$query_part = '';
2173
+		if (empty($ids_to_delete_indexed_by_column)) {
2174
+			return $query_part;
2175
+		} elseif ($this->has_primary_key_field()) {
2176
+			$query = array();
2177
+			foreach ($ids_to_delete_indexed_by_column as $column => $ids) {
2178
+				$query[] = $column . ' IN' . $this->_construct_in_value($ids, $this->_primary_key_field);
2179
+			}
2180
+			$query_part = ! empty($query) ? implode(' AND ', $query) : $query_part;
2181
+		} elseif (count($this->get_combined_primary_key_fields()) > 1) {
2182
+			$ways_to_identify_a_row = array();
2183
+			foreach ($ids_to_delete_indexed_by_column as $ids_to_delete_indexed_by_column_for_each_row) {
2184
+				$values_for_each_combined_primary_key_for_a_row = array();
2185
+				foreach ($ids_to_delete_indexed_by_column_for_each_row as $column => $id) {
2186
+					$values_for_each_combined_primary_key_for_a_row[] = $column . '=' . $id;
2187
+				}
2188
+				$ways_to_identify_a_row[] = '('
2189
+											. implode(' AND ', $values_for_each_combined_primary_key_for_a_row)
2190
+											. ')';
2191
+			}
2192
+			$query_part = implode(' OR ', $ways_to_identify_a_row);
2193
+		}
2194
+		return $query_part;
2195
+	}
2196
+
2197
+
2198
+
2199
+	/**
2200
+	 * Gets the model field by the fully qualified name
2201
+	 * @param string $qualified_column_name eg 'Event_CPT.post_name' or $field_obj->get_qualified_column()
2202
+	 * @return EE_Model_Field_Base
2203
+	 */
2204
+	public function get_field_by_column($qualified_column_name)
2205
+	{
2206
+		foreach ($this->field_settings(true) as $field_name => $field_obj) {
2207
+			if ($field_obj->get_qualified_column() === $qualified_column_name) {
2208
+				return $field_obj;
2209
+			}
2210
+		}
2211
+		throw new EE_Error(
2212
+			sprintf(
2213
+				esc_html__('Could not find a field on the model "%1$s" for qualified column "%2$s"', 'event_espresso'),
2214
+				$this->get_this_model_name(),
2215
+				$qualified_column_name
2216
+			)
2217
+		);
2218
+	}
2219
+
2220
+
2221
+
2222
+	/**
2223
+	 * Count all the rows that match criteria the model query params.
2224
+	 * If $field_to_count isn't provided, the model's primary key is used. Otherwise, we count by field_to_count's
2225
+	 * column
2226
+	 *
2227
+	 * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2228
+	 * @param string $field_to_count field on model to count by (not column name)
2229
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2230
+	 *                               that by the setting $distinct to TRUE;
2231
+	 * @return int
2232
+	 * @throws EE_Error
2233
+	 */
2234
+	public function count($query_params = array(), $field_to_count = null, $distinct = false)
2235
+	{
2236
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
2237
+		if ($field_to_count) {
2238
+			$field_obj = $this->field_settings_for($field_to_count);
2239
+			$column_to_count = $field_obj->get_qualified_column();
2240
+		} elseif ($this->has_primary_key_field()) {
2241
+			$pk_field_obj = $this->get_primary_key_field();
2242
+			$column_to_count = $pk_field_obj->get_qualified_column();
2243
+		} else {
2244
+			// there's no primary key
2245
+			// if we're counting distinct items, and there's no primary key,
2246
+			// we need to list out the columns for distinction;
2247
+			// otherwise we can just use star
2248
+			if ($distinct) {
2249
+				$columns_to_use = array();
2250
+				foreach ($this->get_combined_primary_key_fields() as $field_obj) {
2251
+					$columns_to_use[] = $field_obj->get_qualified_column();
2252
+				}
2253
+				$column_to_count = implode(',', $columns_to_use);
2254
+			} else {
2255
+				$column_to_count = '*';
2256
+			}
2257
+		}
2258
+		$column_to_count = $distinct ? "DISTINCT " . $column_to_count : $column_to_count;
2259
+		$SQL = "SELECT COUNT(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2260
+		return (int) $this->_do_wpdb_query('get_var', array($SQL));
2261
+	}
2262
+
2263
+
2264
+
2265
+	/**
2266
+	 * Sums up the value of the $field_to_sum (defaults to the primary key, which isn't terribly useful)
2267
+	 *
2268
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2269
+	 * @param string $field_to_sum name of field (array key in $_fields array)
2270
+	 * @return float
2271
+	 * @throws EE_Error
2272
+	 */
2273
+	public function sum($query_params, $field_to_sum = null)
2274
+	{
2275
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
2276
+		if ($field_to_sum) {
2277
+			$field_obj = $this->field_settings_for($field_to_sum);
2278
+		} else {
2279
+			$field_obj = $this->get_primary_key_field();
2280
+		}
2281
+		$column_to_count = $field_obj->get_qualified_column();
2282
+		$SQL = "SELECT SUM(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2283
+		$return_value = $this->_do_wpdb_query('get_var', array($SQL));
2284
+		$data_type = $field_obj->get_wpdb_data_type();
2285
+		if ($data_type === '%d' || $data_type === '%s') {
2286
+			return (float) $return_value;
2287
+		}
2288
+		// must be %f
2289
+		return (float) $return_value;
2290
+	}
2291
+
2292
+
2293
+
2294
+	/**
2295
+	 * Just calls the specified method on $wpdb with the given arguments
2296
+	 * Consolidates a little extra error handling code
2297
+	 *
2298
+	 * @param string $wpdb_method
2299
+	 * @param array  $arguments_to_provide
2300
+	 * @throws EE_Error
2301
+	 * @global wpdb  $wpdb
2302
+	 * @return mixed
2303
+	 */
2304
+	protected function _do_wpdb_query($wpdb_method, $arguments_to_provide)
2305
+	{
2306
+		// if we're in maintenance mode level 2, DON'T run any queries
2307
+		// because level 2 indicates the database needs updating and
2308
+		// is probably out of sync with the code
2309
+		if (! EE_Maintenance_Mode::instance()->models_can_query()) {
2310
+			throw new EE_Error(sprintf(__(
2311
+				"Event Espresso Level 2 Maintenance mode is active. That means EE can not run ANY database queries until the necessary migration scripts have run which will take EE out of maintenance mode level 2. Please inform support of this error.",
2312
+				"event_espresso"
2313
+			)));
2314
+		}
2315
+		/** @type WPDB $wpdb */
2316
+		global $wpdb;
2317
+		if (! method_exists($wpdb, $wpdb_method)) {
2318
+			throw new EE_Error(sprintf(__(
2319
+				'There is no method named "%s" on Wordpress\' $wpdb object',
2320
+				'event_espresso'
2321
+			), $wpdb_method));
2322
+		}
2323
+		if (WP_DEBUG) {
2324
+			$old_show_errors_value = $wpdb->show_errors;
2325
+			$wpdb->show_errors(false);
2326
+		}
2327
+		$result = $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2328
+		$this->show_db_query_if_previously_requested($wpdb->last_query);
2329
+		if (WP_DEBUG) {
2330
+			$wpdb->show_errors($old_show_errors_value);
2331
+			if (! empty($wpdb->last_error)) {
2332
+				throw new EE_Error(sprintf(__('WPDB Error: "%s"', 'event_espresso'), $wpdb->last_error));
2333
+			}
2334
+			if ($result === false) {
2335
+				throw new EE_Error(sprintf(__(
2336
+					'WPDB Error occurred, but no error message was logged by wpdb! The wpdb method called was "%1$s" and the arguments were "%2$s"',
2337
+					'event_espresso'
2338
+				), $wpdb_method, var_export($arguments_to_provide, true)));
2339
+			}
2340
+		} elseif ($result === false) {
2341
+			EE_Error::add_error(
2342
+				sprintf(
2343
+					__(
2344
+						'A database error has occurred. Turn on WP_DEBUG for more information.||A database error occurred doing wpdb method "%1$s", with arguments "%2$s". The error was "%3$s"',
2345
+						'event_espresso'
2346
+					),
2347
+					$wpdb_method,
2348
+					var_export($arguments_to_provide, true),
2349
+					$wpdb->last_error
2350
+				),
2351
+				__FILE__,
2352
+				__FUNCTION__,
2353
+				__LINE__
2354
+			);
2355
+		}
2356
+		return $result;
2357
+	}
2358
+
2359
+
2360
+
2361
+	/**
2362
+	 * Attempts to run the indicated WPDB method with the provided arguments,
2363
+	 * and if there's an error tries to verify the DB is correct. Uses
2364
+	 * the static property EEM_Base::$_db_verification_level to determine whether
2365
+	 * we should try to fix the EE core db, the addons, or just give up
2366
+	 *
2367
+	 * @param string $wpdb_method
2368
+	 * @param array  $arguments_to_provide
2369
+	 * @return mixed
2370
+	 */
2371
+	private function _process_wpdb_query($wpdb_method, $arguments_to_provide)
2372
+	{
2373
+		/** @type WPDB $wpdb */
2374
+		global $wpdb;
2375
+		$wpdb->last_error = null;
2376
+		$result = call_user_func_array(array($wpdb, $wpdb_method), $arguments_to_provide);
2377
+		// was there an error running the query? but we don't care on new activations
2378
+		// (we're going to setup the DB anyway on new activations)
2379
+		if (
2380
+			($result === false || ! empty($wpdb->last_error))
2381
+			&& EE_System::instance()->detect_req_type() !== EE_System::req_type_new_activation
2382
+		) {
2383
+			switch (EEM_Base::$_db_verification_level) {
2384
+				case EEM_Base::db_verified_none:
2385
+					// let's double-check core's DB
2386
+					$error_message = $this->_verify_core_db($wpdb_method, $arguments_to_provide);
2387
+					break;
2388
+				case EEM_Base::db_verified_core:
2389
+					// STILL NO LOVE?? verify all the addons too. Maybe they need to be fixed
2390
+					$error_message = $this->_verify_addons_db($wpdb_method, $arguments_to_provide);
2391
+					break;
2392
+				case EEM_Base::db_verified_addons:
2393
+					// ummmm... you in trouble
2394
+					return $result;
2395
+					break;
2396
+			}
2397
+			if (! empty($error_message)) {
2398
+				EE_Log::instance()->log(__FILE__, __FUNCTION__, $error_message, 'error');
2399
+				trigger_error($error_message);
2400
+			}
2401
+			return $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2402
+		}
2403
+		return $result;
2404
+	}
2405
+
2406
+
2407
+
2408
+	/**
2409
+	 * Verifies the EE core database is up-to-date and records that we've done it on
2410
+	 * EEM_Base::$_db_verification_level
2411
+	 *
2412
+	 * @param string $wpdb_method
2413
+	 * @param array  $arguments_to_provide
2414
+	 * @return string
2415
+	 */
2416
+	private function _verify_core_db($wpdb_method, $arguments_to_provide)
2417
+	{
2418
+		/** @type WPDB $wpdb */
2419
+		global $wpdb;
2420
+		// ok remember that we've already attempted fixing the core db, in case the problem persists
2421
+		EEM_Base::$_db_verification_level = EEM_Base::db_verified_core;
2422
+		$error_message = sprintf(
2423
+			__(
2424
+				'WPDB Error "%1$s" while running wpdb method "%2$s" with arguments %3$s. Automatically attempting to fix EE Core DB',
2425
+				'event_espresso'
2426
+			),
2427
+			$wpdb->last_error,
2428
+			$wpdb_method,
2429
+			wp_json_encode($arguments_to_provide)
2430
+		);
2431
+		EE_System::instance()->initialize_db_if_no_migrations_required(false, true);
2432
+		return $error_message;
2433
+	}
2434
+
2435
+
2436
+
2437
+	/**
2438
+	 * Verifies the EE addons' database is up-to-date and records that we've done it on
2439
+	 * EEM_Base::$_db_verification_level
2440
+	 *
2441
+	 * @param $wpdb_method
2442
+	 * @param $arguments_to_provide
2443
+	 * @return string
2444
+	 */
2445
+	private function _verify_addons_db($wpdb_method, $arguments_to_provide)
2446
+	{
2447
+		/** @type WPDB $wpdb */
2448
+		global $wpdb;
2449
+		// ok remember that we've already attempted fixing the addons dbs, in case the problem persists
2450
+		EEM_Base::$_db_verification_level = EEM_Base::db_verified_addons;
2451
+		$error_message = sprintf(
2452
+			__(
2453
+				'WPDB AGAIN: Error "%1$s" while running the same method and arguments as before. Automatically attempting to fix EE Addons DB',
2454
+				'event_espresso'
2455
+			),
2456
+			$wpdb->last_error,
2457
+			$wpdb_method,
2458
+			wp_json_encode($arguments_to_provide)
2459
+		);
2460
+		EE_System::instance()->initialize_addons();
2461
+		return $error_message;
2462
+	}
2463
+
2464
+
2465
+
2466
+	/**
2467
+	 * In order to avoid repeating this code for the get_all, sum, and count functions, put the code parts
2468
+	 * that are identical in here. Returns a string of SQL of everything in a SELECT query except the beginning
2469
+	 * SELECT clause, eg " FROM wp_posts AS Event INNER JOIN ... WHERE ... ORDER BY ... LIMIT ... GROUP BY ... HAVING
2470
+	 * ..."
2471
+	 *
2472
+	 * @param EE_Model_Query_Info_Carrier $model_query_info
2473
+	 * @return string
2474
+	 */
2475
+	private function _construct_2nd_half_of_select_query(EE_Model_Query_Info_Carrier $model_query_info)
2476
+	{
2477
+		return " FROM " . $model_query_info->get_full_join_sql() .
2478
+			   $model_query_info->get_where_sql() .
2479
+			   $model_query_info->get_group_by_sql() .
2480
+			   $model_query_info->get_having_sql() .
2481
+			   $model_query_info->get_order_by_sql() .
2482
+			   $model_query_info->get_limit_sql();
2483
+	}
2484
+
2485
+
2486
+
2487
+	/**
2488
+	 * Set to easily debug the next X queries ran from this model.
2489
+	 *
2490
+	 * @param int $count
2491
+	 */
2492
+	public function show_next_x_db_queries($count = 1)
2493
+	{
2494
+		$this->_show_next_x_db_queries = $count;
2495
+	}
2496
+
2497
+
2498
+
2499
+	/**
2500
+	 * @param $sql_query
2501
+	 */
2502
+	public function show_db_query_if_previously_requested($sql_query)
2503
+	{
2504
+		if ($this->_show_next_x_db_queries > 0) {
2505
+			echo $sql_query;
2506
+			$this->_show_next_x_db_queries--;
2507
+		}
2508
+	}
2509
+
2510
+
2511
+
2512
+	/**
2513
+	 * Adds a relationship of the correct type between $modelObject and $otherModelObject.
2514
+	 * There are the 3 cases:
2515
+	 * 'belongsTo' relationship: sets $id_or_obj's foreign_key to be $other_model_id_or_obj's primary_key. If
2516
+	 * $otherModelObject has no ID, it is first saved.
2517
+	 * 'hasMany' relationship: sets $other_model_id_or_obj's foreign_key to be $id_or_obj's primary_key. If $id_or_obj
2518
+	 * has no ID, it is first saved.
2519
+	 * 'hasAndBelongsToMany' relationships: checks that there isn't already an entry in the join table, and adds one.
2520
+	 * If one of the model Objects has not yet been saved to the database, it is saved before adding the entry in the
2521
+	 * join table
2522
+	 *
2523
+	 * @param        EE_Base_Class                     /int $thisModelObject
2524
+	 * @param        EE_Base_Class                     /int $id_or_obj EE_base_Class or ID of other Model Object
2525
+	 * @param string $relationName                     , key in EEM_Base::_relations
2526
+	 *                                                 an attendee to a group, you also want to specify which role they
2527
+	 *                                                 will have in that group. So you would use this parameter to
2528
+	 *                                                 specify array('role-column-name'=>'role-id')
2529
+	 * @param array  $extra_join_model_fields_n_values This allows you to enter further query params for the relation
2530
+	 *                                                 to for relation to methods that allow you to further specify
2531
+	 *                                                 extra columns to join by (such as HABTM).  Keep in mind that the
2532
+	 *                                                 only acceptable query_params is strict "col" => "value" pairs
2533
+	 *                                                 because these will be inserted in any new rows created as well.
2534
+	 * @return EE_Base_Class which was added as a relation. Object referred to by $other_model_id_or_obj
2535
+	 * @throws EE_Error
2536
+	 */
2537
+	public function add_relationship_to(
2538
+		$id_or_obj,
2539
+		$other_model_id_or_obj,
2540
+		$relationName,
2541
+		$extra_join_model_fields_n_values = array()
2542
+	) {
2543
+		$relation_obj = $this->related_settings_for($relationName);
2544
+		return $relation_obj->add_relation_to($id_or_obj, $other_model_id_or_obj, $extra_join_model_fields_n_values);
2545
+	}
2546
+
2547
+
2548
+
2549
+	/**
2550
+	 * Removes a relationship of the correct type between $modelObject and $otherModelObject.
2551
+	 * There are the 3 cases:
2552
+	 * 'belongsTo' relationship: sets $modelObject's foreign_key to null, if that field is nullable.Otherwise throws an
2553
+	 * error
2554
+	 * 'hasMany' relationship: sets $otherModelObject's foreign_key to null,if that field is nullable.Otherwise throws
2555
+	 * an error
2556
+	 * 'hasAndBelongsToMany' relationships:removes any existing entry in the join table between the two models.
2557
+	 *
2558
+	 * @param        EE_Base_Class /int $id_or_obj
2559
+	 * @param        EE_Base_Class /int $other_model_id_or_obj EE_Base_Class or ID of other Model Object
2560
+	 * @param string $relationName key in EEM_Base::_relations
2561
+	 * @return boolean of success
2562
+	 * @throws EE_Error
2563
+	 * @param array  $where_query  This allows you to enter further query params for the relation to for relation to
2564
+	 *                             methods that allow you to further specify extra columns to join by (such as HABTM).
2565
+	 *                             Keep in mind that the only acceptable query_params is strict "col" => "value" pairs
2566
+	 *                             because these will be inserted in any new rows created as well.
2567
+	 */
2568
+	public function remove_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
2569
+	{
2570
+		$relation_obj = $this->related_settings_for($relationName);
2571
+		return $relation_obj->remove_relation_to($id_or_obj, $other_model_id_or_obj, $where_query);
2572
+	}
2573
+
2574
+
2575
+
2576
+	/**
2577
+	 * @param mixed           $id_or_obj
2578
+	 * @param string          $relationName
2579
+	 * @param array           $where_query_params
2580
+	 * @param EE_Base_Class[] objects to which relations were removed
2581
+	 * @return \EE_Base_Class[]
2582
+	 * @throws EE_Error
2583
+	 */
2584
+	public function remove_relations($id_or_obj, $relationName, $where_query_params = array())
2585
+	{
2586
+		$relation_obj = $this->related_settings_for($relationName);
2587
+		return $relation_obj->remove_relations($id_or_obj, $where_query_params);
2588
+	}
2589
+
2590
+
2591
+
2592
+	/**
2593
+	 * Gets all the related items of the specified $model_name, using $query_params.
2594
+	 * Note: by default, we remove the "default query params"
2595
+	 * because we want to get even deleted items etc.
2596
+	 *
2597
+	 * @param mixed  $id_or_obj    EE_Base_Class child or its ID
2598
+	 * @param string $model_name   like 'Event', 'Registration', etc. always singular
2599
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2600
+	 * @return EE_Base_Class[]
2601
+	 * @throws EE_Error
2602
+	 */
2603
+	public function get_all_related($id_or_obj, $model_name, $query_params = null)
2604
+	{
2605
+		$model_obj = $this->ensure_is_obj($id_or_obj);
2606
+		$relation_settings = $this->related_settings_for($model_name);
2607
+		return $relation_settings->get_all_related($model_obj, $query_params);
2608
+	}
2609
+
2610
+
2611
+
2612
+	/**
2613
+	 * Deletes all the model objects across the relation indicated by $model_name
2614
+	 * which are related to $id_or_obj which meet the criteria set in $query_params.
2615
+	 * However, if the model objects can't be deleted because of blocking related model objects, then
2616
+	 * they aren't deleted. (Unless the thing that would have been deleted can be soft-deleted, that still happens).
2617
+	 *
2618
+	 * @param EE_Base_Class|int|string $id_or_obj
2619
+	 * @param string                   $model_name
2620
+	 * @param array                    $query_params
2621
+	 * @return int how many deleted
2622
+	 * @throws EE_Error
2623
+	 */
2624
+	public function delete_related($id_or_obj, $model_name, $query_params = array())
2625
+	{
2626
+		$model_obj = $this->ensure_is_obj($id_or_obj);
2627
+		$relation_settings = $this->related_settings_for($model_name);
2628
+		return $relation_settings->delete_all_related($model_obj, $query_params);
2629
+	}
2630
+
2631
+
2632
+
2633
+	/**
2634
+	 * Hard deletes all the model objects across the relation indicated by $model_name
2635
+	 * which are related to $id_or_obj which meet the criteria set in $query_params. If
2636
+	 * the model objects can't be hard deleted because of blocking related model objects,
2637
+	 * just does a soft-delete on them instead.
2638
+	 *
2639
+	 * @param EE_Base_Class|int|string $id_or_obj
2640
+	 * @param string                   $model_name
2641
+	 * @param array                    $query_params
2642
+	 * @return int how many deleted
2643
+	 * @throws EE_Error
2644
+	 */
2645
+	public function delete_related_permanently($id_or_obj, $model_name, $query_params = array())
2646
+	{
2647
+		$model_obj = $this->ensure_is_obj($id_or_obj);
2648
+		$relation_settings = $this->related_settings_for($model_name);
2649
+		return $relation_settings->delete_related_permanently($model_obj, $query_params);
2650
+	}
2651
+
2652
+
2653
+
2654
+	/**
2655
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2656
+	 * unless otherwise specified in the $query_params
2657
+	 *
2658
+	 * @param        int             /EE_Base_Class $id_or_obj
2659
+	 * @param string $model_name     like 'Event', or 'Registration'
2660
+	 * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2661
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2662
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2663
+	 *                               that by the setting $distinct to TRUE;
2664
+	 * @return int
2665
+	 * @throws EE_Error
2666
+	 */
2667
+	public function count_related(
2668
+		$id_or_obj,
2669
+		$model_name,
2670
+		$query_params = array(),
2671
+		$field_to_count = null,
2672
+		$distinct = false
2673
+	) {
2674
+		$related_model = $this->get_related_model_obj($model_name);
2675
+		// we're just going to use the query params on the related model's normal get_all query,
2676
+		// except add a condition to say to match the current mod
2677
+		if (! isset($query_params['default_where_conditions'])) {
2678
+			$query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2679
+		}
2680
+		$this_model_name = $this->get_this_model_name();
2681
+		$this_pk_field_name = $this->get_primary_key_field()->get_name();
2682
+		$query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2683
+		return $related_model->count($query_params, $field_to_count, $distinct);
2684
+	}
2685
+
2686
+
2687
+
2688
+	/**
2689
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2690
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2691
+	 *
2692
+	 * @param        int           /EE_Base_Class $id_or_obj
2693
+	 * @param string $model_name   like 'Event', or 'Registration'
2694
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2695
+	 * @param string $field_to_sum name of field to count by. By default, uses primary key
2696
+	 * @return float
2697
+	 * @throws EE_Error
2698
+	 */
2699
+	public function sum_related($id_or_obj, $model_name, $query_params, $field_to_sum = null)
2700
+	{
2701
+		$related_model = $this->get_related_model_obj($model_name);
2702
+		if (! is_array($query_params)) {
2703
+			EE_Error::doing_it_wrong(
2704
+				'EEM_Base::sum_related',
2705
+				sprintf(
2706
+					__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
2707
+					gettype($query_params)
2708
+				),
2709
+				'4.6.0'
2710
+			);
2711
+			$query_params = array();
2712
+		}
2713
+		// we're just going to use the query params on the related model's normal get_all query,
2714
+		// except add a condition to say to match the current mod
2715
+		if (! isset($query_params['default_where_conditions'])) {
2716
+			$query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2717
+		}
2718
+		$this_model_name = $this->get_this_model_name();
2719
+		$this_pk_field_name = $this->get_primary_key_field()->get_name();
2720
+		$query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2721
+		return $related_model->sum($query_params, $field_to_sum);
2722
+	}
2723
+
2724
+
2725
+
2726
+	/**
2727
+	 * Uses $this->_relatedModels info to find the first related model object of relation $relationName to the given
2728
+	 * $modelObject
2729
+	 *
2730
+	 * @param int | EE_Base_Class $id_or_obj        EE_Base_Class child or its ID
2731
+	 * @param string              $other_model_name , key in $this->_relatedModels, eg 'Registration', or 'Events'
2732
+	 * @param array               $query_params     @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2733
+	 * @return EE_Base_Class
2734
+	 * @throws EE_Error
2735
+	 */
2736
+	public function get_first_related(EE_Base_Class $id_or_obj, $other_model_name, $query_params)
2737
+	{
2738
+		$query_params['limit'] = 1;
2739
+		$results = $this->get_all_related($id_or_obj, $other_model_name, $query_params);
2740
+		if ($results) {
2741
+			return array_shift($results);
2742
+		}
2743
+		return null;
2744
+	}
2745
+
2746
+
2747
+
2748
+	/**
2749
+	 * Gets the model's name as it's expected in queries. For example, if this is EEM_Event model, that would be Event
2750
+	 *
2751
+	 * @return string
2752
+	 */
2753
+	public function get_this_model_name()
2754
+	{
2755
+		return str_replace("EEM_", "", get_class($this));
2756
+	}
2757
+
2758
+
2759
+
2760
+	/**
2761
+	 * Gets the model field on this model which is of type EE_Any_Foreign_Model_Name_Field
2762
+	 *
2763
+	 * @return EE_Any_Foreign_Model_Name_Field
2764
+	 * @throws EE_Error
2765
+	 */
2766
+	public function get_field_containing_related_model_name()
2767
+	{
2768
+		foreach ($this->field_settings(true) as $field) {
2769
+			if ($field instanceof EE_Any_Foreign_Model_Name_Field) {
2770
+				$field_with_model_name = $field;
2771
+			}
2772
+		}
2773
+		if (! isset($field_with_model_name) || ! $field_with_model_name) {
2774
+			throw new EE_Error(sprintf(
2775
+				__("There is no EE_Any_Foreign_Model_Name field on model %s", "event_espresso"),
2776
+				$this->get_this_model_name()
2777
+			));
2778
+		}
2779
+		return $field_with_model_name;
2780
+	}
2781
+
2782
+
2783
+
2784
+	/**
2785
+	 * Inserts a new entry into the database, for each table.
2786
+	 * Note: does not add the item to the entity map because that is done by EE_Base_Class::save() right after this.
2787
+	 * If client code uses EEM_Base::insert() directly, then although the item isn't in the entity map,
2788
+	 * we also know there is no model object with the newly inserted item's ID at the moment (because
2789
+	 * if there were, then they would already be in the DB and this would fail); and in the future if someone
2790
+	 * creates a model object with this ID (or grabs it from the DB) then it will be added to the
2791
+	 * entity map at that time anyways. SO, no need for EEM_Base::insert ot add to the entity map
2792
+	 *
2793
+	 * @param array $field_n_values keys are field names, values are their values (in the client code's domain if
2794
+	 *                              $values_already_prepared_by_model_object is false, in the model object's domain if
2795
+	 *                              $values_already_prepared_by_model_object is true. See comment about this at the top
2796
+	 *                              of EEM_Base)
2797
+	 * @return int|string new primary key on main table that got inserted
2798
+	 * @throws EE_Error
2799
+	 */
2800
+	public function insert($field_n_values)
2801
+	{
2802
+		/**
2803
+		 * Filters the fields and their values before inserting an item using the models
2804
+		 *
2805
+		 * @param array    $fields_n_values keys are the fields and values are their new values
2806
+		 * @param EEM_Base $model           the model used
2807
+		 */
2808
+		$field_n_values = (array) apply_filters('FHEE__EEM_Base__insert__fields_n_values', $field_n_values, $this);
2809
+		if ($this->_satisfies_unique_indexes($field_n_values)) {
2810
+			$main_table = $this->_get_main_table();
2811
+			$new_id = $this->_insert_into_specific_table($main_table, $field_n_values, false);
2812
+			if ($new_id !== false) {
2813
+				foreach ($this->_get_other_tables() as $other_table) {
2814
+					$this->_insert_into_specific_table($other_table, $field_n_values, $new_id);
2815
+				}
2816
+			}
2817
+			/**
2818
+			 * Done just after attempting to insert a new model object
2819
+			 *
2820
+			 * @param EEM_Base   $model           used
2821
+			 * @param array      $fields_n_values fields and their values
2822
+			 * @param int|string the              ID of the newly-inserted model object
2823
+			 */
2824
+			do_action('AHEE__EEM_Base__insert__end', $this, $field_n_values, $new_id);
2825
+			return $new_id;
2826
+		}
2827
+		return false;
2828
+	}
2829
+
2830
+
2831
+
2832
+	/**
2833
+	 * Checks that the result would satisfy the unique indexes on this model
2834
+	 *
2835
+	 * @param array  $field_n_values
2836
+	 * @param string $action
2837
+	 * @return boolean
2838
+	 * @throws EE_Error
2839
+	 */
2840
+	protected function _satisfies_unique_indexes($field_n_values, $action = 'insert')
2841
+	{
2842
+		foreach ($this->unique_indexes() as $index_name => $index) {
2843
+			$uniqueness_where_params = array_intersect_key($field_n_values, $index->fields());
2844
+			if ($this->exists(array($uniqueness_where_params))) {
2845
+				EE_Error::add_error(
2846
+					sprintf(
2847
+						__(
2848
+							"Could not %s %s. %s uniqueness index failed. Fields %s must form a unique set, but an entry already exists with values %s.",
2849
+							"event_espresso"
2850
+						),
2851
+						$action,
2852
+						$this->_get_class_name(),
2853
+						$index_name,
2854
+						implode(",", $index->field_names()),
2855
+						http_build_query($uniqueness_where_params)
2856
+					),
2857
+					__FILE__,
2858
+					__FUNCTION__,
2859
+					__LINE__
2860
+				);
2861
+				return false;
2862
+			}
2863
+		}
2864
+		return true;
2865
+	}
2866
+
2867
+
2868
+
2869
+	/**
2870
+	 * Checks the database for an item that conflicts (ie, if this item were
2871
+	 * saved to the DB would break some uniqueness requirement, like a primary key
2872
+	 * or an index primary key set) with the item specified. $id_obj_or_fields_array
2873
+	 * can be either an EE_Base_Class or an array of fields n values
2874
+	 *
2875
+	 * @param EE_Base_Class|array $obj_or_fields_array
2876
+	 * @param boolean             $include_primary_key whether to use the model object's primary key
2877
+	 *                                                 when looking for conflicts
2878
+	 *                                                 (ie, if false, we ignore the model object's primary key
2879
+	 *                                                 when finding "conflicts". If true, it's also considered).
2880
+	 *                                                 Only works for INT primary key,
2881
+	 *                                                 STRING primary keys cannot be ignored
2882
+	 * @throws EE_Error
2883
+	 * @return EE_Base_Class|array
2884
+	 */
2885
+	public function get_one_conflicting($obj_or_fields_array, $include_primary_key = true)
2886
+	{
2887
+		if ($obj_or_fields_array instanceof EE_Base_Class) {
2888
+			$fields_n_values = $obj_or_fields_array->model_field_array();
2889
+		} elseif (is_array($obj_or_fields_array)) {
2890
+			$fields_n_values = $obj_or_fields_array;
2891
+		} else {
2892
+			throw new EE_Error(
2893
+				sprintf(
2894
+					__(
2895
+						"%s get_all_conflicting should be called with a model object or an array of field names and values, you provided %d",
2896
+						"event_espresso"
2897
+					),
2898
+					get_class($this),
2899
+					$obj_or_fields_array
2900
+				)
2901
+			);
2902
+		}
2903
+		$query_params = array();
2904
+		if (
2905
+			$this->has_primary_key_field()
2906
+			&& ($include_primary_key
2907
+				|| $this->get_primary_key_field()
2908
+				   instanceof
2909
+				   EE_Primary_Key_String_Field)
2910
+			&& isset($fields_n_values[ $this->primary_key_name() ])
2911
+		) {
2912
+			$query_params[0]['OR'][ $this->primary_key_name() ] = $fields_n_values[ $this->primary_key_name() ];
2913
+		}
2914
+		foreach ($this->unique_indexes() as $unique_index_name => $unique_index) {
2915
+			$uniqueness_where_params = array_intersect_key($fields_n_values, $unique_index->fields());
2916
+			$query_params[0]['OR'][ 'AND*' . $unique_index_name ] = $uniqueness_where_params;
2917
+		}
2918
+		// if there is nothing to base this search on, then we shouldn't find anything
2919
+		if (empty($query_params)) {
2920
+			return array();
2921
+		}
2922
+		return $this->get_one($query_params);
2923
+	}
2924
+
2925
+
2926
+
2927
+	/**
2928
+	 * Like count, but is optimized and returns a boolean instead of an int
2929
+	 *
2930
+	 * @param array $query_params
2931
+	 * @return boolean
2932
+	 * @throws EE_Error
2933
+	 */
2934
+	public function exists($query_params)
2935
+	{
2936
+		$query_params['limit'] = 1;
2937
+		return $this->count($query_params) > 0;
2938
+	}
2939
+
2940
+
2941
+
2942
+	/**
2943
+	 * Wrapper for exists, except ignores default query parameters so we're only considering ID
2944
+	 *
2945
+	 * @param int|string $id
2946
+	 * @return boolean
2947
+	 * @throws EE_Error
2948
+	 */
2949
+	public function exists_by_ID($id)
2950
+	{
2951
+		return $this->exists(
2952
+			array(
2953
+				'default_where_conditions' => EEM_Base::default_where_conditions_none,
2954
+				array(
2955
+					$this->primary_key_name() => $id,
2956
+				),
2957
+			)
2958
+		);
2959
+	}
2960
+
2961
+
2962
+
2963
+	/**
2964
+	 * Inserts a new row in $table, using the $cols_n_values which apply to that table.
2965
+	 * If a $new_id is supplied and if $table is an EE_Other_Table, we assume
2966
+	 * we need to add a foreign key column to point to $new_id (which should be the primary key's value
2967
+	 * on the main table)
2968
+	 * This is protected rather than private because private is not accessible to any child methods and there MAY be
2969
+	 * cases where we want to call it directly rather than via insert().
2970
+	 *
2971
+	 * @access   protected
2972
+	 * @param EE_Table_Base $table
2973
+	 * @param array         $fields_n_values each key should be in field's keys, and value should be an int, string or
2974
+	 *                                       float
2975
+	 * @param int           $new_id          for now we assume only int keys
2976
+	 * @throws EE_Error
2977
+	 * @global WPDB         $wpdb            only used to get the $wpdb->insert_id after performing an insert
2978
+	 * @return int ID of new row inserted, or FALSE on failure
2979
+	 */
2980
+	protected function _insert_into_specific_table(EE_Table_Base $table, $fields_n_values, $new_id = 0)
2981
+	{
2982
+		global $wpdb;
2983
+		$insertion_col_n_values = array();
2984
+		$format_for_insertion = array();
2985
+		$fields_on_table = $this->_get_fields_for_table($table->get_table_alias());
2986
+		foreach ($fields_on_table as $field_name => $field_obj) {
2987
+			// check if its an auto-incrementing column, in which case we should just leave it to do its autoincrement thing
2988
+			if ($field_obj->is_auto_increment()) {
2989
+				continue;
2990
+			}
2991
+			$prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
2992
+			// if the value we want to assign it to is NULL, just don't mention it for the insertion
2993
+			if ($prepared_value !== null) {
2994
+				$insertion_col_n_values[ $field_obj->get_table_column() ] = $prepared_value;
2995
+				$format_for_insertion[] = $field_obj->get_wpdb_data_type();
2996
+			}
2997
+		}
2998
+		if ($table instanceof EE_Secondary_Table && $new_id) {
2999
+			// its not the main table, so we should have already saved the main table's PK which we just inserted
3000
+			// so add the fk to the main table as a column
3001
+			$insertion_col_n_values[ $table->get_fk_on_table() ] = $new_id;
3002
+			$format_for_insertion[] = '%d';// yes right now we're only allowing these foreign keys to be INTs
3003
+		}
3004
+		// insert the new entry
3005
+		$result = $this->_do_wpdb_query(
3006
+			'insert',
3007
+			array($table->get_table_name(), $insertion_col_n_values, $format_for_insertion)
3008
+		);
3009
+		if ($result === false) {
3010
+			return false;
3011
+		}
3012
+		// ok, now what do we return for the ID of the newly-inserted thing?
3013
+		if ($this->has_primary_key_field()) {
3014
+			if ($this->get_primary_key_field()->is_auto_increment()) {
3015
+				return $wpdb->insert_id;
3016
+			}
3017
+			// it's not an auto-increment primary key, so
3018
+			// it must have been supplied
3019
+			return $fields_n_values[ $this->get_primary_key_field()->get_name() ];
3020
+		}
3021
+		// we can't return a  primary key because there is none. instead return
3022
+		// a unique string indicating this model
3023
+		return $this->get_index_primary_key_string($fields_n_values);
3024
+	}
3025
+
3026
+
3027
+
3028
+	/**
3029
+	 * Prepare the $field_obj 's value in $fields_n_values for use in the database.
3030
+	 * If the field doesn't allow NULL, try to use its default. (If it doesn't allow NULL,
3031
+	 * and there is no default, we pass it along. WPDB will take care of it)
3032
+	 *
3033
+	 * @param EE_Model_Field_Base $field_obj
3034
+	 * @param array               $fields_n_values
3035
+	 * @return mixed string|int|float depending on what the table column will be expecting
3036
+	 * @throws EE_Error
3037
+	 */
3038
+	protected function _prepare_value_or_use_default($field_obj, $fields_n_values)
3039
+	{
3040
+		// if this field doesn't allow nullable, don't allow it
3041
+		if (
3042
+			! $field_obj->is_nullable()
3043
+			&& (
3044
+				! isset($fields_n_values[ $field_obj->get_name() ])
3045
+				|| $fields_n_values[ $field_obj->get_name() ] === null
3046
+			)
3047
+		) {
3048
+			$fields_n_values[ $field_obj->get_name() ] = $field_obj->get_default_value();
3049
+		}
3050
+		$unprepared_value = isset($fields_n_values[ $field_obj->get_name() ])
3051
+			? $fields_n_values[ $field_obj->get_name() ]
3052
+			: null;
3053
+		return $this->_prepare_value_for_use_in_db($unprepared_value, $field_obj);
3054
+	}
3055
+
3056
+
3057
+
3058
+	/**
3059
+	 * Consolidates code for preparing  a value supplied to the model for use int eh db. Calls the field's
3060
+	 * prepare_for_use_in_db method on the value, and depending on $value_already_prepare_by_model_obj, may also call
3061
+	 * the field's prepare_for_set() method.
3062
+	 *
3063
+	 * @param mixed               $value value in the client code domain if $value_already_prepared_by_model_object is
3064
+	 *                                   false, otherwise a value in the model object's domain (see lengthy comment at
3065
+	 *                                   top of file)
3066
+	 * @param EE_Model_Field_Base $field field which will be doing the preparing of the value. If null, we assume
3067
+	 *                                   $value is a custom selection
3068
+	 * @return mixed a value ready for use in the database for insertions, updating, or in a where clause
3069
+	 */
3070
+	private function _prepare_value_for_use_in_db($value, $field)
3071
+	{
3072
+		if ($field && $field instanceof EE_Model_Field_Base) {
3073
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
3074
+			switch ($this->_values_already_prepared_by_model_object) {
3075
+				/** @noinspection PhpMissingBreakStatementInspection */
3076
+				case self::not_prepared_by_model_object:
3077
+					$value = $field->prepare_for_set($value);
3078
+				// purposefully left out "return"
3079
+				case self::prepared_by_model_object:
3080
+					/** @noinspection SuspiciousAssignmentsInspection */
3081
+					$value = $field->prepare_for_use_in_db($value);
3082
+				case self::prepared_for_use_in_db:
3083
+					// leave the value alone
3084
+			}
3085
+			return $value;
3086
+			// phpcs:enable
3087
+		}
3088
+		return $value;
3089
+	}
3090
+
3091
+
3092
+
3093
+	/**
3094
+	 * Returns the main table on this model
3095
+	 *
3096
+	 * @return EE_Primary_Table
3097
+	 * @throws EE_Error
3098
+	 */
3099
+	protected function _get_main_table()
3100
+	{
3101
+		foreach ($this->_tables as $table) {
3102
+			if ($table instanceof EE_Primary_Table) {
3103
+				return $table;
3104
+			}
3105
+		}
3106
+		throw new EE_Error(sprintf(__(
3107
+			'There are no main tables on %s. They should be added to _tables array in the constructor',
3108
+			'event_espresso'
3109
+		), get_class($this)));
3110
+	}
3111
+
3112
+
3113
+
3114
+	/**
3115
+	 * table
3116
+	 * returns EE_Primary_Table table name
3117
+	 *
3118
+	 * @return string
3119
+	 * @throws EE_Error
3120
+	 */
3121
+	public function table()
3122
+	{
3123
+		return $this->_get_main_table()->get_table_name();
3124
+	}
3125
+
3126
+
3127
+
3128
+	/**
3129
+	 * table
3130
+	 * returns first EE_Secondary_Table table name
3131
+	 *
3132
+	 * @return string
3133
+	 */
3134
+	public function second_table()
3135
+	{
3136
+		// grab second table from tables array
3137
+		$second_table = end($this->_tables);
3138
+		return $second_table instanceof EE_Secondary_Table ? $second_table->get_table_name() : null;
3139
+	}
3140
+
3141
+
3142
+
3143
+	/**
3144
+	 * get_table_obj_by_alias
3145
+	 * returns table name given it's alias
3146
+	 *
3147
+	 * @param string $table_alias
3148
+	 * @return EE_Primary_Table | EE_Secondary_Table
3149
+	 */
3150
+	public function get_table_obj_by_alias($table_alias = '')
3151
+	{
3152
+		return isset($this->_tables[ $table_alias ]) ? $this->_tables[ $table_alias ] : null;
3153
+	}
3154
+
3155
+
3156
+
3157
+	/**
3158
+	 * Gets all the tables of type EE_Other_Table from EEM_CPT_Basel_Model::_tables
3159
+	 *
3160
+	 * @return EE_Secondary_Table[]
3161
+	 */
3162
+	protected function _get_other_tables()
3163
+	{
3164
+		$other_tables = array();
3165
+		foreach ($this->_tables as $table_alias => $table) {
3166
+			if ($table instanceof EE_Secondary_Table) {
3167
+				$other_tables[ $table_alias ] = $table;
3168
+			}
3169
+		}
3170
+		return $other_tables;
3171
+	}
3172
+
3173
+
3174
+
3175
+	/**
3176
+	 * Finds all the fields that correspond to the given table
3177
+	 *
3178
+	 * @param string $table_alias , array key in EEM_Base::_tables
3179
+	 * @return EE_Model_Field_Base[]
3180
+	 */
3181
+	public function _get_fields_for_table($table_alias)
3182
+	{
3183
+		return $this->_fields[ $table_alias ];
3184
+	}
3185
+
3186
+
3187
+
3188
+	/**
3189
+	 * Recurses through all the where parameters, and finds all the related models we'll need
3190
+	 * to complete this query. Eg, given where parameters like array('EVT_ID'=>3) from within Event model, we won't
3191
+	 * need any related models. But if the array were array('Registrations.REG_ID'=>3), we'd need the related
3192
+	 * Registration model. If it were array('Registrations.Transactions.Payments.PAY_ID'=>3), then we'd need the
3193
+	 * related Registration, Transaction, and Payment models.
3194
+	 *
3195
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3196
+	 * @return EE_Model_Query_Info_Carrier
3197
+	 * @throws EE_Error
3198
+	 */
3199
+	public function _extract_related_models_from_query($query_params)
3200
+	{
3201
+		$query_info_carrier = new EE_Model_Query_Info_Carrier();
3202
+		if (array_key_exists(0, $query_params)) {
3203
+			$this->_extract_related_models_from_sub_params_array_keys($query_params[0], $query_info_carrier, 0);
3204
+		}
3205
+		if (array_key_exists('group_by', $query_params)) {
3206
+			if (is_array($query_params['group_by'])) {
3207
+				$this->_extract_related_models_from_sub_params_array_values(
3208
+					$query_params['group_by'],
3209
+					$query_info_carrier,
3210
+					'group_by'
3211
+				);
3212
+			} elseif (! empty($query_params['group_by'])) {
3213
+				$this->_extract_related_model_info_from_query_param(
3214
+					$query_params['group_by'],
3215
+					$query_info_carrier,
3216
+					'group_by'
3217
+				);
3218
+			}
3219
+		}
3220
+		if (array_key_exists('having', $query_params)) {
3221
+			$this->_extract_related_models_from_sub_params_array_keys(
3222
+				$query_params[0],
3223
+				$query_info_carrier,
3224
+				'having'
3225
+			);
3226
+		}
3227
+		if (array_key_exists('order_by', $query_params)) {
3228
+			if (is_array($query_params['order_by'])) {
3229
+				$this->_extract_related_models_from_sub_params_array_keys(
3230
+					$query_params['order_by'],
3231
+					$query_info_carrier,
3232
+					'order_by'
3233
+				);
3234
+			} elseif (! empty($query_params['order_by'])) {
3235
+				$this->_extract_related_model_info_from_query_param(
3236
+					$query_params['order_by'],
3237
+					$query_info_carrier,
3238
+					'order_by'
3239
+				);
3240
+			}
3241
+		}
3242
+		if (array_key_exists('force_join', $query_params)) {
3243
+			$this->_extract_related_models_from_sub_params_array_values(
3244
+				$query_params['force_join'],
3245
+				$query_info_carrier,
3246
+				'force_join'
3247
+			);
3248
+		}
3249
+		$this->extractRelatedModelsFromCustomSelects($query_info_carrier);
3250
+		return $query_info_carrier;
3251
+	}
3252
+
3253
+
3254
+
3255
+	/**
3256
+	 * For extracting related models from WHERE (0), HAVING (having), ORDER BY (order_by) or forced joins (force_join)
3257
+	 *
3258
+	 * @param array                       $sub_query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#-0-where-conditions
3259
+	 * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3260
+	 * @param string                      $query_param_type one of $this->_allowed_query_params
3261
+	 * @throws EE_Error
3262
+	 * @return \EE_Model_Query_Info_Carrier
3263
+	 */
3264
+	private function _extract_related_models_from_sub_params_array_keys(
3265
+		$sub_query_params,
3266
+		EE_Model_Query_Info_Carrier $model_query_info_carrier,
3267
+		$query_param_type
3268
+	) {
3269
+		if (! empty($sub_query_params)) {
3270
+			$sub_query_params = (array) $sub_query_params;
3271
+			foreach ($sub_query_params as $param => $possibly_array_of_params) {
3272
+				// $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3273
+				$this->_extract_related_model_info_from_query_param(
3274
+					$param,
3275
+					$model_query_info_carrier,
3276
+					$query_param_type
3277
+				);
3278
+				// if $possibly_array_of_params is an array, try recursing into it, searching for keys which
3279
+				// indicate needed joins. Eg, array('NOT'=>array('Registration.TXN_ID'=>23)). In this case, we tried
3280
+				// extracting models out of the 'NOT', which obviously wasn't successful, and then we recurse into the value
3281
+				// of array('Registration.TXN_ID'=>23)
3282
+				$query_param_sans_stars = $this->_remove_stars_and_anything_after_from_condition_query_param_key($param);
3283
+				if (in_array($query_param_sans_stars, $this->_logic_query_param_keys, true)) {
3284
+					if (! is_array($possibly_array_of_params)) {
3285
+						throw new EE_Error(sprintf(
3286
+							__(
3287
+								"You used a special where query param %s, but the value isn't an array of where query params, it's just %s'. It should be an array, eg array('EVT_ID'=>23,'OR'=>array('Venue.VNU_ID'=>32,'Venue.VNU_name'=>'monkey_land'))",
3288
+								"event_espresso"
3289
+							),
3290
+							$param,
3291
+							$possibly_array_of_params
3292
+						));
3293
+					}
3294
+					$this->_extract_related_models_from_sub_params_array_keys(
3295
+						$possibly_array_of_params,
3296
+						$model_query_info_carrier,
3297
+						$query_param_type
3298
+					);
3299
+				} elseif (
3300
+					$query_param_type === 0 // ie WHERE
3301
+						  && is_array($possibly_array_of_params)
3302
+						  && isset($possibly_array_of_params[2])
3303
+						  && $possibly_array_of_params[2] == true
3304
+				) {
3305
+					// then $possible_array_of_params looks something like array('<','DTT_sold',true)
3306
+					// indicating that $possible_array_of_params[1] is actually a field name,
3307
+					// from which we should extract query parameters!
3308
+					if (! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3309
+						throw new EE_Error(sprintf(__(
3310
+							"Improperly formed query parameter %s. It should be numerically indexed like array('<','DTT_sold',true); but you provided %s",
3311
+							"event_espresso"
3312
+						), $query_param_type, implode(",", $possibly_array_of_params)));
3313
+					}
3314
+					$this->_extract_related_model_info_from_query_param(
3315
+						$possibly_array_of_params[1],
3316
+						$model_query_info_carrier,
3317
+						$query_param_type
3318
+					);
3319
+				}
3320
+			}
3321
+		}
3322
+		return $model_query_info_carrier;
3323
+	}
3324
+
3325
+
3326
+
3327
+	/**
3328
+	 * For extracting related models from forced_joins, where the array values contain the info about what
3329
+	 * models to join with. Eg an array like array('Attendee','Price.Price_Type');
3330
+	 *
3331
+	 * @param array                       $sub_query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3332
+	 * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3333
+	 * @param string                      $query_param_type one of $this->_allowed_query_params
3334
+	 * @throws EE_Error
3335
+	 * @return \EE_Model_Query_Info_Carrier
3336
+	 */
3337
+	private function _extract_related_models_from_sub_params_array_values(
3338
+		$sub_query_params,
3339
+		EE_Model_Query_Info_Carrier $model_query_info_carrier,
3340
+		$query_param_type
3341
+	) {
3342
+		if (! empty($sub_query_params)) {
3343
+			if (! is_array($sub_query_params)) {
3344
+				throw new EE_Error(sprintf(
3345
+					__("Query parameter %s should be an array, but it isn't.", "event_espresso"),
3346
+					$sub_query_params
3347
+				));
3348
+			}
3349
+			foreach ($sub_query_params as $param) {
3350
+				// $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3351
+				$this->_extract_related_model_info_from_query_param(
3352
+					$param,
3353
+					$model_query_info_carrier,
3354
+					$query_param_type
3355
+				);
3356
+			}
3357
+		}
3358
+		return $model_query_info_carrier;
3359
+	}
3360
+
3361
+
3362
+	/**
3363
+	 * Extract all the query parts from  model query params
3364
+	 * and put into a EEM_Related_Model_Info_Carrier for easy extraction into a query. We create this object
3365
+	 * instead of directly constructing the SQL because often we need to extract info from the $query_params
3366
+	 * but use them in a different order. Eg, we need to know what models we are querying
3367
+	 * before we know what joins to perform. However, we need to know what data types correspond to which fields on
3368
+	 * other models before we can finalize the where clause SQL.
3369
+	 *
3370
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3371
+	 * @throws EE_Error
3372
+	 * @return EE_Model_Query_Info_Carrier
3373
+	 * @throws ModelConfigurationException
3374
+	 */
3375
+	public function _create_model_query_info_carrier($query_params)
3376
+	{
3377
+		if (! is_array($query_params)) {
3378
+			EE_Error::doing_it_wrong(
3379
+				'EEM_Base::_create_model_query_info_carrier',
3380
+				sprintf(
3381
+					__(
3382
+						'$query_params should be an array, you passed a variable of type %s',
3383
+						'event_espresso'
3384
+					),
3385
+					gettype($query_params)
3386
+				),
3387
+				'4.6.0'
3388
+			);
3389
+			$query_params = array();
3390
+		}
3391
+		$query_params[0] = isset($query_params[0]) ? $query_params[0] : array();
3392
+		// first check if we should alter the query to account for caps or not
3393
+		// because the caps might require us to do extra joins
3394
+		if (isset($query_params['caps']) && $query_params['caps'] !== 'none') {
3395
+			$query_params[0] = array_replace_recursive(
3396
+				$query_params[0],
3397
+				$this->caps_where_conditions(
3398
+					$query_params['caps']
3399
+				)
3400
+			);
3401
+		}
3402
+
3403
+		// check if we should alter the query to remove data related to protected
3404
+		// custom post types
3405
+		if (isset($query_params['exclude_protected']) && $query_params['exclude_protected'] === true) {
3406
+			$where_param_key_for_password = $this->modelChainAndPassword();
3407
+			// only include if related to a cpt where no password has been set
3408
+			$query_params[0]['OR*nopassword'] = array(
3409
+				$where_param_key_for_password => '',
3410
+				$where_param_key_for_password . '*' => array('IS_NULL')
3411
+			);
3412
+		}
3413
+		$query_object = $this->_extract_related_models_from_query($query_params);
3414
+		// verify where_query_params has NO numeric indexes.... that's simply not how you use it!
3415
+		foreach ($query_params[0] as $key => $value) {
3416
+			if (is_int($key)) {
3417
+				throw new EE_Error(
3418
+					sprintf(
3419
+						__(
3420
+							"WHERE query params must NOT be numerically-indexed. You provided the array key '%s' for value '%s' while querying model %s. All the query params provided were '%s' Please read documentation on EEM_Base::get_all.",
3421
+							"event_espresso"
3422
+						),
3423
+						$key,
3424
+						var_export($value, true),
3425
+						var_export($query_params, true),
3426
+						get_class($this)
3427
+					)
3428
+				);
3429
+			}
3430
+		}
3431
+		if (
3432
+			array_key_exists('default_where_conditions', $query_params)
3433
+			&& ! empty($query_params['default_where_conditions'])
3434
+		) {
3435
+			$use_default_where_conditions = $query_params['default_where_conditions'];
3436
+		} else {
3437
+			$use_default_where_conditions = EEM_Base::default_where_conditions_all;
3438
+		}
3439
+		$query_params[0] = array_merge(
3440
+			$this->_get_default_where_conditions_for_models_in_query(
3441
+				$query_object,
3442
+				$use_default_where_conditions,
3443
+				$query_params[0]
3444
+			),
3445
+			$query_params[0]
3446
+		);
3447
+		$query_object->set_where_sql($this->_construct_where_clause($query_params[0]));
3448
+		// if this is a "on_join_limit" then we are limiting on on a specific table in a multi_table join.
3449
+		// So we need to setup a subquery and use that for the main join.
3450
+		// Note for now this only works on the primary table for the model.
3451
+		// So for instance, you could set the limit array like this:
3452
+		// array( 'on_join_limit' => array('Primary_Table_Alias', array(1,10) ) )
3453
+		if (array_key_exists('on_join_limit', $query_params) && ! empty($query_params['on_join_limit'])) {
3454
+			$query_object->set_main_model_join_sql(
3455
+				$this->_construct_limit_join_select(
3456
+					$query_params['on_join_limit'][0],
3457
+					$query_params['on_join_limit'][1]
3458
+				)
3459
+			);
3460
+		}
3461
+		// set limit
3462
+		if (array_key_exists('limit', $query_params)) {
3463
+			if (is_array($query_params['limit'])) {
3464
+				if (! isset($query_params['limit'][0], $query_params['limit'][1])) {
3465
+					$e = sprintf(
3466
+						__(
3467
+							"Invalid DB query. You passed '%s' for the LIMIT, but only the following are valid: an integer, string representing an integer, a string like 'int,int', or an array like array(int,int)",
3468
+							"event_espresso"
3469
+						),
3470
+						http_build_query($query_params['limit'])
3471
+					);
3472
+					throw new EE_Error($e . "|" . $e);
3473
+				}
3474
+				// they passed us an array for the limit. Assume it's like array(50,25), meaning offset by 50, and get 25
3475
+				$query_object->set_limit_sql(" LIMIT " . $query_params['limit'][0] . "," . $query_params['limit'][1]);
3476
+			} elseif (! empty($query_params['limit'])) {
3477
+				$query_object->set_limit_sql(" LIMIT " . $query_params['limit']);
3478
+			}
3479
+		}
3480
+		// set order by
3481
+		if (array_key_exists('order_by', $query_params)) {
3482
+			if (is_array($query_params['order_by'])) {
3483
+				// if they're using 'order_by' as an array, they can't use 'order' (because 'order_by' must
3484
+				// specify whether to ascend or descend on each field. Eg 'order_by'=>array('EVT_ID'=>'ASC'). So
3485
+				// including 'order' wouldn't make any sense if 'order_by' has already specified which way to order!
3486
+				if (array_key_exists('order', $query_params)) {
3487
+					throw new EE_Error(
3488
+						sprintf(
3489
+							__(
3490
+								"In querying %s, we are using query parameter 'order_by' as an array (keys:%s,values:%s), and so we can't use query parameter 'order' (value %s). You should just use the 'order_by' parameter ",
3491
+								"event_espresso"
3492
+							),
3493
+							get_class($this),
3494
+							implode(", ", array_keys($query_params['order_by'])),
3495
+							implode(", ", $query_params['order_by']),
3496
+							$query_params['order']
3497
+						)
3498
+					);
3499
+				}
3500
+				$this->_extract_related_models_from_sub_params_array_keys(
3501
+					$query_params['order_by'],
3502
+					$query_object,
3503
+					'order_by'
3504
+				);
3505
+				// assume it's an array of fields to order by
3506
+				$order_array = array();
3507
+				foreach ($query_params['order_by'] as $field_name_to_order_by => $order) {
3508
+					$order = $this->_extract_order($order);
3509
+					$order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by) . SP . $order;
3510
+				}
3511
+				$query_object->set_order_by_sql(" ORDER BY " . implode(",", $order_array));
3512
+			} elseif (! empty($query_params['order_by'])) {
3513
+				$this->_extract_related_model_info_from_query_param(
3514
+					$query_params['order_by'],
3515
+					$query_object,
3516
+					'order',
3517
+					$query_params['order_by']
3518
+				);
3519
+				$order = isset($query_params['order'])
3520
+					? $this->_extract_order($query_params['order'])
3521
+					: 'DESC';
3522
+				$query_object->set_order_by_sql(
3523
+					" ORDER BY " . $this->_deduce_column_name_from_query_param($query_params['order_by']) . SP . $order
3524
+				);
3525
+			}
3526
+		}
3527
+		// if 'order_by' wasn't set, maybe they are just using 'order' on its own?
3528
+		if (
3529
+			! array_key_exists('order_by', $query_params)
3530
+			&& array_key_exists('order', $query_params)
3531
+			&& ! empty($query_params['order'])
3532
+		) {
3533
+			$pk_field = $this->get_primary_key_field();
3534
+			$order = $this->_extract_order($query_params['order']);
3535
+			$query_object->set_order_by_sql(" ORDER BY " . $pk_field->get_qualified_column() . SP . $order);
3536
+		}
3537
+		// set group by
3538
+		if (array_key_exists('group_by', $query_params)) {
3539
+			if (is_array($query_params['group_by'])) {
3540
+				// it's an array, so assume we'll be grouping by a bunch of stuff
3541
+				$group_by_array = array();
3542
+				foreach ($query_params['group_by'] as $field_name_to_group_by) {
3543
+					$group_by_array[] = $this->_deduce_column_name_from_query_param($field_name_to_group_by);
3544
+				}
3545
+				$query_object->set_group_by_sql(" GROUP BY " . implode(", ", $group_by_array));
3546
+			} elseif (! empty($query_params['group_by'])) {
3547
+				$query_object->set_group_by_sql(
3548
+					" GROUP BY " . $this->_deduce_column_name_from_query_param($query_params['group_by'])
3549
+				);
3550
+			}
3551
+		}
3552
+		// set having
3553
+		if (array_key_exists('having', $query_params) && $query_params['having']) {
3554
+			$query_object->set_having_sql($this->_construct_having_clause($query_params['having']));
3555
+		}
3556
+		// now, just verify they didn't pass anything wack
3557
+		foreach ($query_params as $query_key => $query_value) {
3558
+			if (! in_array($query_key, $this->_allowed_query_params, true)) {
3559
+				throw new EE_Error(
3560
+					sprintf(
3561
+						__(
3562
+							"You passed %s as a query parameter to %s, which is illegal! The allowed query parameters are %s",
3563
+							'event_espresso'
3564
+						),
3565
+						$query_key,
3566
+						get_class($this),
3567
+						//                      print_r( $this->_allowed_query_params, TRUE )
3568
+						implode(',', $this->_allowed_query_params)
3569
+					)
3570
+				);
3571
+			}
3572
+		}
3573
+		$main_model_join_sql = $query_object->get_main_model_join_sql();
3574
+		if (empty($main_model_join_sql)) {
3575
+			$query_object->set_main_model_join_sql($this->_construct_internal_join());
3576
+		}
3577
+		return $query_object;
3578
+	}
3579
+
3580
+
3581
+
3582
+	/**
3583
+	 * Gets the where conditions that should be imposed on the query based on the
3584
+	 * context (eg reading frontend, backend, edit or delete).
3585
+	 *
3586
+	 * @param string $context one of EEM_Base::valid_cap_contexts()
3587
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3588
+	 * @throws EE_Error
3589
+	 */
3590
+	public function caps_where_conditions($context = self::caps_read)
3591
+	{
3592
+		EEM_Base::verify_is_valid_cap_context($context);
3593
+		$cap_where_conditions = array();
3594
+		$cap_restrictions = $this->caps_missing($context);
3595
+		/**
3596
+		 * @var $cap_restrictions EE_Default_Where_Conditions[]
3597
+		 */
3598
+		foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
3599
+			$cap_where_conditions = array_replace_recursive(
3600
+				$cap_where_conditions,
3601
+				$restriction_if_no_cap->get_default_where_conditions()
3602
+			);
3603
+		}
3604
+		return apply_filters(
3605
+			'FHEE__EEM_Base__caps_where_conditions__return',
3606
+			$cap_where_conditions,
3607
+			$this,
3608
+			$context,
3609
+			$cap_restrictions
3610
+		);
3611
+	}
3612
+
3613
+
3614
+
3615
+	/**
3616
+	 * Verifies that $should_be_order_string is in $this->_allowed_order_values,
3617
+	 * otherwise throws an exception
3618
+	 *
3619
+	 * @param string $should_be_order_string
3620
+	 * @return string either ASC, asc, DESC or desc
3621
+	 * @throws EE_Error
3622
+	 */
3623
+	private function _extract_order($should_be_order_string)
3624
+	{
3625
+		if (in_array($should_be_order_string, $this->_allowed_order_values)) {
3626
+			return $should_be_order_string;
3627
+		}
3628
+		throw new EE_Error(
3629
+			sprintf(
3630
+				__(
3631
+					"While performing a query on '%s', tried to use '%s' as an order parameter. ",
3632
+					"event_espresso"
3633
+				),
3634
+				get_class($this),
3635
+				$should_be_order_string
3636
+			)
3637
+		);
3638
+	}
3639
+
3640
+
3641
+
3642
+	/**
3643
+	 * Looks at all the models which are included in this query, and asks each
3644
+	 * for their universal_where_params, and returns them in the same format as $query_params[0] (where),
3645
+	 * so they can be merged
3646
+	 *
3647
+	 * @param EE_Model_Query_Info_Carrier $query_info_carrier
3648
+	 * @param string                      $use_default_where_conditions can be 'none','other_models_only', or 'all'.
3649
+	 *                                                                  'none' means NO default where conditions will
3650
+	 *                                                                  be used AT ALL during this query.
3651
+	 *                                                                  'other_models_only' means default where
3652
+	 *                                                                  conditions from other models will be used, but
3653
+	 *                                                                  not for this primary model. 'all', the default,
3654
+	 *                                                                  means default where conditions will apply as
3655
+	 *                                                                  normal
3656
+	 * @param array                       $where_query_params           @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3657
+	 * @throws EE_Error
3658
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3659
+	 */
3660
+	private function _get_default_where_conditions_for_models_in_query(
3661
+		EE_Model_Query_Info_Carrier $query_info_carrier,
3662
+		$use_default_where_conditions = EEM_Base::default_where_conditions_all,
3663
+		$where_query_params = array()
3664
+	) {
3665
+		$allowed_used_default_where_conditions_values = EEM_Base::valid_default_where_conditions();
3666
+		if (! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3667
+			throw new EE_Error(sprintf(
3668
+				__(
3669
+					"You passed an invalid value to the query parameter 'default_where_conditions' of '%s'. Allowed values are %s",
3670
+					"event_espresso"
3671
+				),
3672
+				$use_default_where_conditions,
3673
+				implode(", ", $allowed_used_default_where_conditions_values)
3674
+			));
3675
+		}
3676
+		$universal_query_params = array();
3677
+		if ($this->_should_use_default_where_conditions($use_default_where_conditions, true)) {
3678
+			$universal_query_params = $this->_get_default_where_conditions();
3679
+		} elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, true)) {
3680
+			$universal_query_params = $this->_get_minimum_where_conditions();
3681
+		}
3682
+		foreach ($query_info_carrier->get_model_names_included() as $model_relation_path => $model_name) {
3683
+			$related_model = $this->get_related_model_obj($model_name);
3684
+			if ($this->_should_use_default_where_conditions($use_default_where_conditions, false)) {
3685
+				$related_model_universal_where_params = $related_model->_get_default_where_conditions($model_relation_path);
3686
+			} elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, false)) {
3687
+				$related_model_universal_where_params = $related_model->_get_minimum_where_conditions($model_relation_path);
3688
+			} else {
3689
+				// we don't want to add full or even minimum default where conditions from this model, so just continue
3690
+				continue;
3691
+			}
3692
+			$overrides = $this->_override_defaults_or_make_null_friendly(
3693
+				$related_model_universal_where_params,
3694
+				$where_query_params,
3695
+				$related_model,
3696
+				$model_relation_path
3697
+			);
3698
+			$universal_query_params = EEH_Array::merge_arrays_and_overwrite_keys(
3699
+				$universal_query_params,
3700
+				$overrides
3701
+			);
3702
+		}
3703
+		return $universal_query_params;
3704
+	}
3705
+
3706
+
3707
+
3708
+	/**
3709
+	 * Determines whether or not we should use default where conditions for the model in question
3710
+	 * (this model, or other related models).
3711
+	 * Basically, we should use default where conditions on this model if they have requested to use them on all models,
3712
+	 * this model only, or to use minimum where conditions on all other models and normal where conditions on this one.
3713
+	 * We should use default where conditions on related models when they requested to use default where conditions
3714
+	 * on all models, or specifically just on other related models
3715
+	 * @param      $default_where_conditions_value
3716
+	 * @param bool $for_this_model false means this is for OTHER related models
3717
+	 * @return bool
3718
+	 */
3719
+	private function _should_use_default_where_conditions($default_where_conditions_value, $for_this_model = true)
3720
+	{
3721
+		return (
3722
+				   $for_this_model
3723
+				   && in_array(
3724
+					   $default_where_conditions_value,
3725
+					   array(
3726
+						   EEM_Base::default_where_conditions_all,
3727
+						   EEM_Base::default_where_conditions_this_only,
3728
+						   EEM_Base::default_where_conditions_minimum_others,
3729
+					   ),
3730
+					   true
3731
+				   )
3732
+			   )
3733
+			   || (
3734
+				   ! $for_this_model
3735
+				   && in_array(
3736
+					   $default_where_conditions_value,
3737
+					   array(
3738
+						   EEM_Base::default_where_conditions_all,
3739
+						   EEM_Base::default_where_conditions_others_only,
3740
+					   ),
3741
+					   true
3742
+				   )
3743
+			   );
3744
+	}
3745
+
3746
+	/**
3747
+	 * Determines whether or not we should use default minimum conditions for the model in question
3748
+	 * (this model, or other related models).
3749
+	 * Basically, we should use minimum where conditions on this model only if they requested all models to use minimum
3750
+	 * where conditions.
3751
+	 * We should use minimum where conditions on related models if they requested to use minimum where conditions
3752
+	 * on this model or others
3753
+	 * @param      $default_where_conditions_value
3754
+	 * @param bool $for_this_model false means this is for OTHER related models
3755
+	 * @return bool
3756
+	 */
3757
+	private function _should_use_minimum_where_conditions($default_where_conditions_value, $for_this_model = true)
3758
+	{
3759
+		return (
3760
+				   $for_this_model
3761
+				   && $default_where_conditions_value === EEM_Base::default_where_conditions_minimum_all
3762
+			   )
3763
+			   || (
3764
+				   ! $for_this_model
3765
+				   && in_array(
3766
+					   $default_where_conditions_value,
3767
+					   array(
3768
+						   EEM_Base::default_where_conditions_minimum_others,
3769
+						   EEM_Base::default_where_conditions_minimum_all,
3770
+					   ),
3771
+					   true
3772
+				   )
3773
+			   );
3774
+	}
3775
+
3776
+
3777
+	/**
3778
+	 * Checks if any of the defaults have been overridden. If there are any that AREN'T overridden,
3779
+	 * then we also add a special where condition which allows for that model's primary key
3780
+	 * to be null (which is important for JOINs. Eg, if you want to see all Events ordered by Venue's name,
3781
+	 * then Event's with NO Venue won't appear unless you allow VNU_ID to be NULL)
3782
+	 *
3783
+	 * @param array    $default_where_conditions
3784
+	 * @param array    $provided_where_conditions
3785
+	 * @param EEM_Base $model
3786
+	 * @param string   $model_relation_path like 'Transaction.Payment.'
3787
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3788
+	 * @throws EE_Error
3789
+	 */
3790
+	private function _override_defaults_or_make_null_friendly(
3791
+		$default_where_conditions,
3792
+		$provided_where_conditions,
3793
+		$model,
3794
+		$model_relation_path
3795
+	) {
3796
+		$null_friendly_where_conditions = array();
3797
+		$none_overridden = true;
3798
+		$or_condition_key_for_defaults = 'OR*' . get_class($model);
3799
+		foreach ($default_where_conditions as $key => $val) {
3800
+			if (isset($provided_where_conditions[ $key ])) {
3801
+				$none_overridden = false;
3802
+			} else {
3803
+				$null_friendly_where_conditions[ $or_condition_key_for_defaults ]['AND'][ $key ] = $val;
3804
+			}
3805
+		}
3806
+		if ($none_overridden && $default_where_conditions) {
3807
+			if ($model->has_primary_key_field()) {
3808
+				$null_friendly_where_conditions[ $or_condition_key_for_defaults ][ $model_relation_path
3809
+																				. "."
3810
+																				. $model->primary_key_name() ] = array('IS NULL');
3811
+			}/*else{
3812 3812
                 //@todo NO PK, use other defaults
3813 3813
             }*/
3814
-        }
3815
-        return $null_friendly_where_conditions;
3816
-    }
3817
-
3818
-
3819
-
3820
-    /**
3821
-     * Uses the _default_where_conditions_strategy set during __construct() to get
3822
-     * default where conditions on all get_all, update, and delete queries done by this model.
3823
-     * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3824
-     * NOT array('Event_CPT.post_type'=>'esp_event').
3825
-     *
3826
-     * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3827
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3828
-     */
3829
-    private function _get_default_where_conditions($model_relation_path = '')
3830
-    {
3831
-        if ($this->_ignore_where_strategy) {
3832
-            return array();
3833
-        }
3834
-        return $this->_default_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3835
-    }
3836
-
3837
-
3838
-
3839
-    /**
3840
-     * Uses the _minimum_where_conditions_strategy set during __construct() to get
3841
-     * minimum where conditions on all get_all, update, and delete queries done by this model.
3842
-     * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3843
-     * NOT array('Event_CPT.post_type'=>'esp_event').
3844
-     * Similar to _get_default_where_conditions
3845
-     *
3846
-     * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3847
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3848
-     */
3849
-    protected function _get_minimum_where_conditions($model_relation_path = '')
3850
-    {
3851
-        if ($this->_ignore_where_strategy) {
3852
-            return array();
3853
-        }
3854
-        return $this->_minimum_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3855
-    }
3856
-
3857
-
3858
-
3859
-    /**
3860
-     * Creates the string of SQL for the select part of a select query, everything behind SELECT and before FROM.
3861
-     * Eg, "Event.post_id, Event.post_name,Event_Detail.EVT_ID..."
3862
-     *
3863
-     * @param EE_Model_Query_Info_Carrier $model_query_info
3864
-     * @return string
3865
-     * @throws EE_Error
3866
-     */
3867
-    private function _construct_default_select_sql(EE_Model_Query_Info_Carrier $model_query_info)
3868
-    {
3869
-        $selects = $this->_get_columns_to_select_for_this_model();
3870
-        foreach ($model_query_info->get_model_names_included() as $model_relation_chain => $name_of_other_model_included) {
3871
-            $other_model_included = $this->get_related_model_obj($name_of_other_model_included);
3872
-            $other_model_selects = $other_model_included->_get_columns_to_select_for_this_model($model_relation_chain);
3873
-            foreach ($other_model_selects as $key => $value) {
3874
-                $selects[] = $value;
3875
-            }
3876
-        }
3877
-        return implode(", ", $selects);
3878
-    }
3879
-
3880
-
3881
-
3882
-    /**
3883
-     * Gets an array of columns to select for this model, which are necessary for it to create its objects.
3884
-     * So that's going to be the columns for all the fields on the model
3885
-     *
3886
-     * @param string $model_relation_chain like 'Question.Question_Group.Event'
3887
-     * @return array numerically indexed, values are columns to select and rename, eg "Event.ID AS 'Event.ID'"
3888
-     */
3889
-    public function _get_columns_to_select_for_this_model($model_relation_chain = '')
3890
-    {
3891
-        $fields = $this->field_settings();
3892
-        $selects = array();
3893
-        $table_alias_with_model_relation_chain_prefix = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
3894
-            $model_relation_chain,
3895
-            $this->get_this_model_name()
3896
-        );
3897
-        foreach ($fields as $field_obj) {
3898
-            $selects[] = $table_alias_with_model_relation_chain_prefix
3899
-                         . $field_obj->get_table_alias()
3900
-                         . "."
3901
-                         . $field_obj->get_table_column()
3902
-                         . " AS '"
3903
-                         . $table_alias_with_model_relation_chain_prefix
3904
-                         . $field_obj->get_table_alias()
3905
-                         . "."
3906
-                         . $field_obj->get_table_column()
3907
-                         . "'";
3908
-        }
3909
-        // make sure we are also getting the PKs of each table
3910
-        $tables = $this->get_tables();
3911
-        if (count($tables) > 1) {
3912
-            foreach ($tables as $table_obj) {
3913
-                $qualified_pk_column = $table_alias_with_model_relation_chain_prefix
3914
-                                       . $table_obj->get_fully_qualified_pk_column();
3915
-                if (! in_array($qualified_pk_column, $selects)) {
3916
-                    $selects[] = "$qualified_pk_column AS '$qualified_pk_column'";
3917
-                }
3918
-            }
3919
-        }
3920
-        return $selects;
3921
-    }
3922
-
3923
-
3924
-
3925
-    /**
3926
-     * Given a $query_param like 'Registration.Transaction.TXN_ID', pops off 'Registration.',
3927
-     * gets the join statement for it; gets the data types for it; and passes the remaining 'Transaction.TXN_ID'
3928
-     * onto its related Transaction object to do the same. Returns an EE_Join_And_Data_Types object which contains the
3929
-     * SQL for joining, and the data types
3930
-     *
3931
-     * @param null|string                 $original_query_param
3932
-     * @param string                      $query_param          like Registration.Transaction.TXN_ID
3933
-     * @param EE_Model_Query_Info_Carrier $passed_in_query_info
3934
-     * @param    string                   $query_param_type     like Registration.Transaction.TXN_ID
3935
-     *                                                          or 'PAY_ID'. Otherwise, we don't expect there to be a
3936
-     *                                                          column name. We only want model names, eg 'Event.Venue'
3937
-     *                                                          or 'Registration's
3938
-     * @param string                      $original_query_param what it originally was (eg
3939
-     *                                                          Registration.Transaction.TXN_ID). If null, we assume it
3940
-     *                                                          matches $query_param
3941
-     * @throws EE_Error
3942
-     * @return void only modifies the EEM_Related_Model_Info_Carrier passed into it
3943
-     */
3944
-    private function _extract_related_model_info_from_query_param(
3945
-        $query_param,
3946
-        EE_Model_Query_Info_Carrier $passed_in_query_info,
3947
-        $query_param_type,
3948
-        $original_query_param = null
3949
-    ) {
3950
-        if ($original_query_param === null) {
3951
-            $original_query_param = $query_param;
3952
-        }
3953
-        $query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);
3954
-        /** @var $allow_logic_query_params bool whether or not to allow logic_query_params like 'NOT','OR', or 'AND' */
3955
-        $allow_logic_query_params = in_array($query_param_type, array('where', 'having', 0, 'custom_selects'), true);
3956
-        $allow_fields = in_array(
3957
-            $query_param_type,
3958
-            array('where', 'having', 'order_by', 'group_by', 'order', 'custom_selects', 0),
3959
-            true
3960
-        );
3961
-        // check to see if we have a field on this model
3962
-        $this_model_fields = $this->field_settings(true);
3963
-        if (array_key_exists($query_param, $this_model_fields)) {
3964
-            if ($allow_fields) {
3965
-                return;
3966
-            }
3967
-            throw new EE_Error(
3968
-                sprintf(
3969
-                    __(
3970
-                        "Using a field name (%s) on model %s is not allowed on this query param type '%s'. Original query param was %s",
3971
-                        "event_espresso"
3972
-                    ),
3973
-                    $query_param,
3974
-                    get_class($this),
3975
-                    $query_param_type,
3976
-                    $original_query_param
3977
-                )
3978
-            );
3979
-        }
3980
-        // check if this is a special logic query param
3981
-        if (in_array($query_param, $this->_logic_query_param_keys, true)) {
3982
-            if ($allow_logic_query_params) {
3983
-                return;
3984
-            }
3985
-            throw new EE_Error(
3986
-                sprintf(
3987
-                    __(
3988
-                        'Logic query params ("%1$s") are being used incorrectly with the following query param ("%2$s") on model %3$s. %4$sAdditional Info:%4$s%5$s',
3989
-                        'event_espresso'
3990
-                    ),
3991
-                    implode('", "', $this->_logic_query_param_keys),
3992
-                    $query_param,
3993
-                    get_class($this),
3994
-                    '<br />',
3995
-                    "\t"
3996
-                    . ' $passed_in_query_info = <pre>'
3997
-                    . print_r($passed_in_query_info, true)
3998
-                    . '</pre>'
3999
-                    . "\n\t"
4000
-                    . ' $query_param_type = '
4001
-                    . $query_param_type
4002
-                    . "\n\t"
4003
-                    . ' $original_query_param = '
4004
-                    . $original_query_param
4005
-                )
4006
-            );
4007
-        }
4008
-        // check if it's a custom selection
4009
-        if (
4010
-            $this->_custom_selections instanceof CustomSelects
4011
-            && in_array($query_param, $this->_custom_selections->columnAliases(), true)
4012
-        ) {
4013
-            return;
4014
-        }
4015
-        // check if has a model name at the beginning
4016
-        // and
4017
-        // check if it's a field on a related model
4018
-        if (
4019
-            $this->extractJoinModelFromQueryParams(
4020
-                $passed_in_query_info,
4021
-                $query_param,
4022
-                $original_query_param,
4023
-                $query_param_type
4024
-            )
4025
-        ) {
4026
-            return;
4027
-        }
4028
-
4029
-        // ok so $query_param didn't start with a model name
4030
-        // and we previously confirmed it wasn't a logic query param or field on the current model
4031
-        // it's wack, that's what it is
4032
-        throw new EE_Error(
4033
-            sprintf(
4034
-                esc_html__(
4035
-                    "There is no model named '%s' related to %s. Query param type is %s and original query param is %s",
4036
-                    "event_espresso"
4037
-                ),
4038
-                $query_param,
4039
-                get_class($this),
4040
-                $query_param_type,
4041
-                $original_query_param
4042
-            )
4043
-        );
4044
-    }
4045
-
4046
-
4047
-    /**
4048
-     * Extracts any possible join model information from the provided possible_join_string.
4049
-     * This method will read the provided $possible_join_string value and determine if there are any possible model join
4050
-     * parts that should be added to the query.
4051
-     *
4052
-     * @param EE_Model_Query_Info_Carrier $query_info_carrier
4053
-     * @param string                      $possible_join_string  Such as Registration.REG_ID, or Registration
4054
-     * @param null|string                 $original_query_param
4055
-     * @param string                      $query_parameter_type  The type for the source of the $possible_join_string
4056
-     *                                                           ('where', 'order_by', 'group_by', 'custom_selects' etc.)
4057
-     * @return bool  returns true if a join was added and false if not.
4058
-     * @throws EE_Error
4059
-     */
4060
-    private function extractJoinModelFromQueryParams(
4061
-        EE_Model_Query_Info_Carrier $query_info_carrier,
4062
-        $possible_join_string,
4063
-        $original_query_param,
4064
-        $query_parameter_type
4065
-    ) {
4066
-        foreach ($this->_model_relations as $valid_related_model_name => $relation_obj) {
4067
-            if (strpos($possible_join_string, $valid_related_model_name . ".") === 0) {
4068
-                $this->_add_join_to_model($valid_related_model_name, $query_info_carrier, $original_query_param);
4069
-                $possible_join_string = substr($possible_join_string, strlen($valid_related_model_name . "."));
4070
-                if ($possible_join_string === '') {
4071
-                    // nothing left to $query_param
4072
-                    // we should actually end in a field name, not a model like this!
4073
-                    throw new EE_Error(
4074
-                        sprintf(
4075
-                            esc_html__(
4076
-                                "Query param '%s' (of type %s on model %s) shouldn't end on a period (.) ",
4077
-                                "event_espresso"
4078
-                            ),
4079
-                            $possible_join_string,
4080
-                            $query_parameter_type,
4081
-                            get_class($this),
4082
-                            $valid_related_model_name
4083
-                        )
4084
-                    );
4085
-                }
4086
-                $related_model_obj = $this->get_related_model_obj($valid_related_model_name);
4087
-                $related_model_obj->_extract_related_model_info_from_query_param(
4088
-                    $possible_join_string,
4089
-                    $query_info_carrier,
4090
-                    $query_parameter_type,
4091
-                    $original_query_param
4092
-                );
4093
-                return true;
4094
-            }
4095
-            if ($possible_join_string === $valid_related_model_name) {
4096
-                $this->_add_join_to_model(
4097
-                    $valid_related_model_name,
4098
-                    $query_info_carrier,
4099
-                    $original_query_param
4100
-                );
4101
-                return true;
4102
-            }
4103
-        }
4104
-        return false;
4105
-    }
4106
-
4107
-
4108
-    /**
4109
-     * Extracts related models from Custom Selects and sets up any joins for those related models.
4110
-     * @param EE_Model_Query_Info_Carrier $query_info_carrier
4111
-     * @throws EE_Error
4112
-     */
4113
-    private function extractRelatedModelsFromCustomSelects(EE_Model_Query_Info_Carrier $query_info_carrier)
4114
-    {
4115
-        if (
4116
-            $this->_custom_selections instanceof CustomSelects
4117
-            && ($this->_custom_selections->type() === CustomSelects::TYPE_STRUCTURED
4118
-                || $this->_custom_selections->type() == CustomSelects::TYPE_COMPLEX
4119
-            )
4120
-        ) {
4121
-            $original_selects = $this->_custom_selections->originalSelects();
4122
-            foreach ($original_selects as $alias => $select_configuration) {
4123
-                $this->extractJoinModelFromQueryParams(
4124
-                    $query_info_carrier,
4125
-                    $select_configuration[0],
4126
-                    $select_configuration[0],
4127
-                    'custom_selects'
4128
-                );
4129
-            }
4130
-        }
4131
-    }
4132
-
4133
-
4134
-
4135
-    /**
4136
-     * Privately used by _extract_related_model_info_from_query_param to add a join to $model_name
4137
-     * and store it on $passed_in_query_info
4138
-     *
4139
-     * @param string                      $model_name
4140
-     * @param EE_Model_Query_Info_Carrier $passed_in_query_info
4141
-     * @param string                      $original_query_param used to extract the relation chain between the queried
4142
-     *                                                          model and $model_name. Eg, if we are querying Event,
4143
-     *                                                          and are adding a join to 'Payment' with the original
4144
-     *                                                          query param key
4145
-     *                                                          'Registration.Transaction.Payment.PAY_amount', we want
4146
-     *                                                          to extract 'Registration.Transaction.Payment', in case
4147
-     *                                                          Payment wants to add default query params so that it
4148
-     *                                                          will know what models to prepend onto its default query
4149
-     *                                                          params or in case it wants to rename tables (in case
4150
-     *                                                          there are multiple joins to the same table)
4151
-     * @return void
4152
-     * @throws EE_Error
4153
-     */
4154
-    private function _add_join_to_model(
4155
-        $model_name,
4156
-        EE_Model_Query_Info_Carrier $passed_in_query_info,
4157
-        $original_query_param
4158
-    ) {
4159
-        $relation_obj = $this->related_settings_for($model_name);
4160
-        $model_relation_chain = EE_Model_Parser::extract_model_relation_chain($model_name, $original_query_param);
4161
-        // check if the relation is HABTM, because then we're essentially doing two joins
4162
-        // If so, join first to the JOIN table, and add its data types, and then continue as normal
4163
-        if ($relation_obj instanceof EE_HABTM_Relation) {
4164
-            $join_model_obj = $relation_obj->get_join_model();
4165
-            // replace the model specified with the join model for this relation chain, whi
4166
-            $relation_chain_to_join_model = EE_Model_Parser::replace_model_name_with_join_model_name_in_model_relation_chain(
4167
-                $model_name,
4168
-                $join_model_obj->get_this_model_name(),
4169
-                $model_relation_chain
4170
-            );
4171
-            $passed_in_query_info->merge(
4172
-                new EE_Model_Query_Info_Carrier(
4173
-                    array($relation_chain_to_join_model => $join_model_obj->get_this_model_name()),
4174
-                    $relation_obj->get_join_to_intermediate_model_statement($relation_chain_to_join_model)
4175
-                )
4176
-            );
4177
-        }
4178
-        // now just join to the other table pointed to by the relation object, and add its data types
4179
-        $passed_in_query_info->merge(
4180
-            new EE_Model_Query_Info_Carrier(
4181
-                array($model_relation_chain => $model_name),
4182
-                $relation_obj->get_join_statement($model_relation_chain)
4183
-            )
4184
-        );
4185
-    }
4186
-
4187
-
4188
-
4189
-    /**
4190
-     * Constructs SQL for where clause, like "WHERE Event.ID = 23 AND Transaction.amount > 100" etc.
4191
-     *
4192
-     * @param array $where_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4193
-     * @return string of SQL
4194
-     * @throws EE_Error
4195
-     */
4196
-    private function _construct_where_clause($where_params)
4197
-    {
4198
-        $SQL = $this->_construct_condition_clause_recursive($where_params, ' AND ');
4199
-        if ($SQL) {
4200
-            return " WHERE " . $SQL;
4201
-        }
4202
-        return '';
4203
-    }
4204
-
4205
-
4206
-
4207
-    /**
4208
-     * Just like the _construct_where_clause, except prepends 'HAVING' instead of 'WHERE',
4209
-     * and should be passed HAVING parameters, not WHERE parameters
4210
-     *
4211
-     * @param array $having_params
4212
-     * @return string
4213
-     * @throws EE_Error
4214
-     */
4215
-    private function _construct_having_clause($having_params)
4216
-    {
4217
-        $SQL = $this->_construct_condition_clause_recursive($having_params, ' AND ');
4218
-        if ($SQL) {
4219
-            return " HAVING " . $SQL;
4220
-        }
4221
-        return '';
4222
-    }
4223
-
4224
-
4225
-    /**
4226
-     * Used for creating nested WHERE conditions. Eg "WHERE ! (Event.ID = 3 OR ( Event_Meta.meta_key = 'bob' AND
4227
-     * Event_Meta.meta_value = 'foo'))"
4228
-     *
4229
-     * @param array  $where_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4230
-     * @param string $glue         joins each subclause together. Should really only be " AND " or " OR "...
4231
-     * @throws EE_Error
4232
-     * @return string of SQL
4233
-     */
4234
-    private function _construct_condition_clause_recursive($where_params, $glue = ' AND')
4235
-    {
4236
-        $where_clauses = array();
4237
-        foreach ($where_params as $query_param => $op_and_value_or_sub_condition) {
4238
-            $query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);// str_replace("*",'',$query_param);
4239
-            if (in_array($query_param, $this->_logic_query_param_keys)) {
4240
-                switch ($query_param) {
4241
-                    case 'not':
4242
-                    case 'NOT':
4243
-                        $where_clauses[] = "! ("
4244
-                                           . $this->_construct_condition_clause_recursive(
4245
-                                               $op_and_value_or_sub_condition,
4246
-                                               $glue
4247
-                                           )
4248
-                                           . ")";
4249
-                        break;
4250
-                    case 'and':
4251
-                    case 'AND':
4252
-                        $where_clauses[] = " ("
4253
-                                           . $this->_construct_condition_clause_recursive(
4254
-                                               $op_and_value_or_sub_condition,
4255
-                                               ' AND '
4256
-                                           )
4257
-                                           . ")";
4258
-                        break;
4259
-                    case 'or':
4260
-                    case 'OR':
4261
-                        $where_clauses[] = " ("
4262
-                                           . $this->_construct_condition_clause_recursive(
4263
-                                               $op_and_value_or_sub_condition,
4264
-                                               ' OR '
4265
-                                           )
4266
-                                           . ")";
4267
-                        break;
4268
-                }
4269
-            } else {
4270
-                $field_obj = $this->_deduce_field_from_query_param($query_param);
4271
-                // if it's not a normal field, maybe it's a custom selection?
4272
-                if (! $field_obj) {
4273
-                    if ($this->_custom_selections instanceof CustomSelects) {
4274
-                        $field_obj = $this->_custom_selections->getDataTypeForAlias($query_param);
4275
-                    } else {
4276
-                        throw new EE_Error(sprintf(__(
4277
-                            "%s is neither a valid model field name, nor a custom selection",
4278
-                            "event_espresso"
4279
-                        ), $query_param));
4280
-                    }
4281
-                }
4282
-                $op_and_value_sql = $this->_construct_op_and_value($op_and_value_or_sub_condition, $field_obj);
4283
-                $where_clauses[] = $this->_deduce_column_name_from_query_param($query_param) . SP . $op_and_value_sql;
4284
-            }
4285
-        }
4286
-        return $where_clauses ? implode($glue, $where_clauses) : '';
4287
-    }
4288
-
4289
-
4290
-
4291
-    /**
4292
-     * Takes the input parameter and extract the table name (alias) and column name
4293
-     *
4294
-     * @param string $query_param like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4295
-     * @throws EE_Error
4296
-     * @return string table alias and column name for SQL, eg "Transaction.TXN_ID"
4297
-     */
4298
-    private function _deduce_column_name_from_query_param($query_param)
4299
-    {
4300
-        $field = $this->_deduce_field_from_query_param($query_param);
4301
-        if ($field) {
4302
-            $table_alias_prefix = EE_Model_Parser::extract_table_alias_model_relation_chain_from_query_param(
4303
-                $field->get_model_name(),
4304
-                $query_param
4305
-            );
4306
-            return $table_alias_prefix . $field->get_qualified_column();
4307
-        }
4308
-        if (
4309
-            $this->_custom_selections instanceof CustomSelects
4310
-            && in_array($query_param, $this->_custom_selections->columnAliases(), true)
4311
-        ) {
4312
-            // maybe it's custom selection item?
4313
-            // if so, just use it as the "column name"
4314
-            return $query_param;
4315
-        }
4316
-        $custom_select_aliases = $this->_custom_selections instanceof CustomSelects
4317
-            ? implode(',', $this->_custom_selections->columnAliases())
4318
-            : '';
4319
-        throw new EE_Error(
4320
-            sprintf(
4321
-                __(
4322
-                    "%s is not a valid field on this model, nor a custom selection (%s)",
4323
-                    "event_espresso"
4324
-                ),
4325
-                $query_param,
4326
-                $custom_select_aliases
4327
-            )
4328
-        );
4329
-    }
4330
-
4331
-
4332
-
4333
-    /**
4334
-     * Removes the * and anything after it from the condition query param key. It is useful to add the * to condition
4335
-     * query param keys (eg, 'OR*', 'EVT_ID') in order for the array keys to still be unique, so that they don't get
4336
-     * overwritten Takes a string like 'Event.EVT_ID*', 'TXN_total**', 'OR*1st', and 'DTT_reg_start*foobar' to
4337
-     * 'Event.EVT_ID', 'TXN_total', 'OR', and 'DTT_reg_start', respectively.
4338
-     *
4339
-     * @param string $condition_query_param_key
4340
-     * @return string
4341
-     */
4342
-    private function _remove_stars_and_anything_after_from_condition_query_param_key($condition_query_param_key)
4343
-    {
4344
-        $pos_of_star = strpos($condition_query_param_key, '*');
4345
-        if ($pos_of_star === false) {
4346
-            return $condition_query_param_key;
4347
-        }
4348
-        $condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
4349
-        return $condition_query_param_sans_star;
4350
-    }
4351
-
4352
-
4353
-
4354
-    /**
4355
-     * creates the SQL for the operator and the value in a WHERE clause, eg "< 23" or "LIKE '%monkey%'"
4356
-     *
4357
-     * @param                            mixed      array | string    $op_and_value
4358
-     * @param EE_Model_Field_Base|string $field_obj . If string, should be one of EEM_Base::_valid_wpdb_data_types
4359
-     * @throws EE_Error
4360
-     * @return string
4361
-     */
4362
-    private function _construct_op_and_value($op_and_value, $field_obj)
4363
-    {
4364
-        if (is_array($op_and_value)) {
4365
-            $operator = isset($op_and_value[0]) ? $this->_prepare_operator_for_sql($op_and_value[0]) : null;
4366
-            if (! $operator) {
4367
-                $php_array_like_string = array();
4368
-                foreach ($op_and_value as $key => $value) {
4369
-                    $php_array_like_string[] = "$key=>$value";
4370
-                }
4371
-                throw new EE_Error(
4372
-                    sprintf(
4373
-                        __(
4374
-                            "You setup a query parameter like you were going to specify an operator, but didn't. You provided '(%s)', but the operator should be at array key index 0 (eg array('>',32))",
4375
-                            "event_espresso"
4376
-                        ),
4377
-                        implode(",", $php_array_like_string)
4378
-                    )
4379
-                );
4380
-            }
4381
-            $value = isset($op_and_value[1]) ? $op_and_value[1] : null;
4382
-        } else {
4383
-            $operator = '=';
4384
-            $value = $op_and_value;
4385
-        }
4386
-        // check to see if the value is actually another field
4387
-        if (is_array($op_and_value) && isset($op_and_value[2]) && $op_and_value[2] == true) {
4388
-            return $operator . SP . $this->_deduce_column_name_from_query_param($value);
4389
-        }
4390
-        if (in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4391
-            // in this case, the value should be an array, or at least a comma-separated list
4392
-            // it will need to handle a little differently
4393
-            $cleaned_value = $this->_construct_in_value($value, $field_obj);
4394
-            // note: $cleaned_value has already been run through $wpdb->prepare()
4395
-            return $operator . SP . $cleaned_value;
4396
-        }
4397
-        if (in_array($operator, $this->valid_between_style_operators()) && is_array($value)) {
4398
-            // the value should be an array with count of two.
4399
-            if (count($value) !== 2) {
4400
-                throw new EE_Error(
4401
-                    sprintf(
4402
-                        __(
4403
-                            "The '%s' operator must be used with an array of values and there must be exactly TWO values in that array.",
4404
-                            'event_espresso'
4405
-                        ),
4406
-                        "BETWEEN"
4407
-                    )
4408
-                );
4409
-            }
4410
-            $cleaned_value = $this->_construct_between_value($value, $field_obj);
4411
-            return $operator . SP . $cleaned_value;
4412
-        }
4413
-        if (in_array($operator, $this->valid_null_style_operators())) {
4414
-            if ($value !== null) {
4415
-                throw new EE_Error(
4416
-                    sprintf(
4417
-                        __(
4418
-                            "You attempted to give a value  (%s) while using a NULL-style operator (%s). That isn't valid",
4419
-                            "event_espresso"
4420
-                        ),
4421
-                        $value,
4422
-                        $operator
4423
-                    )
4424
-                );
4425
-            }
4426
-            return $operator;
4427
-        }
4428
-        if (in_array($operator, $this->valid_like_style_operators()) && ! is_array($value)) {
4429
-            // if the operator is 'LIKE', we want to allow percent signs (%) and not
4430
-            // remove other junk. So just treat it as a string.
4431
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, '%s');
4432
-        }
4433
-        if (! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4434
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, $field_obj);
4435
-        }
4436
-        if (in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4437
-            throw new EE_Error(
4438
-                sprintf(
4439
-                    __(
4440
-                        "Operator '%s' must be used with an array of values, eg 'Registration.REG_ID' => array('%s',array(1,2,3))",
4441
-                        'event_espresso'
4442
-                    ),
4443
-                    $operator,
4444
-                    $operator
4445
-                )
4446
-            );
4447
-        }
4448
-        if (! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4449
-            throw new EE_Error(
4450
-                sprintf(
4451
-                    __(
4452
-                        "Operator '%s' must be used with a single value, not an array. Eg 'Registration.REG_ID => array('%s',23))",
4453
-                        'event_espresso'
4454
-                    ),
4455
-                    $operator,
4456
-                    $operator
4457
-                )
4458
-            );
4459
-        }
4460
-        throw new EE_Error(
4461
-            sprintf(
4462
-                __(
4463
-                    "It appears you've provided some totally invalid query parameters. Operator and value were:'%s', which isn't right at all",
4464
-                    "event_espresso"
4465
-                ),
4466
-                http_build_query($op_and_value)
4467
-            )
4468
-        );
4469
-    }
4470
-
4471
-
4472
-
4473
-    /**
4474
-     * Creates the operands to be used in a BETWEEN query, eg "'2014-12-31 20:23:33' AND '2015-01-23 12:32:54'"
4475
-     *
4476
-     * @param array                      $values
4477
-     * @param EE_Model_Field_Base|string $field_obj if string, it should be the datatype to be used when querying, eg
4478
-     *                                              '%s'
4479
-     * @return string
4480
-     * @throws EE_Error
4481
-     */
4482
-    public function _construct_between_value($values, $field_obj)
4483
-    {
4484
-        $cleaned_values = array();
4485
-        foreach ($values as $value) {
4486
-            $cleaned_values[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4487
-        }
4488
-        return $cleaned_values[0] . " AND " . $cleaned_values[1];
4489
-    }
4490
-
4491
-
4492
-    /**
4493
-     * Takes an array or a comma-separated list of $values and cleans them
4494
-     * according to $data_type using $wpdb->prepare, and then makes the list a
4495
-     * string surrounded by ( and ). Eg, _construct_in_value(array(1,2,3),'%d') would
4496
-     * return '(1,2,3)'; _construct_in_value("1,2,hack",'%d') would return '(1,2,1)' (assuming
4497
-     * I'm right that a string, when interpreted as a digit, becomes a 1. It might become a 0)
4498
-     *
4499
-     * @param mixed                      $values    array or comma-separated string
4500
-     * @param EE_Model_Field_Base|string $field_obj if string, it should be a wpdb data type like '%s', or '%d'
4501
-     * @return string of SQL to follow an 'IN' or 'NOT IN' operator
4502
-     * @throws EE_Error
4503
-     */
4504
-    public function _construct_in_value($values, $field_obj)
4505
-    {
4506
-        $prepped = [];
4507
-        // check if the value is a CSV list
4508
-        if (is_string($values)) {
4509
-            // in which case, turn it into an array
4510
-            $values = explode(',', $values);
4511
-        }
4512
-        // make sure we only have one of each value in the list
4513
-        $values = array_unique($values);
4514
-        foreach ($values as $value) {
4515
-            $prepped[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4516
-        }
4517
-        // we would just LOVE to leave $cleaned_values as an empty array, and return the value as "()",
4518
-        // but unfortunately that's invalid SQL. So instead we return a string which we KNOW will evaluate to be the empty set
4519
-        // which is effectively equivalent to returning "()". We don't return "(0)" because that only works for auto-incrementing columns
4520
-        if (empty($prepped)) {
4521
-            $all_fields = $this->field_settings();
4522
-            $first_field    = reset($all_fields);
4523
-            $main_table = $this->_get_main_table();
4524
-            $prepped[]  = "SELECT {$first_field->get_table_column()} FROM {$main_table->get_table_name()} WHERE FALSE";
4525
-        }
4526
-        return '(' . implode(',', $prepped) . ')';
4527
-    }
4528
-
4529
-
4530
-
4531
-    /**
4532
-     * @param mixed                      $value
4533
-     * @param EE_Model_Field_Base|string $field_obj if string it should be a wpdb data type like '%d'
4534
-     * @throws EE_Error
4535
-     * @return false|null|string
4536
-     */
4537
-    private function _wpdb_prepare_using_field($value, $field_obj)
4538
-    {
4539
-        /** @type WPDB $wpdb */
4540
-        global $wpdb;
4541
-        if ($field_obj instanceof EE_Model_Field_Base) {
4542
-            return $wpdb->prepare(
4543
-                $field_obj->get_wpdb_data_type(),
4544
-                $this->_prepare_value_for_use_in_db($value, $field_obj)
4545
-            );
4546
-        } //$field_obj should really just be a data type
4547
-        if (! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4548
-            throw new EE_Error(
4549
-                sprintf(
4550
-                    __("%s is not a valid wpdb datatype. Valid ones are %s", "event_espresso"),
4551
-                    $field_obj,
4552
-                    implode(",", $this->_valid_wpdb_data_types)
4553
-                )
4554
-            );
4555
-        }
4556
-        return $wpdb->prepare($field_obj, $value);
4557
-    }
4558
-
4559
-
4560
-
4561
-    /**
4562
-     * Takes the input parameter and finds the model field that it indicates.
4563
-     *
4564
-     * @param string $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4565
-     * @throws EE_Error
4566
-     * @return EE_Model_Field_Base
4567
-     */
4568
-    protected function _deduce_field_from_query_param($query_param_name)
4569
-    {
4570
-        // ok, now proceed with deducing which part is the model's name, and which is the field's name
4571
-        // which will help us find the database table and column
4572
-        $query_param_parts = explode(".", $query_param_name);
4573
-        if (empty($query_param_parts)) {
4574
-            throw new EE_Error(sprintf(__(
4575
-                "_extract_column_name is empty when trying to extract column and table name from %s",
4576
-                'event_espresso'
4577
-            ), $query_param_name));
4578
-        }
4579
-        $number_of_parts = count($query_param_parts);
4580
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
4581
-        if ($number_of_parts === 1) {
4582
-            $field_name = $last_query_param_part;
4583
-            $model_obj = $this;
4584
-        } else {// $number_of_parts >= 2
4585
-            // the last part is the column name, and there are only 2parts. therefore...
4586
-            $field_name = $last_query_param_part;
4587
-            $model_obj = $this->get_related_model_obj($query_param_parts[ $number_of_parts - 2 ]);
4588
-        }
4589
-        try {
4590
-            return $model_obj->field_settings_for($field_name);
4591
-        } catch (EE_Error $e) {
4592
-            return null;
4593
-        }
4594
-    }
4595
-
4596
-
4597
-
4598
-    /**
4599
-     * Given a field's name (ie, a key in $this->field_settings()), uses the EE_Model_Field object to get the table's
4600
-     * alias and column which corresponds to it
4601
-     *
4602
-     * @param string $field_name
4603
-     * @throws EE_Error
4604
-     * @return string
4605
-     */
4606
-    public function _get_qualified_column_for_field($field_name)
4607
-    {
4608
-        $all_fields = $this->field_settings();
4609
-        $field = isset($all_fields[ $field_name ]) ? $all_fields[ $field_name ] : false;
4610
-        if ($field) {
4611
-            return $field->get_qualified_column();
4612
-        }
4613
-        throw new EE_Error(
4614
-            sprintf(
4615
-                __(
4616
-                    "There is no field titled %s on model %s. Either the query trying to use it is bad, or you need to add it to the list of fields on the model.",
4617
-                    'event_espresso'
4618
-                ),
4619
-                $field_name,
4620
-                get_class($this)
4621
-            )
4622
-        );
4623
-    }
4624
-
4625
-
4626
-
4627
-    /**
4628
-     * similar to \EEM_Base::_get_qualified_column_for_field() but returns an array with data for ALL fields.
4629
-     * Example usage:
4630
-     * EEM_Ticket::instance()->get_all_wpdb_results(
4631
-     *      array(),
4632
-     *      ARRAY_A,
4633
-     *      EEM_Ticket::instance()->get_qualified_columns_for_all_fields()
4634
-     *  );
4635
-     * is equivalent to
4636
-     *  EEM_Ticket::instance()->get_all_wpdb_results( array(), ARRAY_A, '*' );
4637
-     * and
4638
-     *  EEM_Event::instance()->get_all_wpdb_results(
4639
-     *      array(
4640
-     *          array(
4641
-     *              'Datetime.Ticket.TKT_ID' => array( '<', 100 ),
4642
-     *          ),
4643
-     *          ARRAY_A,
4644
-     *          implode(
4645
-     *              ', ',
4646
-     *              array_merge(
4647
-     *                  EEM_Event::instance()->get_qualified_columns_for_all_fields( '', false ),
4648
-     *                  EEM_Ticket::instance()->get_qualified_columns_for_all_fields( 'Datetime', false )
4649
-     *              )
4650
-     *          )
4651
-     *      )
4652
-     *  );
4653
-     * selects rows from the database, selecting all the event and ticket columns, where the ticket ID is below 100
4654
-     *
4655
-     * @param string $model_relation_chain        the chain of models used to join between the model you want to query
4656
-     *                                            and the one whose fields you are selecting for example: when querying
4657
-     *                                            tickets model and selecting fields from the tickets model you would
4658
-     *                                            leave this parameter empty, because no models are needed to join
4659
-     *                                            between the queried model and the selected one. Likewise when
4660
-     *                                            querying the datetime model and selecting fields from the tickets
4661
-     *                                            model, it would also be left empty, because there is a direct
4662
-     *                                            relation from datetimes to tickets, so no model is needed to join
4663
-     *                                            them together. However, when querying from the event model and
4664
-     *                                            selecting fields from the ticket model, you should provide the string
4665
-     *                                            'Datetime', indicating that the event model must first join to the
4666
-     *                                            datetime model in order to find its relation to ticket model.
4667
-     *                                            Also, when querying from the venue model and selecting fields from
4668
-     *                                            the ticket model, you should provide the string 'Event.Datetime',
4669
-     *                                            indicating you need to join the venue model to the event model,
4670
-     *                                            to the datetime model, in order to find its relation to the ticket model.
4671
-     *                                            This string is used to deduce the prefix that gets added onto the
4672
-     *                                            models' tables qualified columns
4673
-     * @param bool   $return_string               if true, will return a string with qualified column names separated
4674
-     *                                            by ', ' if false, will simply return a numerically indexed array of
4675
-     *                                            qualified column names
4676
-     * @return array|string
4677
-     */
4678
-    public function get_qualified_columns_for_all_fields($model_relation_chain = '', $return_string = true)
4679
-    {
4680
-        $table_prefix = str_replace('.', '__', $model_relation_chain) . (empty($model_relation_chain) ? '' : '__');
4681
-        $qualified_columns = array();
4682
-        foreach ($this->field_settings() as $field_name => $field) {
4683
-            $qualified_columns[] = $table_prefix . $field->get_qualified_column();
4684
-        }
4685
-        return $return_string ? implode(', ', $qualified_columns) : $qualified_columns;
4686
-    }
4687
-
4688
-
4689
-
4690
-    /**
4691
-     * constructs the select use on special limit joins
4692
-     * NOTE: for now this has only been tested and will work when the  table alias is for the PRIMARY table. Although
4693
-     * its setup so the select query will be setup on and just doing the special select join off of the primary table
4694
-     * (as that is typically where the limits would be set).
4695
-     *
4696
-     * @param  string       $table_alias The table the select is being built for
4697
-     * @param  mixed|string $limit       The limit for this select
4698
-     * @return string                The final select join element for the query.
4699
-     */
4700
-    public function _construct_limit_join_select($table_alias, $limit)
4701
-    {
4702
-        $SQL = '';
4703
-        foreach ($this->_tables as $table_obj) {
4704
-            if ($table_obj instanceof EE_Primary_Table) {
4705
-                $SQL .= $table_alias === $table_obj->get_table_alias()
4706
-                    ? $table_obj->get_select_join_limit($limit)
4707
-                    : SP . $table_obj->get_table_name() . " AS " . $table_obj->get_table_alias() . SP;
4708
-            } elseif ($table_obj instanceof EE_Secondary_Table) {
4709
-                $SQL .= $table_alias === $table_obj->get_table_alias()
4710
-                    ? $table_obj->get_select_join_limit_join($limit)
4711
-                    : SP . $table_obj->get_join_sql($table_alias) . SP;
4712
-            }
4713
-        }
4714
-        return $SQL;
4715
-    }
4716
-
4717
-
4718
-
4719
-    /**
4720
-     * Constructs the internal join if there are multiple tables, or simply the table's name and alias
4721
-     * Eg "wp_post AS Event" or "wp_post AS Event INNER JOIN wp_postmeta Event_Meta ON Event.ID = Event_Meta.post_id"
4722
-     *
4723
-     * @return string SQL
4724
-     * @throws EE_Error
4725
-     */
4726
-    public function _construct_internal_join()
4727
-    {
4728
-        $SQL = $this->_get_main_table()->get_table_sql();
4729
-        $SQL .= $this->_construct_internal_join_to_table_with_alias($this->_get_main_table()->get_table_alias());
4730
-        return $SQL;
4731
-    }
4732
-
4733
-
4734
-
4735
-    /**
4736
-     * Constructs the SQL for joining all the tables on this model.
4737
-     * Normally $alias should be the primary table's alias, but in cases where
4738
-     * we have already joined to a secondary table (eg, the secondary table has a foreign key and is joined before the
4739
-     * primary table) then we should provide that secondary table's alias. Eg, with $alias being the primary table's
4740
-     * alias, this will construct SQL like:
4741
-     * " INNER JOIN wp_esp_secondary_table AS Secondary_Table ON Primary_Table.pk = Secondary_Table.fk".
4742
-     * With $alias being a secondary table's alias, this will construct SQL like:
4743
-     * " INNER JOIN wp_esp_primary_table AS Primary_Table ON Primary_Table.pk = Secondary_Table.fk".
4744
-     *
4745
-     * @param string $alias_prefixed table alias to join to (this table should already be in the FROM SQL clause)
4746
-     * @return string
4747
-     */
4748
-    public function _construct_internal_join_to_table_with_alias($alias_prefixed)
4749
-    {
4750
-        $SQL = '';
4751
-        $alias_sans_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($alias_prefixed);
4752
-        foreach ($this->_tables as $table_obj) {
4753
-            if ($table_obj instanceof EE_Secondary_Table) {// table is secondary table
4754
-                if ($alias_sans_prefix === $table_obj->get_table_alias()) {
4755
-                    // so we're joining to this table, meaning the table is already in
4756
-                    // the FROM statement, BUT the primary table isn't. So we want
4757
-                    // to add the inverse join sql
4758
-                    $SQL .= $table_obj->get_inverse_join_sql($alias_prefixed);
4759
-                } else {
4760
-                    // just add a regular JOIN to this table from the primary table
4761
-                    $SQL .= $table_obj->get_join_sql($alias_prefixed);
4762
-                }
4763
-            }//if it's a primary table, dont add any SQL. it should already be in the FROM statement
4764
-        }
4765
-        return $SQL;
4766
-    }
4767
-
4768
-
4769
-
4770
-    /**
4771
-     * Gets an array for storing all the data types on the next-to-be-executed-query.
4772
-     * This should be a growing array of keys being table-columns (eg 'EVT_ID' and 'Event.EVT_ID'), and values being
4773
-     * their data type (eg, '%s', '%d', etc)
4774
-     *
4775
-     * @return array
4776
-     */
4777
-    public function _get_data_types()
4778
-    {
4779
-        $data_types = array();
4780
-        foreach ($this->field_settings() as $field_obj) {
4781
-            // $data_types[$field_obj->get_table_column()] = $field_obj->get_wpdb_data_type();
4782
-            /** @var $field_obj EE_Model_Field_Base */
4783
-            $data_types[ $field_obj->get_qualified_column() ] = $field_obj->get_wpdb_data_type();
4784
-        }
4785
-        return $data_types;
4786
-    }
4787
-
4788
-
4789
-
4790
-    /**
4791
-     * Gets the model object given the relation's name / model's name (eg, 'Event', 'Registration',etc. Always singular)
4792
-     *
4793
-     * @param string $model_name
4794
-     * @throws EE_Error
4795
-     * @return EEM_Base
4796
-     */
4797
-    public function get_related_model_obj($model_name)
4798
-    {
4799
-        $model_classname = "EEM_" . $model_name;
4800
-        if (! class_exists($model_classname)) {
4801
-            throw new EE_Error(sprintf(__(
4802
-                "You specified a related model named %s in your query. No such model exists, if it did, it would have the classname %s",
4803
-                'event_espresso'
4804
-            ), $model_name, $model_classname));
4805
-        }
4806
-        return call_user_func($model_classname . "::instance");
4807
-    }
4808
-
4809
-
4810
-
4811
-    /**
4812
-     * Returns the array of EE_ModelRelations for this model.
4813
-     *
4814
-     * @return EE_Model_Relation_Base[]
4815
-     */
4816
-    public function relation_settings()
4817
-    {
4818
-        return $this->_model_relations;
4819
-    }
4820
-
4821
-
4822
-
4823
-    /**
4824
-     * Gets all related models that this model BELONGS TO. Handy to know sometimes
4825
-     * because without THOSE models, this model probably doesn't have much purpose.
4826
-     * (Eg, without an event, datetimes have little purpose.)
4827
-     *
4828
-     * @return EE_Belongs_To_Relation[]
4829
-     */
4830
-    public function belongs_to_relations()
4831
-    {
4832
-        $belongs_to_relations = array();
4833
-        foreach ($this->relation_settings() as $model_name => $relation_obj) {
4834
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
4835
-                $belongs_to_relations[ $model_name ] = $relation_obj;
4836
-            }
4837
-        }
4838
-        return $belongs_to_relations;
4839
-    }
4840
-
4841
-
4842
-
4843
-    /**
4844
-     * Returns the specified EE_Model_Relation, or throws an exception
4845
-     *
4846
-     * @param string $relation_name name of relation, key in $this->_relatedModels
4847
-     * @throws EE_Error
4848
-     * @return EE_Model_Relation_Base
4849
-     */
4850
-    public function related_settings_for($relation_name)
4851
-    {
4852
-        $relatedModels = $this->relation_settings();
4853
-        if (! array_key_exists($relation_name, $relatedModels)) {
4854
-            throw new EE_Error(
4855
-                sprintf(
4856
-                    __(
4857
-                        'Cannot get %s related to %s. There is no model relation of that type. There is, however, %s...',
4858
-                        'event_espresso'
4859
-                    ),
4860
-                    $relation_name,
4861
-                    $this->_get_class_name(),
4862
-                    implode(', ', array_keys($relatedModels))
4863
-                )
4864
-            );
4865
-        }
4866
-        return $relatedModels[ $relation_name ];
4867
-    }
4868
-
4869
-
4870
-
4871
-    /**
4872
-     * A convenience method for getting a specific field's settings, instead of getting all field settings for all
4873
-     * fields
4874
-     *
4875
-     * @param string $fieldName
4876
-     * @param boolean $include_db_only_fields
4877
-     * @throws EE_Error
4878
-     * @return EE_Model_Field_Base
4879
-     */
4880
-    public function field_settings_for($fieldName, $include_db_only_fields = true)
4881
-    {
4882
-        $fieldSettings = $this->field_settings($include_db_only_fields);
4883
-        if (! array_key_exists($fieldName, $fieldSettings)) {
4884
-            throw new EE_Error(sprintf(
4885
-                __("There is no field/column '%s' on '%s'", 'event_espresso'),
4886
-                $fieldName,
4887
-                get_class($this)
4888
-            ));
4889
-        }
4890
-        return $fieldSettings[ $fieldName ];
4891
-    }
4892
-
4893
-
4894
-
4895
-    /**
4896
-     * Checks if this field exists on this model
4897
-     *
4898
-     * @param string $fieldName a key in the model's _field_settings array
4899
-     * @return boolean
4900
-     */
4901
-    public function has_field($fieldName)
4902
-    {
4903
-        $fieldSettings = $this->field_settings(true);
4904
-        if (isset($fieldSettings[ $fieldName ])) {
4905
-            return true;
4906
-        }
4907
-        return false;
4908
-    }
4909
-
4910
-
4911
-
4912
-    /**
4913
-     * Returns whether or not this model has a relation to the specified model
4914
-     *
4915
-     * @param string $relation_name possibly one of the keys in the relation_settings array
4916
-     * @return boolean
4917
-     */
4918
-    public function has_relation($relation_name)
4919
-    {
4920
-        $relations = $this->relation_settings();
4921
-        if (isset($relations[ $relation_name ])) {
4922
-            return true;
4923
-        }
4924
-        return false;
4925
-    }
4926
-
4927
-
4928
-
4929
-    /**
4930
-     * gets the field object of type 'primary_key' from the fieldsSettings attribute.
4931
-     * Eg, on EE_Answer that would be ANS_ID field object
4932
-     *
4933
-     * @param $field_obj
4934
-     * @return boolean
4935
-     */
4936
-    public function is_primary_key_field($field_obj)
4937
-    {
4938
-        return $field_obj instanceof EE_Primary_Key_Field_Base ? true : false;
4939
-    }
4940
-
4941
-
4942
-
4943
-    /**
4944
-     * gets the field object of type 'primary_key' from the fieldsSettings attribute.
4945
-     * Eg, on EE_Answer that would be ANS_ID field object
4946
-     *
4947
-     * @return EE_Model_Field_Base
4948
-     * @throws EE_Error
4949
-     */
4950
-    public function get_primary_key_field()
4951
-    {
4952
-        if ($this->_primary_key_field === null) {
4953
-            foreach ($this->field_settings(true) as $field_obj) {
4954
-                if ($this->is_primary_key_field($field_obj)) {
4955
-                    $this->_primary_key_field = $field_obj;
4956
-                    break;
4957
-                }
4958
-            }
4959
-            if (! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
4960
-                throw new EE_Error(sprintf(
4961
-                    __("There is no Primary Key defined on model %s", 'event_espresso'),
4962
-                    get_class($this)
4963
-                ));
4964
-            }
4965
-        }
4966
-        return $this->_primary_key_field;
4967
-    }
4968
-
4969
-
4970
-
4971
-    /**
4972
-     * Returns whether or not not there is a primary key on this model.
4973
-     * Internally does some caching.
4974
-     *
4975
-     * @return boolean
4976
-     */
4977
-    public function has_primary_key_field()
4978
-    {
4979
-        if ($this->_has_primary_key_field === null) {
4980
-            try {
4981
-                $this->get_primary_key_field();
4982
-                $this->_has_primary_key_field = true;
4983
-            } catch (EE_Error $e) {
4984
-                $this->_has_primary_key_field = false;
4985
-            }
4986
-        }
4987
-        return $this->_has_primary_key_field;
4988
-    }
4989
-
4990
-
4991
-
4992
-    /**
4993
-     * Finds the first field of type $field_class_name.
4994
-     *
4995
-     * @param string $field_class_name class name of field that you want to find. Eg, EE_Datetime_Field,
4996
-     *                                 EE_Foreign_Key_Field, etc
4997
-     * @return EE_Model_Field_Base or null if none is found
4998
-     */
4999
-    public function get_a_field_of_type($field_class_name)
5000
-    {
5001
-        foreach ($this->field_settings() as $field) {
5002
-            if ($field instanceof $field_class_name) {
5003
-                return $field;
5004
-            }
5005
-        }
5006
-        return null;
5007
-    }
5008
-
5009
-
5010
-
5011
-    /**
5012
-     * Gets a foreign key field pointing to model.
5013
-     *
5014
-     * @param string $model_name eg Event, Registration, not EEM_Event
5015
-     * @return EE_Foreign_Key_Field_Base
5016
-     * @throws EE_Error
5017
-     */
5018
-    public function get_foreign_key_to($model_name)
5019
-    {
5020
-        if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5021
-            foreach ($this->field_settings() as $field) {
5022
-                if (
5023
-                    $field instanceof EE_Foreign_Key_Field_Base
5024
-                    && in_array($model_name, $field->get_model_names_pointed_to())
5025
-                ) {
5026
-                    $this->_cache_foreign_key_to_fields[ $model_name ] = $field;
5027
-                    break;
5028
-                }
5029
-            }
5030
-            if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5031
-                throw new EE_Error(sprintf(__(
5032
-                    "There is no foreign key field pointing to model %s on model %s",
5033
-                    'event_espresso'
5034
-                ), $model_name, get_class($this)));
5035
-            }
5036
-        }
5037
-        return $this->_cache_foreign_key_to_fields[ $model_name ];
5038
-    }
5039
-
5040
-
5041
-
5042
-    /**
5043
-     * Gets the table name (including $wpdb->prefix) for the table alias
5044
-     *
5045
-     * @param string $table_alias eg Event, Event_Meta, Registration, Transaction, but maybe
5046
-     *                            a table alias with a model chain prefix, like 'Venue__Event_Venue___Event_Meta'.
5047
-     *                            Either one works
5048
-     * @return string
5049
-     */
5050
-    public function get_table_for_alias($table_alias)
5051
-    {
5052
-        $table_alias_sans_model_relation_chain_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($table_alias);
5053
-        return $this->_tables[ $table_alias_sans_model_relation_chain_prefix ]->get_table_name();
5054
-    }
5055
-
5056
-
5057
-
5058
-    /**
5059
-     * Returns a flat array of all field son this model, instead of organizing them
5060
-     * by table_alias as they are in the constructor.
5061
-     *
5062
-     * @param bool $include_db_only_fields flag indicating whether or not to include the db-only fields
5063
-     * @return EE_Model_Field_Base[] where the keys are the field's name
5064
-     */
5065
-    public function field_settings($include_db_only_fields = false)
5066
-    {
5067
-        if ($include_db_only_fields) {
5068
-            if ($this->_cached_fields === null) {
5069
-                $this->_cached_fields = array();
5070
-                foreach ($this->_fields as $fields_corresponding_to_table) {
5071
-                    foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5072
-                        $this->_cached_fields[ $field_name ] = $field_obj;
5073
-                    }
5074
-                }
5075
-            }
5076
-            return $this->_cached_fields;
5077
-        }
5078
-        if ($this->_cached_fields_non_db_only === null) {
5079
-            $this->_cached_fields_non_db_only = array();
5080
-            foreach ($this->_fields as $fields_corresponding_to_table) {
5081
-                foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5082
-                    /** @var $field_obj EE_Model_Field_Base */
5083
-                    if (! $field_obj->is_db_only_field()) {
5084
-                        $this->_cached_fields_non_db_only[ $field_name ] = $field_obj;
5085
-                    }
5086
-                }
5087
-            }
5088
-        }
5089
-        return $this->_cached_fields_non_db_only;
5090
-    }
5091
-
5092
-
5093
-
5094
-    /**
5095
-     *        cycle though array of attendees and create objects out of each item
5096
-     *
5097
-     * @access        private
5098
-     * @param        array $rows of results of $wpdb->get_results($query,ARRAY_A)
5099
-     * @return \EE_Base_Class[] array keys are primary keys (if there is a primary key on the model. if not,
5100
-     *                           numerically indexed)
5101
-     * @throws EE_Error
5102
-     */
5103
-    protected function _create_objects($rows = array())
5104
-    {
5105
-        $array_of_objects = array();
5106
-        if (empty($rows)) {
5107
-            return array();
5108
-        }
5109
-        $count_if_model_has_no_primary_key = 0;
5110
-        $has_primary_key = $this->has_primary_key_field();
5111
-        $primary_key_field = $has_primary_key ? $this->get_primary_key_field() : null;
5112
-        foreach ((array) $rows as $row) {
5113
-            if (empty($row)) {
5114
-                // wp did its weird thing where it returns an array like array(0=>null), which is totally not helpful...
5115
-                return array();
5116
-            }
5117
-            // check if we've already set this object in the results array,
5118
-            // in which case there's no need to process it further (again)
5119
-            if ($has_primary_key) {
5120
-                $table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5121
-                    $row,
5122
-                    $primary_key_field->get_qualified_column(),
5123
-                    $primary_key_field->get_table_column()
5124
-                );
5125
-                if ($table_pk_value && isset($array_of_objects[ $table_pk_value ])) {
5126
-                    continue;
5127
-                }
5128
-            }
5129
-            $classInstance = $this->instantiate_class_from_array_or_object($row);
5130
-            if (! $classInstance) {
5131
-                throw new EE_Error(
5132
-                    sprintf(
5133
-                        __('Could not create instance of class %s from row %s', 'event_espresso'),
5134
-                        $this->get_this_model_name(),
5135
-                        http_build_query($row)
5136
-                    )
5137
-                );
5138
-            }
5139
-            // set the timezone on the instantiated objects
5140
-            $classInstance->set_timezone($this->_timezone);
5141
-            // make sure if there is any timezone setting present that we set the timezone for the object
5142
-            $key = $has_primary_key ? $classInstance->ID() : $count_if_model_has_no_primary_key++;
5143
-            $array_of_objects[ $key ] = $classInstance;
5144
-            // also, for all the relations of type BelongsTo, see if we can cache
5145
-            // those related models
5146
-            // (we could do this for other relations too, but if there are conditions
5147
-            // that filtered out some fo the results, then we'd be caching an incomplete set
5148
-            // so it requires a little more thought than just caching them immediately...)
5149
-            foreach ($this->_model_relations as $modelName => $relation_obj) {
5150
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
5151
-                    // check if this model's INFO is present. If so, cache it on the model
5152
-                    $other_model = $relation_obj->get_other_model();
5153
-                    $other_model_obj_maybe = $other_model->instantiate_class_from_array_or_object($row);
5154
-                    // if we managed to make a model object from the results, cache it on the main model object
5155
-                    if ($other_model_obj_maybe) {
5156
-                        // set timezone on these other model objects if they are present
5157
-                        $other_model_obj_maybe->set_timezone($this->_timezone);
5158
-                        $classInstance->cache($modelName, $other_model_obj_maybe);
5159
-                    }
5160
-                }
5161
-            }
5162
-            // also, if this was a custom select query, let's see if there are any results for the custom select fields
5163
-            // and add them to the object as well.  We'll convert according to the set data_type if there's any set for
5164
-            // the field in the CustomSelects object
5165
-            if ($this->_custom_selections instanceof CustomSelects) {
5166
-                $classInstance->setCustomSelectsValues(
5167
-                    $this->getValuesForCustomSelectAliasesFromResults($row)
5168
-                );
5169
-            }
5170
-        }
5171
-        return $array_of_objects;
5172
-    }
5173
-
5174
-
5175
-    /**
5176
-     * This will parse a given row of results from the db and see if any keys in the results match an alias within the
5177
-     * current CustomSelects object. This will be used to build an array of values indexed by those keys.
5178
-     *
5179
-     * @param array $db_results_row
5180
-     * @return array
5181
-     */
5182
-    protected function getValuesForCustomSelectAliasesFromResults(array $db_results_row)
5183
-    {
5184
-        $results = array();
5185
-        if ($this->_custom_selections instanceof CustomSelects) {
5186
-            foreach ($this->_custom_selections->columnAliases() as $alias) {
5187
-                if (isset($db_results_row[ $alias ])) {
5188
-                    $results[ $alias ] = $this->convertValueToDataType(
5189
-                        $db_results_row[ $alias ],
5190
-                        $this->_custom_selections->getDataTypeForAlias($alias)
5191
-                    );
5192
-                }
5193
-            }
5194
-        }
5195
-        return $results;
5196
-    }
5197
-
5198
-
5199
-    /**
5200
-     * This will set the value for the given alias
5201
-     * @param string $value
5202
-     * @param string $datatype (one of %d, %s, %f)
5203
-     * @return int|string|float (int for %d, string for %s, float for %f)
5204
-     */
5205
-    protected function convertValueToDataType($value, $datatype)
5206
-    {
5207
-        switch ($datatype) {
5208
-            case '%f':
5209
-                return (float) $value;
5210
-            case '%d':
5211
-                return (int) $value;
5212
-            default:
5213
-                return (string) $value;
5214
-        }
5215
-    }
5216
-
5217
-
5218
-    /**
5219
-     * The purpose of this method is to allow us to create a model object that is not in the db that holds default
5220
-     * values. A typical example of where this is used is when creating a new item and the initial load of a form.  We
5221
-     * dont' necessarily want to test for if the object is present but just assume it is BUT load the defaults from the
5222
-     * object (as set in the model_field!).
5223
-     *
5224
-     * @return EE_Base_Class single EE_Base_Class object with default values for the properties.
5225
-     */
5226
-    public function create_default_object()
5227
-    {
5228
-        $this_model_fields_and_values = array();
5229
-        // setup the row using default values;
5230
-        foreach ($this->field_settings() as $field_name => $field_obj) {
5231
-            $this_model_fields_and_values[ $field_name ] = $field_obj->get_default_value();
5232
-        }
5233
-        $className = $this->_get_class_name();
5234
-        $classInstance = EE_Registry::instance()
5235
-                                    ->load_class($className, array($this_model_fields_and_values), false, false);
5236
-        return $classInstance;
5237
-    }
5238
-
5239
-
5240
-
5241
-    /**
5242
-     * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
5243
-     *                             or an stdClass where each property is the name of a column,
5244
-     * @return EE_Base_Class
5245
-     * @throws EE_Error
5246
-     */
5247
-    public function instantiate_class_from_array_or_object($cols_n_values)
5248
-    {
5249
-        if (! is_array($cols_n_values) && is_object($cols_n_values)) {
5250
-            $cols_n_values = get_object_vars($cols_n_values);
5251
-        }
5252
-        $primary_key = null;
5253
-        // make sure the array only has keys that are fields/columns on this model
5254
-        $this_model_fields_n_values = $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5255
-        if ($this->has_primary_key_field() && isset($this_model_fields_n_values[ $this->primary_key_name() ])) {
5256
-            $primary_key = $this_model_fields_n_values[ $this->primary_key_name() ];
5257
-        }
5258
-        $className = $this->_get_class_name();
5259
-        // check we actually found results that we can use to build our model object
5260
-        // if not, return null
5261
-        if ($this->has_primary_key_field()) {
5262
-            if (empty($this_model_fields_n_values[ $this->primary_key_name() ])) {
5263
-                return null;
5264
-            }
5265
-        } elseif ($this->unique_indexes()) {
5266
-            $first_column = reset($this_model_fields_n_values);
5267
-            if (empty($first_column)) {
5268
-                return null;
5269
-            }
5270
-        }
5271
-        // if there is no primary key or the object doesn't already exist in the entity map, then create a new instance
5272
-        if ($primary_key) {
5273
-            $classInstance = $this->get_from_entity_map($primary_key);
5274
-            if (! $classInstance) {
5275
-                $classInstance = EE_Registry::instance()
5276
-                                            ->load_class(
5277
-                                                $className,
5278
-                                                array($this_model_fields_n_values, $this->_timezone),
5279
-                                                true,
5280
-                                                false
5281
-                                            );
5282
-                // add this new object to the entity map
5283
-                $classInstance = $this->add_to_entity_map($classInstance);
5284
-            }
5285
-        } else {
5286
-            $classInstance = EE_Registry::instance()
5287
-                                        ->load_class(
5288
-                                            $className,
5289
-                                            array($this_model_fields_n_values, $this->_timezone),
5290
-                                            true,
5291
-                                            false
5292
-                                        );
5293
-        }
5294
-        return $classInstance;
5295
-    }
5296
-
5297
-
5298
-
5299
-    /**
5300
-     * Gets the model object from the  entity map if it exists
5301
-     *
5302
-     * @param int|string $id the ID of the model object
5303
-     * @return EE_Base_Class
5304
-     */
5305
-    public function get_from_entity_map($id)
5306
-    {
5307
-        return isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])
5308
-            ? $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] : null;
5309
-    }
5310
-
5311
-
5312
-
5313
-    /**
5314
-     * add_to_entity_map
5315
-     * Adds the object to the model's entity mappings
5316
-     *        Effectively tells the models "Hey, this model object is the most up-to-date representation of the data,
5317
-     *        and for the remainder of the request, it's even more up-to-date than what's in the database.
5318
-     *        So, if the database doesn't agree with what's in the entity mapper, ignore the database"
5319
-     *        If the database gets updated directly and you want the entity mapper to reflect that change,
5320
-     *        then this method should be called immediately after the update query
5321
-     * Note: The map is indexed by whatever the current blog id is set (via EEM_Base::$_model_query_blog_id).  This is
5322
-     * so on multisite, the entity map is specific to the query being done for a specific site.
5323
-     *
5324
-     * @param    EE_Base_Class $object
5325
-     * @throws EE_Error
5326
-     * @return \EE_Base_Class
5327
-     */
5328
-    public function add_to_entity_map(EE_Base_Class $object)
5329
-    {
5330
-        $className = $this->_get_class_name();
5331
-        if (! $object instanceof $className) {
5332
-            throw new EE_Error(sprintf(
5333
-                __("You tried adding a %s to a mapping of %ss", "event_espresso"),
5334
-                is_object($object) ? get_class($object) : $object,
5335
-                $className
5336
-            ));
5337
-        }
5338
-        /** @var $object EE_Base_Class */
5339
-        if (! $object->ID()) {
5340
-            throw new EE_Error(sprintf(__(
5341
-                "You tried storing a model object with NO ID in the %s entity mapper.",
5342
-                "event_espresso"
5343
-            ), get_class($this)));
5344
-        }
5345
-        // double check it's not already there
5346
-        $classInstance = $this->get_from_entity_map($object->ID());
5347
-        if ($classInstance) {
5348
-            return $classInstance;
5349
-        }
5350
-        $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $object->ID() ] = $object;
5351
-        return $object;
5352
-    }
5353
-
5354
-
5355
-
5356
-    /**
5357
-     * if a valid identifier is provided, then that entity is unset from the entity map,
5358
-     * if no identifier is provided, then the entire entity map is emptied
5359
-     *
5360
-     * @param int|string $id the ID of the model object
5361
-     * @return boolean
5362
-     */
5363
-    public function clear_entity_map($id = null)
5364
-    {
5365
-        if (empty($id)) {
5366
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ] = array();
5367
-            return true;
5368
-        }
5369
-        if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
5370
-            unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
5371
-            return true;
5372
-        }
5373
-        return false;
5374
-    }
5375
-
5376
-
5377
-
5378
-    /**
5379
-     * Public wrapper for _deduce_fields_n_values_from_cols_n_values.
5380
-     * Given an array where keys are column (or column alias) names and values,
5381
-     * returns an array of their corresponding field names and database values
5382
-     *
5383
-     * @param array $cols_n_values
5384
-     * @return array
5385
-     */
5386
-    public function deduce_fields_n_values_from_cols_n_values($cols_n_values)
5387
-    {
5388
-        return $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5389
-    }
5390
-
5391
-
5392
-
5393
-    /**
5394
-     * _deduce_fields_n_values_from_cols_n_values
5395
-     * Given an array where keys are column (or column alias) names and values,
5396
-     * returns an array of their corresponding field names and database values
5397
-     *
5398
-     * @param string $cols_n_values
5399
-     * @return array
5400
-     */
5401
-    protected function _deduce_fields_n_values_from_cols_n_values($cols_n_values)
5402
-    {
5403
-        $this_model_fields_n_values = array();
5404
-        foreach ($this->get_tables() as $table_alias => $table_obj) {
5405
-            $table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5406
-                $cols_n_values,
5407
-                $table_obj->get_fully_qualified_pk_column(),
5408
-                $table_obj->get_pk_column()
5409
-            );
5410
-            // there is a primary key on this table and its not set. Use defaults for all its columns
5411
-            if ($table_pk_value === null && $table_obj->get_pk_column()) {
5412
-                foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5413
-                    if (! $field_obj->is_db_only_field()) {
5414
-                        // prepare field as if its coming from db
5415
-                        $prepared_value = $field_obj->prepare_for_set($field_obj->get_default_value());
5416
-                        $this_model_fields_n_values[ $field_name ] = $field_obj->prepare_for_use_in_db($prepared_value);
5417
-                    }
5418
-                }
5419
-            } else {
5420
-                // the table's rows existed. Use their values
5421
-                foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5422
-                    if (! $field_obj->is_db_only_field()) {
5423
-                        $this_model_fields_n_values[ $field_name ] = $this->_get_column_value_with_table_alias_or_not(
5424
-                            $cols_n_values,
5425
-                            $field_obj->get_qualified_column(),
5426
-                            $field_obj->get_table_column()
5427
-                        );
5428
-                    }
5429
-                }
5430
-            }
5431
-        }
5432
-        return $this_model_fields_n_values;
5433
-    }
5434
-
5435
-
5436
-    /**
5437
-     * @param $cols_n_values
5438
-     * @param $qualified_column
5439
-     * @param $regular_column
5440
-     * @return null
5441
-     * @throws EE_Error
5442
-     * @throws ReflectionException
5443
-     */
5444
-    protected function _get_column_value_with_table_alias_or_not($cols_n_values, $qualified_column, $regular_column)
5445
-    {
5446
-        $value = null;
5447
-        // ask the field what it think it's table_name.column_name should be, and call it the "qualified column"
5448
-        // does the field on the model relate to this column retrieved from the db?
5449
-        // or is it a db-only field? (not relating to the model)
5450
-        if (isset($cols_n_values[ $qualified_column ])) {
5451
-            $value = $cols_n_values[ $qualified_column ];
5452
-        } elseif (isset($cols_n_values[ $regular_column ])) {
5453
-            $value = $cols_n_values[ $regular_column ];
5454
-        } elseif (! empty($this->foreign_key_aliases)) {
5455
-            // no PK?  ok check if there is a foreign key alias set for this table
5456
-            // then check if that alias exists in the incoming data
5457
-            // AND that the actual PK the $FK_alias represents matches the $qualified_column (full PK)
5458
-            foreach ($this->foreign_key_aliases as $FK_alias => $PK_column) {
5459
-                if ($PK_column === $qualified_column && isset($cols_n_values[ $FK_alias ])) {
5460
-                    $value = $cols_n_values[ $FK_alias ];
5461
-                    list($pk_class) = explode('.', $PK_column);
5462
-                    $pk_model_name = "EEM_{$pk_class}";
5463
-                    /** @var EEM_Base $pk_model */
5464
-                    $pk_model = EE_Registry::instance()->load_model($pk_model_name);
5465
-                    if ($pk_model instanceof EEM_Base) {
5466
-                        // make sure object is pulled from db and added to entity map
5467
-                        $pk_model->get_one_by_ID($value);
5468
-                    }
5469
-                    break;
5470
-                }
5471
-            }
5472
-        }
5473
-        return $value;
5474
-    }
5475
-
5476
-
5477
-
5478
-    /**
5479
-     * refresh_entity_map_from_db
5480
-     * Makes sure the model object in the entity map at $id assumes the values
5481
-     * of the database (opposite of EE_base_Class::save())
5482
-     *
5483
-     * @param int|string $id
5484
-     * @return EE_Base_Class
5485
-     * @throws EE_Error
5486
-     */
5487
-    public function refresh_entity_map_from_db($id)
5488
-    {
5489
-        $obj_in_map = $this->get_from_entity_map($id);
5490
-        if ($obj_in_map) {
5491
-            $wpdb_results = $this->_get_all_wpdb_results(
5492
-                array(array($this->get_primary_key_field()->get_name() => $id), 'limit' => 1)
5493
-            );
5494
-            if ($wpdb_results && is_array($wpdb_results)) {
5495
-                $one_row = reset($wpdb_results);
5496
-                foreach ($this->_deduce_fields_n_values_from_cols_n_values($one_row) as $field_name => $db_value) {
5497
-                    $obj_in_map->set_from_db($field_name, $db_value);
5498
-                }
5499
-                // clear the cache of related model objects
5500
-                foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5501
-                    $obj_in_map->clear_cache($relation_name, null, true);
5502
-                }
5503
-            }
5504
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] = $obj_in_map;
5505
-            return $obj_in_map;
5506
-        }
5507
-        return $this->get_one_by_ID($id);
5508
-    }
5509
-
5510
-
5511
-
5512
-    /**
5513
-     * refresh_entity_map_with
5514
-     * Leaves the entry in the entity map alone, but updates it to match the provided
5515
-     * $replacing_model_obj (which we assume to be its equivalent but somehow NOT in the entity map).
5516
-     * This is useful if you have a model object you want to make authoritative over what's in the entity map currently.
5517
-     * Note: The old $replacing_model_obj should now be destroyed as it's now un-authoritative
5518
-     *
5519
-     * @param int|string    $id
5520
-     * @param EE_Base_Class $replacing_model_obj
5521
-     * @return \EE_Base_Class
5522
-     * @throws EE_Error
5523
-     */
5524
-    public function refresh_entity_map_with($id, $replacing_model_obj)
5525
-    {
5526
-        $obj_in_map = $this->get_from_entity_map($id);
5527
-        if ($obj_in_map) {
5528
-            if ($replacing_model_obj instanceof EE_Base_Class) {
5529
-                foreach ($replacing_model_obj->model_field_array() as $field_name => $value) {
5530
-                    $obj_in_map->set($field_name, $value);
5531
-                }
5532
-                // make the model object in the entity map's cache match the $replacing_model_obj
5533
-                foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5534
-                    $obj_in_map->clear_cache($relation_name, null, true);
5535
-                    foreach ($replacing_model_obj->get_all_from_cache($relation_name) as $cache_id => $cached_obj) {
5536
-                        $obj_in_map->cache($relation_name, $cached_obj, $cache_id);
5537
-                    }
5538
-                }
5539
-            }
5540
-            return $obj_in_map;
5541
-        }
5542
-        $this->add_to_entity_map($replacing_model_obj);
5543
-        return $replacing_model_obj;
5544
-    }
5545
-
5546
-
5547
-
5548
-    /**
5549
-     * Gets the EE class that corresponds to this model. Eg, for EEM_Answer that
5550
-     * would be EE_Answer.To import that class, you'd just add ".class.php" to the name, like so
5551
-     * require_once($this->_getClassName().".class.php");
5552
-     *
5553
-     * @return string
5554
-     */
5555
-    private function _get_class_name()
5556
-    {
5557
-        return "EE_" . $this->get_this_model_name();
5558
-    }
5559
-
5560
-
5561
-
5562
-    /**
5563
-     * Get the name of the items this model represents, for the quantity specified. Eg,
5564
-     * if $quantity==1, on EEM_Event, it would 'Event' (internationalized), otherwise
5565
-     * it would be 'Events'.
5566
-     *
5567
-     * @param int $quantity
5568
-     * @return string
5569
-     */
5570
-    public function item_name($quantity = 1)
5571
-    {
5572
-        return (int) $quantity === 1 ? $this->singular_item : $this->plural_item;
5573
-    }
5574
-
5575
-
5576
-
5577
-    /**
5578
-     * Very handy general function to allow for plugins to extend any child of EE_TempBase.
5579
-     * If a method is called on a child of EE_TempBase that doesn't exist, this function is called
5580
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments. Instead of
5581
-     * requiring a plugin to extend the EE_TempBase (which works fine is there's only 1 plugin, but when will that
5582
-     * happen?) they can add a hook onto 'filters_hook_espresso__{className}__{methodName}' (eg,
5583
-     * filters_hook_espresso__EE_Answer__my_great_function) and accepts 2 arguments: the object on which the function
5584
-     * was called, and an array of the original arguments passed to the function. Whatever their callback function
5585
-     * returns will be returned by this function. Example: in functions.php (or in a plugin):
5586
-     * add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3); function
5587
-     * my_callback($previousReturnValue,EE_TempBase $object,$argsArray){
5588
-     * $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
5589
-     *        return $previousReturnValue.$returnString;
5590
-     * }
5591
-     * require('EEM_Answer.model.php');
5592
-     * $answer=EEM_Answer::instance();
5593
-     * echo $answer->my_callback('monkeys',100);
5594
-     * //will output "you called my_callback! and passed args:monkeys,100"
5595
-     *
5596
-     * @param string $methodName name of method which was called on a child of EE_TempBase, but which
5597
-     * @param array  $args       array of original arguments passed to the function
5598
-     * @throws EE_Error
5599
-     * @return mixed whatever the plugin which calls add_filter decides
5600
-     */
5601
-    public function __call($methodName, $args)
5602
-    {
5603
-        $className = get_class($this);
5604
-        $tagName = "FHEE__{$className}__{$methodName}";
5605
-        if (! has_filter($tagName)) {
5606
-            throw new EE_Error(
5607
-                sprintf(
5608
-                    __(
5609
-                        'Method %1$s on model %2$s does not exist! You can create one with the following code in functions.php or in a plugin: %4$s function my_callback(%4$s \$previousReturnValue, EEM_Base \$object\ $argsArray=NULL ){%4$s     /*function body*/%4$s      return \$whatever;%4$s }%4$s add_filter( \'%3$s\', \'my_callback\', 10, 3 );',
5610
-                        'event_espresso'
5611
-                    ),
5612
-                    $methodName,
5613
-                    $className,
5614
-                    $tagName,
5615
-                    '<br />'
5616
-                )
5617
-            );
5618
-        }
5619
-        return apply_filters($tagName, null, $this, $args);
5620
-    }
5621
-
5622
-
5623
-
5624
-    /**
5625
-     * Ensures $base_class_obj_or_id is of the EE_Base_Class child that corresponds ot this model.
5626
-     * If not, assumes its an ID, and uses $this->get_one_by_ID() to get the EE_Base_Class.
5627
-     *
5628
-     * @param EE_Base_Class|string|int $base_class_obj_or_id either:
5629
-     *                                                       the EE_Base_Class object that corresponds to this Model,
5630
-     *                                                       the object's class name
5631
-     *                                                       or object's ID
5632
-     * @param boolean                  $ensure_is_in_db      if set, we will also verify this model object
5633
-     *                                                       exists in the database. If it does not, we add it
5634
-     * @throws EE_Error
5635
-     * @return EE_Base_Class
5636
-     */
5637
-    public function ensure_is_obj($base_class_obj_or_id, $ensure_is_in_db = false)
5638
-    {
5639
-        $className = $this->_get_class_name();
5640
-        if ($base_class_obj_or_id instanceof $className) {
5641
-            $model_object = $base_class_obj_or_id;
5642
-        } else {
5643
-            $primary_key_field = $this->get_primary_key_field();
5644
-            if (
5645
-                $primary_key_field instanceof EE_Primary_Key_Int_Field
5646
-                && (
5647
-                    is_int($base_class_obj_or_id)
5648
-                    || is_string($base_class_obj_or_id)
5649
-                )
5650
-            ) {
5651
-                // assume it's an ID.
5652
-                // either a proper integer or a string representing an integer (eg "101" instead of 101)
5653
-                $model_object = $this->get_one_by_ID($base_class_obj_or_id);
5654
-            } elseif (
5655
-                $primary_key_field instanceof EE_Primary_Key_String_Field
5656
-                && is_string($base_class_obj_or_id)
5657
-            ) {
5658
-                // assume its a string representation of the object
5659
-                $model_object = $this->get_one_by_ID($base_class_obj_or_id);
5660
-            } else {
5661
-                throw new EE_Error(
5662
-                    sprintf(
5663
-                        __(
5664
-                            "'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5665
-                            'event_espresso'
5666
-                        ),
5667
-                        $base_class_obj_or_id,
5668
-                        $this->_get_class_name(),
5669
-                        print_r($base_class_obj_or_id, true)
5670
-                    )
5671
-                );
5672
-            }
5673
-        }
5674
-        if ($ensure_is_in_db && $model_object->ID() !== null) {
5675
-            $model_object->save();
5676
-        }
5677
-        return $model_object;
5678
-    }
5679
-
5680
-
5681
-
5682
-    /**
5683
-     * Similar to ensure_is_obj(), this method makes sure $base_class_obj_or_id
5684
-     * is a value of the this model's primary key. If it's an EE_Base_Class child,
5685
-     * returns it ID.
5686
-     *
5687
-     * @param EE_Base_Class|int|string $base_class_obj_or_id
5688
-     * @return int|string depending on the type of this model object's ID
5689
-     * @throws EE_Error
5690
-     */
5691
-    public function ensure_is_ID($base_class_obj_or_id)
5692
-    {
5693
-        $className = $this->_get_class_name();
5694
-        if ($base_class_obj_or_id instanceof $className) {
5695
-            /** @var $base_class_obj_or_id EE_Base_Class */
5696
-            $id = $base_class_obj_or_id->ID();
5697
-        } elseif (is_int($base_class_obj_or_id)) {
5698
-            // assume it's an ID
5699
-            $id = $base_class_obj_or_id;
5700
-        } elseif (is_string($base_class_obj_or_id)) {
5701
-            // assume its a string representation of the object
5702
-            $id = $base_class_obj_or_id;
5703
-        } else {
5704
-            throw new EE_Error(sprintf(
5705
-                __(
5706
-                    "'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5707
-                    'event_espresso'
5708
-                ),
5709
-                $base_class_obj_or_id,
5710
-                $this->_get_class_name(),
5711
-                print_r($base_class_obj_or_id, true)
5712
-            ));
5713
-        }
5714
-        return $id;
5715
-    }
5716
-
5717
-
5718
-
5719
-    /**
5720
-     * Sets whether the values passed to the model (eg, values in WHERE, values in INSERT, UPDATE, etc)
5721
-     * have already been ran through the appropriate model field's prepare_for_use_in_db method. IE, they have
5722
-     * been sanitized and converted into the appropriate domain.
5723
-     * Usually the only place you'll want to change the default (which is to assume values have NOT been sanitized by
5724
-     * the model object/model field) is when making a method call from WITHIN a model object, which has direct access
5725
-     * to its sanitized values. Note: after changing this setting, you should set it back to its previous value (using
5726
-     * get_assumption_concerning_values_already_prepared_by_model_object()) eg.
5727
-     * $EVT = EEM_Event::instance(); $old_setting =
5728
-     * $EVT->get_assumption_concerning_values_already_prepared_by_model_object();
5729
-     * $EVT->assume_values_already_prepared_by_model_object(true);
5730
-     * $EVT->update(array('foo'=>'bar'),array(array('foo'=>'monkey')));
5731
-     * $EVT->assume_values_already_prepared_by_model_object($old_setting);
5732
-     *
5733
-     * @param int $values_already_prepared like one of the constants on EEM_Base
5734
-     * @return void
5735
-     */
5736
-    public function assume_values_already_prepared_by_model_object(
5737
-        $values_already_prepared = self::not_prepared_by_model_object
5738
-    ) {
5739
-        $this->_values_already_prepared_by_model_object = $values_already_prepared;
5740
-    }
5741
-
5742
-
5743
-
5744
-    /**
5745
-     * Read comments for assume_values_already_prepared_by_model_object()
5746
-     *
5747
-     * @return int
5748
-     */
5749
-    public function get_assumption_concerning_values_already_prepared_by_model_object()
5750
-    {
5751
-        return $this->_values_already_prepared_by_model_object;
5752
-    }
5753
-
5754
-
5755
-
5756
-    /**
5757
-     * Gets all the indexes on this model
5758
-     *
5759
-     * @return EE_Index[]
5760
-     */
5761
-    public function indexes()
5762
-    {
5763
-        return $this->_indexes;
5764
-    }
5765
-
5766
-
5767
-
5768
-    /**
5769
-     * Gets all the Unique Indexes on this model
5770
-     *
5771
-     * @return EE_Unique_Index[]
5772
-     */
5773
-    public function unique_indexes()
5774
-    {
5775
-        $unique_indexes = array();
5776
-        foreach ($this->_indexes as $name => $index) {
5777
-            if ($index instanceof EE_Unique_Index) {
5778
-                $unique_indexes [ $name ] = $index;
5779
-            }
5780
-        }
5781
-        return $unique_indexes;
5782
-    }
5783
-
5784
-
5785
-
5786
-    /**
5787
-     * Gets all the fields which, when combined, make the primary key.
5788
-     * This is usually just an array with 1 element (the primary key), but in cases
5789
-     * where there is no primary key, it's a combination of fields as defined
5790
-     * on a primary index
5791
-     *
5792
-     * @return EE_Model_Field_Base[] indexed by the field's name
5793
-     * @throws EE_Error
5794
-     */
5795
-    public function get_combined_primary_key_fields()
5796
-    {
5797
-        foreach ($this->indexes() as $index) {
5798
-            if ($index instanceof EE_Primary_Key_Index) {
5799
-                return $index->fields();
5800
-            }
5801
-        }
5802
-        return array($this->primary_key_name() => $this->get_primary_key_field());
5803
-    }
5804
-
5805
-
5806
-
5807
-    /**
5808
-     * Used to build a primary key string (when the model has no primary key),
5809
-     * which can be used a unique string to identify this model object.
5810
-     *
5811
-     * @param array $fields_n_values keys are field names, values are their values.
5812
-     *                               Note: if you have results from `EEM_Base::get_all_wpdb_results()`, you need to
5813
-     *                               run it through `EEM_Base::deduce_fields_n_values_from_cols_n_values()`
5814
-     *                               before passing it to this function (that will convert it from columns-n-values
5815
-     *                               to field-names-n-values).
5816
-     * @return string
5817
-     * @throws EE_Error
5818
-     */
5819
-    public function get_index_primary_key_string($fields_n_values)
5820
-    {
5821
-        $cols_n_values_for_primary_key_index = array_intersect_key(
5822
-            $fields_n_values,
5823
-            $this->get_combined_primary_key_fields()
5824
-        );
5825
-        return http_build_query($cols_n_values_for_primary_key_index);
5826
-    }
5827
-
5828
-
5829
-
5830
-    /**
5831
-     * Gets the field values from the primary key string
5832
-     *
5833
-     * @see EEM_Base::get_combined_primary_key_fields() and EEM_Base::get_index_primary_key_string()
5834
-     * @param string $index_primary_key_string
5835
-     * @return null|array
5836
-     * @throws EE_Error
5837
-     */
5838
-    public function parse_index_primary_key_string($index_primary_key_string)
5839
-    {
5840
-        $key_fields = $this->get_combined_primary_key_fields();
5841
-        // check all of them are in the $id
5842
-        $key_vals_in_combined_pk = array();
5843
-        parse_str($index_primary_key_string, $key_vals_in_combined_pk);
5844
-        foreach ($key_fields as $key_field_name => $field_obj) {
5845
-            if (! isset($key_vals_in_combined_pk[ $key_field_name ])) {
5846
-                return null;
5847
-            }
5848
-        }
5849
-        return $key_vals_in_combined_pk;
5850
-    }
5851
-
5852
-
5853
-
5854
-    /**
5855
-     * verifies that an array of key-value pairs for model fields has a key
5856
-     * for each field comprising the primary key index
5857
-     *
5858
-     * @param array $key_vals
5859
-     * @return boolean
5860
-     * @throws EE_Error
5861
-     */
5862
-    public function has_all_combined_primary_key_fields($key_vals)
5863
-    {
5864
-        $keys_it_should_have = array_keys($this->get_combined_primary_key_fields());
5865
-        foreach ($keys_it_should_have as $key) {
5866
-            if (! isset($key_vals[ $key ])) {
5867
-                return false;
5868
-            }
5869
-        }
5870
-        return true;
5871
-    }
5872
-
5873
-
5874
-
5875
-    /**
5876
-     * Finds all model objects in the DB that appear to be a copy of $model_object_or_attributes_array.
5877
-     * We consider something to be a copy if all the attributes match (except the ID, of course).
5878
-     *
5879
-     * @param array|EE_Base_Class $model_object_or_attributes_array If its an array, it's field-value pairs
5880
-     * @param array               $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
5881
-     * @throws EE_Error
5882
-     * @return \EE_Base_Class[] Array keys are object IDs (if there is a primary key on the model. if not, numerically
5883
-     *                                                              indexed)
5884
-     */
5885
-    public function get_all_copies($model_object_or_attributes_array, $query_params = array())
5886
-    {
5887
-        if ($model_object_or_attributes_array instanceof EE_Base_Class) {
5888
-            $attributes_array = $model_object_or_attributes_array->model_field_array();
5889
-        } elseif (is_array($model_object_or_attributes_array)) {
5890
-            $attributes_array = $model_object_or_attributes_array;
5891
-        } else {
5892
-            throw new EE_Error(sprintf(__(
5893
-                "get_all_copies should be provided with either a model object or an array of field-value-pairs, but was given %s",
5894
-                "event_espresso"
5895
-            ), $model_object_or_attributes_array));
5896
-        }
5897
-        // even copies obviously won't have the same ID, so remove the primary key
5898
-        // from the WHERE conditions for finding copies (if there is a primary key, of course)
5899
-        if ($this->has_primary_key_field() && isset($attributes_array[ $this->primary_key_name() ])) {
5900
-            unset($attributes_array[ $this->primary_key_name() ]);
5901
-        }
5902
-        if (isset($query_params[0])) {
5903
-            $query_params[0] = array_merge($attributes_array, $query_params);
5904
-        } else {
5905
-            $query_params[0] = $attributes_array;
5906
-        }
5907
-        return $this->get_all($query_params);
5908
-    }
5909
-
5910
-
5911
-
5912
-    /**
5913
-     * Gets the first copy we find. See get_all_copies for more details
5914
-     *
5915
-     * @param       mixed EE_Base_Class | array        $model_object_or_attributes_array
5916
-     * @param array $query_params
5917
-     * @return EE_Base_Class
5918
-     * @throws EE_Error
5919
-     */
5920
-    public function get_one_copy($model_object_or_attributes_array, $query_params = array())
5921
-    {
5922
-        if (! is_array($query_params)) {
5923
-            EE_Error::doing_it_wrong(
5924
-                'EEM_Base::get_one_copy',
5925
-                sprintf(
5926
-                    __('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
5927
-                    gettype($query_params)
5928
-                ),
5929
-                '4.6.0'
5930
-            );
5931
-            $query_params = array();
5932
-        }
5933
-        $query_params['limit'] = 1;
5934
-        $copies = $this->get_all_copies($model_object_or_attributes_array, $query_params);
5935
-        if (is_array($copies)) {
5936
-            return array_shift($copies);
5937
-        }
5938
-        return null;
5939
-    }
5940
-
5941
-
5942
-
5943
-    /**
5944
-     * Updates the item with the specified id. Ignores default query parameters because
5945
-     * we have specified the ID, and its assumed we KNOW what we're doing
5946
-     *
5947
-     * @param array      $fields_n_values keys are field names, values are their new values
5948
-     * @param int|string $id              the value of the primary key to update
5949
-     * @return int number of rows updated
5950
-     * @throws EE_Error
5951
-     */
5952
-    public function update_by_ID($fields_n_values, $id)
5953
-    {
5954
-        $query_params = array(
5955
-            0                          => array($this->get_primary_key_field()->get_name() => $id),
5956
-            'default_where_conditions' => EEM_Base::default_where_conditions_others_only,
5957
-        );
5958
-        return $this->update($fields_n_values, $query_params);
5959
-    }
5960
-
5961
-
5962
-
5963
-    /**
5964
-     * Changes an operator which was supplied to the models into one usable in SQL
5965
-     *
5966
-     * @param string $operator_supplied
5967
-     * @return string an operator which can be used in SQL
5968
-     * @throws EE_Error
5969
-     */
5970
-    private function _prepare_operator_for_sql($operator_supplied)
5971
-    {
5972
-        $sql_operator = isset($this->_valid_operators[ $operator_supplied ]) ? $this->_valid_operators[ $operator_supplied ]
5973
-            : null;
5974
-        if ($sql_operator) {
5975
-            return $sql_operator;
5976
-        }
5977
-        throw new EE_Error(
5978
-            sprintf(
5979
-                __(
5980
-                    "The operator '%s' is not in the list of valid operators: %s",
5981
-                    "event_espresso"
5982
-                ),
5983
-                $operator_supplied,
5984
-                implode(",", array_keys($this->_valid_operators))
5985
-            )
5986
-        );
5987
-    }
5988
-
5989
-
5990
-
5991
-    /**
5992
-     * Gets the valid operators
5993
-     * @return array keys are accepted strings, values are the SQL they are converted to
5994
-     */
5995
-    public function valid_operators()
5996
-    {
5997
-        return $this->_valid_operators;
5998
-    }
5999
-
6000
-
6001
-
6002
-    /**
6003
-     * Gets the between-style operators (take 2 arguments).
6004
-     * @return array keys are accepted strings, values are the SQL they are converted to
6005
-     */
6006
-    public function valid_between_style_operators()
6007
-    {
6008
-        return array_intersect(
6009
-            $this->valid_operators(),
6010
-            $this->_between_style_operators
6011
-        );
6012
-    }
6013
-
6014
-    /**
6015
-     * Gets the "like"-style operators (take a single argument, but it may contain wildcards)
6016
-     * @return array keys are accepted strings, values are the SQL they are converted to
6017
-     */
6018
-    public function valid_like_style_operators()
6019
-    {
6020
-        return array_intersect(
6021
-            $this->valid_operators(),
6022
-            $this->_like_style_operators
6023
-        );
6024
-    }
6025
-
6026
-    /**
6027
-     * Gets the "in"-style operators
6028
-     * @return array keys are accepted strings, values are the SQL they are converted to
6029
-     */
6030
-    public function valid_in_style_operators()
6031
-    {
6032
-        return array_intersect(
6033
-            $this->valid_operators(),
6034
-            $this->_in_style_operators
6035
-        );
6036
-    }
6037
-
6038
-    /**
6039
-     * Gets the "null"-style operators (accept no arguments)
6040
-     * @return array keys are accepted strings, values are the SQL they are converted to
6041
-     */
6042
-    public function valid_null_style_operators()
6043
-    {
6044
-        return array_intersect(
6045
-            $this->valid_operators(),
6046
-            $this->_null_style_operators
6047
-        );
6048
-    }
6049
-
6050
-    /**
6051
-     * Gets an array where keys are the primary keys and values are their 'names'
6052
-     * (as determined by the model object's name() function, which is often overridden)
6053
-     *
6054
-     * @param array $query_params like get_all's
6055
-     * @return string[]
6056
-     * @throws EE_Error
6057
-     */
6058
-    public function get_all_names($query_params = array())
6059
-    {
6060
-        $objs = $this->get_all($query_params);
6061
-        $names = array();
6062
-        foreach ($objs as $obj) {
6063
-            $names[ $obj->ID() ] = $obj->name();
6064
-        }
6065
-        return $names;
6066
-    }
6067
-
6068
-
6069
-
6070
-    /**
6071
-     * Gets an array of primary keys from the model objects. If you acquired the model objects
6072
-     * using EEM_Base::get_all() you don't need to call this (and probably shouldn't because
6073
-     * this is duplicated effort and reduces efficiency) you would be better to use
6074
-     * array_keys() on $model_objects.
6075
-     *
6076
-     * @param \EE_Base_Class[] $model_objects
6077
-     * @param boolean          $filter_out_empty_ids if a model object has an ID of '' or 0, don't bother including it
6078
-     *                                               in the returned array
6079
-     * @return array
6080
-     * @throws EE_Error
6081
-     */
6082
-    public function get_IDs($model_objects, $filter_out_empty_ids = false)
6083
-    {
6084
-        if (! $this->has_primary_key_field()) {
6085
-            if (WP_DEBUG) {
6086
-                EE_Error::add_error(
6087
-                    __('Trying to get IDs from a model than has no primary key', 'event_espresso'),
6088
-                    __FILE__,
6089
-                    __FUNCTION__,
6090
-                    __LINE__
6091
-                );
6092
-            }
6093
-        }
6094
-        $IDs = array();
6095
-        foreach ($model_objects as $model_object) {
6096
-            $id = $model_object->ID();
6097
-            if (! $id) {
6098
-                if ($filter_out_empty_ids) {
6099
-                    continue;
6100
-                }
6101
-                if (WP_DEBUG) {
6102
-                    EE_Error::add_error(
6103
-                        __(
6104
-                            'Called %1$s on a model object that has no ID and so probably hasn\'t been saved to the database',
6105
-                            'event_espresso'
6106
-                        ),
6107
-                        __FILE__,
6108
-                        __FUNCTION__,
6109
-                        __LINE__
6110
-                    );
6111
-                }
6112
-            }
6113
-            $IDs[] = $id;
6114
-        }
6115
-        return $IDs;
6116
-    }
6117
-
6118
-
6119
-
6120
-    /**
6121
-     * Returns the string used in capabilities relating to this model. If there
6122
-     * are no capabilities that relate to this model returns false
6123
-     *
6124
-     * @return string|false
6125
-     */
6126
-    public function cap_slug()
6127
-    {
6128
-        return apply_filters('FHEE__EEM_Base__cap_slug', $this->_caps_slug, $this);
6129
-    }
6130
-
6131
-
6132
-
6133
-    /**
6134
-     * Returns the capability-restrictions array (@see EEM_Base::_cap_restrictions).
6135
-     * If $context is provided (which should be set to one of EEM_Base::valid_cap_contexts())
6136
-     * only returns the cap restrictions array in that context (ie, the array
6137
-     * at that key)
6138
-     *
6139
-     * @param string $context
6140
-     * @return EE_Default_Where_Conditions[] indexed by associated capability
6141
-     * @throws EE_Error
6142
-     */
6143
-    public function cap_restrictions($context = EEM_Base::caps_read)
6144
-    {
6145
-        EEM_Base::verify_is_valid_cap_context($context);
6146
-        // check if we ought to run the restriction generator first
6147
-        if (
6148
-            isset($this->_cap_restriction_generators[ $context ])
6149
-            && $this->_cap_restriction_generators[ $context ] instanceof EE_Restriction_Generator_Base
6150
-            && ! $this->_cap_restriction_generators[ $context ]->has_generated_cap_restrictions()
6151
-        ) {
6152
-            $this->_cap_restrictions[ $context ] = array_merge(
6153
-                $this->_cap_restrictions[ $context ],
6154
-                $this->_cap_restriction_generators[ $context ]->generate_restrictions()
6155
-            );
6156
-        }
6157
-        // and make sure we've finalized the construction of each restriction
6158
-        foreach ($this->_cap_restrictions[ $context ] as $where_conditions_obj) {
6159
-            if ($where_conditions_obj instanceof EE_Default_Where_Conditions) {
6160
-                $where_conditions_obj->_finalize_construct($this);
6161
-            }
6162
-        }
6163
-        return $this->_cap_restrictions[ $context ];
6164
-    }
6165
-
6166
-
6167
-
6168
-    /**
6169
-     * Indicating whether or not this model thinks its a wp core model
6170
-     *
6171
-     * @return boolean
6172
-     */
6173
-    public function is_wp_core_model()
6174
-    {
6175
-        return $this->_wp_core_model;
6176
-    }
6177
-
6178
-
6179
-
6180
-    /**
6181
-     * Gets all the caps that are missing which impose a restriction on
6182
-     * queries made in this context
6183
-     *
6184
-     * @param string $context one of EEM_Base::caps_ constants
6185
-     * @return EE_Default_Where_Conditions[] indexed by capability name
6186
-     * @throws EE_Error
6187
-     */
6188
-    public function caps_missing($context = EEM_Base::caps_read)
6189
-    {
6190
-        $missing_caps = array();
6191
-        $cap_restrictions = $this->cap_restrictions($context);
6192
-        foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
6193
-            if (
6194
-                ! EE_Capabilities::instance()
6195
-                                 ->current_user_can($cap, $this->get_this_model_name() . '_model_applying_caps')
6196
-            ) {
6197
-                $missing_caps[ $cap ] = $restriction_if_no_cap;
6198
-            }
6199
-        }
6200
-        return $missing_caps;
6201
-    }
6202
-
6203
-
6204
-
6205
-    /**
6206
-     * Gets the mapping from capability contexts to action strings used in capability names
6207
-     *
6208
-     * @return array keys are one of EEM_Base::valid_cap_contexts(), and values are usually
6209
-     * one of 'read', 'edit', or 'delete'
6210
-     */
6211
-    public function cap_contexts_to_cap_action_map()
6212
-    {
6213
-        return apply_filters(
6214
-            'FHEE__EEM_Base__cap_contexts_to_cap_action_map',
6215
-            $this->_cap_contexts_to_cap_action_map,
6216
-            $this
6217
-        );
6218
-    }
6219
-
6220
-
6221
-
6222
-    /**
6223
-     * Gets the action string for the specified capability context
6224
-     *
6225
-     * @param string $context
6226
-     * @return string one of EEM_Base::cap_contexts_to_cap_action_map() values
6227
-     * @throws EE_Error
6228
-     */
6229
-    public function cap_action_for_context($context)
6230
-    {
6231
-        $mapping = $this->cap_contexts_to_cap_action_map();
6232
-        if (isset($mapping[ $context ])) {
6233
-            return $mapping[ $context ];
6234
-        }
6235
-        if ($action = apply_filters('FHEE__EEM_Base__cap_action_for_context', null, $this, $mapping, $context)) {
6236
-            return $action;
6237
-        }
6238
-        throw new EE_Error(
6239
-            sprintf(
6240
-                __('Cannot find capability restrictions for context "%1$s", allowed values are:%2$s', 'event_espresso'),
6241
-                $context,
6242
-                implode(',', array_keys($this->cap_contexts_to_cap_action_map()))
6243
-            )
6244
-        );
6245
-    }
6246
-
6247
-
6248
-
6249
-    /**
6250
-     * Returns all the capability contexts which are valid when querying models
6251
-     *
6252
-     * @return array
6253
-     */
6254
-    public static function valid_cap_contexts()
6255
-    {
6256
-        return apply_filters('FHEE__EEM_Base__valid_cap_contexts', array(
6257
-            self::caps_read,
6258
-            self::caps_read_admin,
6259
-            self::caps_edit,
6260
-            self::caps_delete,
6261
-        ));
6262
-    }
6263
-
6264
-
6265
-
6266
-    /**
6267
-     * Returns all valid options for 'default_where_conditions'
6268
-     *
6269
-     * @return array
6270
-     */
6271
-    public static function valid_default_where_conditions()
6272
-    {
6273
-        return array(
6274
-            EEM_Base::default_where_conditions_all,
6275
-            EEM_Base::default_where_conditions_this_only,
6276
-            EEM_Base::default_where_conditions_others_only,
6277
-            EEM_Base::default_where_conditions_minimum_all,
6278
-            EEM_Base::default_where_conditions_minimum_others,
6279
-            EEM_Base::default_where_conditions_none
6280
-        );
6281
-    }
6282
-
6283
-    // public static function default_where_conditions_full
6284
-    /**
6285
-     * Verifies $context is one of EEM_Base::valid_cap_contexts(), if not it throws an exception
6286
-     *
6287
-     * @param string $context
6288
-     * @return bool
6289
-     * @throws EE_Error
6290
-     */
6291
-    public static function verify_is_valid_cap_context($context)
6292
-    {
6293
-        $valid_cap_contexts = EEM_Base::valid_cap_contexts();
6294
-        if (in_array($context, $valid_cap_contexts)) {
6295
-            return true;
6296
-        }
6297
-        throw new EE_Error(
6298
-            sprintf(
6299
-                __(
6300
-                    'Context "%1$s" passed into model "%2$s" is not a valid context. They are: %3$s',
6301
-                    'event_espresso'
6302
-                ),
6303
-                $context,
6304
-                'EEM_Base',
6305
-                implode(',', $valid_cap_contexts)
6306
-            )
6307
-        );
6308
-    }
6309
-
6310
-
6311
-
6312
-    /**
6313
-     * Clears all the models field caches. This is only useful when a sub-class
6314
-     * might have added a field or something and these caches might be invalidated
6315
-     */
6316
-    protected function _invalidate_field_caches()
6317
-    {
6318
-        $this->_cache_foreign_key_to_fields = array();
6319
-        $this->_cached_fields = null;
6320
-        $this->_cached_fields_non_db_only = null;
6321
-    }
6322
-
6323
-
6324
-
6325
-    /**
6326
-     * Gets the list of all the where query param keys that relate to logic instead of field names
6327
-     * (eg "and", "or", "not").
6328
-     *
6329
-     * @return array
6330
-     */
6331
-    public function logic_query_param_keys()
6332
-    {
6333
-        return $this->_logic_query_param_keys;
6334
-    }
6335
-
6336
-
6337
-
6338
-    /**
6339
-     * Determines whether or not the where query param array key is for a logic query param.
6340
-     * Eg 'OR', 'not*', and 'and*because-i-say-so' should all return true, whereas
6341
-     * 'ATT_fname', 'EVT_name*not-you-or-me', and 'ORG_name' should return false
6342
-     *
6343
-     * @param $query_param_key
6344
-     * @return bool
6345
-     */
6346
-    public function is_logic_query_param_key($query_param_key)
6347
-    {
6348
-        foreach ($this->logic_query_param_keys() as $logic_query_param_key) {
6349
-            if (
6350
-                $query_param_key === $logic_query_param_key
6351
-                || strpos($query_param_key, $logic_query_param_key . '*') === 0
6352
-            ) {
6353
-                return true;
6354
-            }
6355
-        }
6356
-        return false;
6357
-    }
6358
-
6359
-    /**
6360
-     * Returns true if this model has a password field on it (regardless of whether that password field has any content)
6361
-     * @since 4.9.74.p
6362
-     * @return boolean
6363
-     */
6364
-    public function hasPassword()
6365
-    {
6366
-        // if we don't yet know if there's a password field, find out and remember it for next time.
6367
-        if ($this->has_password_field === null) {
6368
-            $password_field = $this->getPasswordField();
6369
-            $this->has_password_field = $password_field instanceof EE_Password_Field ? true : false;
6370
-        }
6371
-        return $this->has_password_field;
6372
-    }
6373
-
6374
-    /**
6375
-     * Returns the password field on this model, if there is one
6376
-     * @since 4.9.74.p
6377
-     * @return EE_Password_Field|null
6378
-     */
6379
-    public function getPasswordField()
6380
-    {
6381
-        // if we definetely already know there is a password field or not (because has_password_field is true or false)
6382
-        // there's no need to search for it. If we don't know yet, then find out
6383
-        if ($this->has_password_field === null && $this->password_field === null) {
6384
-            $this->password_field = $this->get_a_field_of_type('EE_Password_Field');
6385
-        }
6386
-        // don't bother setting has_password_field because that's hasPassword()'s job.
6387
-        return $this->password_field;
6388
-    }
6389
-
6390
-
6391
-    /**
6392
-     * Returns the list of field (as EE_Model_Field_Bases) that are protected by the password
6393
-     * @since 4.9.74.p
6394
-     * @return EE_Model_Field_Base[]
6395
-     * @throws EE_Error
6396
-     */
6397
-    public function getPasswordProtectedFields()
6398
-    {
6399
-        $password_field = $this->getPasswordField();
6400
-        $fields = array();
6401
-        if ($password_field instanceof EE_Password_Field) {
6402
-            $field_names = $password_field->protectedFields();
6403
-            foreach ($field_names as $field_name) {
6404
-                $fields[ $field_name ] = $this->field_settings_for($field_name);
6405
-            }
6406
-        }
6407
-        return $fields;
6408
-    }
6409
-
6410
-
6411
-    /**
6412
-     * Checks if the current user can perform the requested action on this model
6413
-     * @since 4.9.74.p
6414
-     * @param string $cap_to_check one of the array keys from _cap_contexts_to_cap_action_map
6415
-     * @param EE_Base_Class|array $model_obj_or_fields_n_values
6416
-     * @return bool
6417
-     * @throws EE_Error
6418
-     * @throws InvalidArgumentException
6419
-     * @throws InvalidDataTypeException
6420
-     * @throws InvalidInterfaceException
6421
-     * @throws ReflectionException
6422
-     * @throws UnexpectedEntityException
6423
-     */
6424
-    public function currentUserCan($cap_to_check, $model_obj_or_fields_n_values)
6425
-    {
6426
-        if ($model_obj_or_fields_n_values instanceof EE_Base_Class) {
6427
-            $model_obj_or_fields_n_values = $model_obj_or_fields_n_values->model_field_array();
6428
-        }
6429
-        if (!is_array($model_obj_or_fields_n_values)) {
6430
-            throw new UnexpectedEntityException(
6431
-                $model_obj_or_fields_n_values,
6432
-                'EE_Base_Class',
6433
-                sprintf(
6434
-                    esc_html__('%1$s must be passed an `EE_Base_Class or an array of fields names with their values. You passed in something different.', 'event_espresso'),
6435
-                    __FUNCTION__
6436
-                )
6437
-            );
6438
-        }
6439
-        return $this->exists(
6440
-            $this->alter_query_params_to_restrict_by_ID(
6441
-                $this->get_index_primary_key_string($model_obj_or_fields_n_values),
6442
-                array(
6443
-                    'default_where_conditions' => 'none',
6444
-                    'caps'                     => $cap_to_check,
6445
-                )
6446
-            )
6447
-        );
6448
-    }
6449
-
6450
-    /**
6451
-     * Returns the query param where conditions key to the password affecting this model.
6452
-     * Eg on EEM_Event this would just be "password", on EEM_Datetime this would be "Event.password", etc.
6453
-     * @since 4.9.74.p
6454
-     * @return null|string
6455
-     * @throws EE_Error
6456
-     * @throws InvalidArgumentException
6457
-     * @throws InvalidDataTypeException
6458
-     * @throws InvalidInterfaceException
6459
-     * @throws ModelConfigurationException
6460
-     * @throws ReflectionException
6461
-     */
6462
-    public function modelChainAndPassword()
6463
-    {
6464
-        if ($this->model_chain_to_password === null) {
6465
-            throw new ModelConfigurationException(
6466
-                $this,
6467
-                esc_html_x(
6468
-                // @codingStandardsIgnoreStart
6469
-                    'Cannot exclude protected data because the model has not specified which model has the password.',
6470
-                    // @codingStandardsIgnoreEnd
6471
-                    '1: model name',
6472
-                    'event_espresso'
6473
-                )
6474
-            );
6475
-        }
6476
-        if ($this->model_chain_to_password === '') {
6477
-            $model_with_password = $this;
6478
-        } else {
6479
-            if ($pos_of_period = strrpos($this->model_chain_to_password, '.')) {
6480
-                $last_model_in_chain = substr($this->model_chain_to_password, $pos_of_period + 1);
6481
-            } else {
6482
-                $last_model_in_chain = $this->model_chain_to_password;
6483
-            }
6484
-            $model_with_password = EE_Registry::instance()->load_model($last_model_in_chain);
6485
-        }
6486
-
6487
-        $password_field = $model_with_password->getPasswordField();
6488
-        if ($password_field instanceof EE_Password_Field) {
6489
-            $password_field_name = $password_field->get_name();
6490
-        } else {
6491
-            throw new ModelConfigurationException(
6492
-                $this,
6493
-                sprintf(
6494
-                    esc_html_x(
6495
-                        'This model claims related model "%1$s" should have a password field on it, but none was found. The model relation chain is "%2$s"',
6496
-                        '1: model name, 2: special string',
6497
-                        'event_espresso'
6498
-                    ),
6499
-                    $model_with_password->get_this_model_name(),
6500
-                    $this->model_chain_to_password
6501
-                )
6502
-            );
6503
-        }
6504
-        return ($this->model_chain_to_password ? $this->model_chain_to_password . '.' : '') . $password_field_name;
6505
-    }
6506
-
6507
-    /**
6508
-     * Returns true if there is a password on a related model which restricts access to some of this model's rows,
6509
-     * or if this model itself has a password affecting access to some of its other fields.
6510
-     * @since 4.9.74.p
6511
-     * @return boolean
6512
-     */
6513
-    public function restrictedByRelatedModelPassword()
6514
-    {
6515
-        return $this->model_chain_to_password !== null;
6516
-    }
3814
+		}
3815
+		return $null_friendly_where_conditions;
3816
+	}
3817
+
3818
+
3819
+
3820
+	/**
3821
+	 * Uses the _default_where_conditions_strategy set during __construct() to get
3822
+	 * default where conditions on all get_all, update, and delete queries done by this model.
3823
+	 * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3824
+	 * NOT array('Event_CPT.post_type'=>'esp_event').
3825
+	 *
3826
+	 * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3827
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3828
+	 */
3829
+	private function _get_default_where_conditions($model_relation_path = '')
3830
+	{
3831
+		if ($this->_ignore_where_strategy) {
3832
+			return array();
3833
+		}
3834
+		return $this->_default_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3835
+	}
3836
+
3837
+
3838
+
3839
+	/**
3840
+	 * Uses the _minimum_where_conditions_strategy set during __construct() to get
3841
+	 * minimum where conditions on all get_all, update, and delete queries done by this model.
3842
+	 * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3843
+	 * NOT array('Event_CPT.post_type'=>'esp_event').
3844
+	 * Similar to _get_default_where_conditions
3845
+	 *
3846
+	 * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3847
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3848
+	 */
3849
+	protected function _get_minimum_where_conditions($model_relation_path = '')
3850
+	{
3851
+		if ($this->_ignore_where_strategy) {
3852
+			return array();
3853
+		}
3854
+		return $this->_minimum_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3855
+	}
3856
+
3857
+
3858
+
3859
+	/**
3860
+	 * Creates the string of SQL for the select part of a select query, everything behind SELECT and before FROM.
3861
+	 * Eg, "Event.post_id, Event.post_name,Event_Detail.EVT_ID..."
3862
+	 *
3863
+	 * @param EE_Model_Query_Info_Carrier $model_query_info
3864
+	 * @return string
3865
+	 * @throws EE_Error
3866
+	 */
3867
+	private function _construct_default_select_sql(EE_Model_Query_Info_Carrier $model_query_info)
3868
+	{
3869
+		$selects = $this->_get_columns_to_select_for_this_model();
3870
+		foreach ($model_query_info->get_model_names_included() as $model_relation_chain => $name_of_other_model_included) {
3871
+			$other_model_included = $this->get_related_model_obj($name_of_other_model_included);
3872
+			$other_model_selects = $other_model_included->_get_columns_to_select_for_this_model($model_relation_chain);
3873
+			foreach ($other_model_selects as $key => $value) {
3874
+				$selects[] = $value;
3875
+			}
3876
+		}
3877
+		return implode(", ", $selects);
3878
+	}
3879
+
3880
+
3881
+
3882
+	/**
3883
+	 * Gets an array of columns to select for this model, which are necessary for it to create its objects.
3884
+	 * So that's going to be the columns for all the fields on the model
3885
+	 *
3886
+	 * @param string $model_relation_chain like 'Question.Question_Group.Event'
3887
+	 * @return array numerically indexed, values are columns to select and rename, eg "Event.ID AS 'Event.ID'"
3888
+	 */
3889
+	public function _get_columns_to_select_for_this_model($model_relation_chain = '')
3890
+	{
3891
+		$fields = $this->field_settings();
3892
+		$selects = array();
3893
+		$table_alias_with_model_relation_chain_prefix = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
3894
+			$model_relation_chain,
3895
+			$this->get_this_model_name()
3896
+		);
3897
+		foreach ($fields as $field_obj) {
3898
+			$selects[] = $table_alias_with_model_relation_chain_prefix
3899
+						 . $field_obj->get_table_alias()
3900
+						 . "."
3901
+						 . $field_obj->get_table_column()
3902
+						 . " AS '"
3903
+						 . $table_alias_with_model_relation_chain_prefix
3904
+						 . $field_obj->get_table_alias()
3905
+						 . "."
3906
+						 . $field_obj->get_table_column()
3907
+						 . "'";
3908
+		}
3909
+		// make sure we are also getting the PKs of each table
3910
+		$tables = $this->get_tables();
3911
+		if (count($tables) > 1) {
3912
+			foreach ($tables as $table_obj) {
3913
+				$qualified_pk_column = $table_alias_with_model_relation_chain_prefix
3914
+									   . $table_obj->get_fully_qualified_pk_column();
3915
+				if (! in_array($qualified_pk_column, $selects)) {
3916
+					$selects[] = "$qualified_pk_column AS '$qualified_pk_column'";
3917
+				}
3918
+			}
3919
+		}
3920
+		return $selects;
3921
+	}
3922
+
3923
+
3924
+
3925
+	/**
3926
+	 * Given a $query_param like 'Registration.Transaction.TXN_ID', pops off 'Registration.',
3927
+	 * gets the join statement for it; gets the data types for it; and passes the remaining 'Transaction.TXN_ID'
3928
+	 * onto its related Transaction object to do the same. Returns an EE_Join_And_Data_Types object which contains the
3929
+	 * SQL for joining, and the data types
3930
+	 *
3931
+	 * @param null|string                 $original_query_param
3932
+	 * @param string                      $query_param          like Registration.Transaction.TXN_ID
3933
+	 * @param EE_Model_Query_Info_Carrier $passed_in_query_info
3934
+	 * @param    string                   $query_param_type     like Registration.Transaction.TXN_ID
3935
+	 *                                                          or 'PAY_ID'. Otherwise, we don't expect there to be a
3936
+	 *                                                          column name. We only want model names, eg 'Event.Venue'
3937
+	 *                                                          or 'Registration's
3938
+	 * @param string                      $original_query_param what it originally was (eg
3939
+	 *                                                          Registration.Transaction.TXN_ID). If null, we assume it
3940
+	 *                                                          matches $query_param
3941
+	 * @throws EE_Error
3942
+	 * @return void only modifies the EEM_Related_Model_Info_Carrier passed into it
3943
+	 */
3944
+	private function _extract_related_model_info_from_query_param(
3945
+		$query_param,
3946
+		EE_Model_Query_Info_Carrier $passed_in_query_info,
3947
+		$query_param_type,
3948
+		$original_query_param = null
3949
+	) {
3950
+		if ($original_query_param === null) {
3951
+			$original_query_param = $query_param;
3952
+		}
3953
+		$query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);
3954
+		/** @var $allow_logic_query_params bool whether or not to allow logic_query_params like 'NOT','OR', or 'AND' */
3955
+		$allow_logic_query_params = in_array($query_param_type, array('where', 'having', 0, 'custom_selects'), true);
3956
+		$allow_fields = in_array(
3957
+			$query_param_type,
3958
+			array('where', 'having', 'order_by', 'group_by', 'order', 'custom_selects', 0),
3959
+			true
3960
+		);
3961
+		// check to see if we have a field on this model
3962
+		$this_model_fields = $this->field_settings(true);
3963
+		if (array_key_exists($query_param, $this_model_fields)) {
3964
+			if ($allow_fields) {
3965
+				return;
3966
+			}
3967
+			throw new EE_Error(
3968
+				sprintf(
3969
+					__(
3970
+						"Using a field name (%s) on model %s is not allowed on this query param type '%s'. Original query param was %s",
3971
+						"event_espresso"
3972
+					),
3973
+					$query_param,
3974
+					get_class($this),
3975
+					$query_param_type,
3976
+					$original_query_param
3977
+				)
3978
+			);
3979
+		}
3980
+		// check if this is a special logic query param
3981
+		if (in_array($query_param, $this->_logic_query_param_keys, true)) {
3982
+			if ($allow_logic_query_params) {
3983
+				return;
3984
+			}
3985
+			throw new EE_Error(
3986
+				sprintf(
3987
+					__(
3988
+						'Logic query params ("%1$s") are being used incorrectly with the following query param ("%2$s") on model %3$s. %4$sAdditional Info:%4$s%5$s',
3989
+						'event_espresso'
3990
+					),
3991
+					implode('", "', $this->_logic_query_param_keys),
3992
+					$query_param,
3993
+					get_class($this),
3994
+					'<br />',
3995
+					"\t"
3996
+					. ' $passed_in_query_info = <pre>'
3997
+					. print_r($passed_in_query_info, true)
3998
+					. '</pre>'
3999
+					. "\n\t"
4000
+					. ' $query_param_type = '
4001
+					. $query_param_type
4002
+					. "\n\t"
4003
+					. ' $original_query_param = '
4004
+					. $original_query_param
4005
+				)
4006
+			);
4007
+		}
4008
+		// check if it's a custom selection
4009
+		if (
4010
+			$this->_custom_selections instanceof CustomSelects
4011
+			&& in_array($query_param, $this->_custom_selections->columnAliases(), true)
4012
+		) {
4013
+			return;
4014
+		}
4015
+		// check if has a model name at the beginning
4016
+		// and
4017
+		// check if it's a field on a related model
4018
+		if (
4019
+			$this->extractJoinModelFromQueryParams(
4020
+				$passed_in_query_info,
4021
+				$query_param,
4022
+				$original_query_param,
4023
+				$query_param_type
4024
+			)
4025
+		) {
4026
+			return;
4027
+		}
4028
+
4029
+		// ok so $query_param didn't start with a model name
4030
+		// and we previously confirmed it wasn't a logic query param or field on the current model
4031
+		// it's wack, that's what it is
4032
+		throw new EE_Error(
4033
+			sprintf(
4034
+				esc_html__(
4035
+					"There is no model named '%s' related to %s. Query param type is %s and original query param is %s",
4036
+					"event_espresso"
4037
+				),
4038
+				$query_param,
4039
+				get_class($this),
4040
+				$query_param_type,
4041
+				$original_query_param
4042
+			)
4043
+		);
4044
+	}
4045
+
4046
+
4047
+	/**
4048
+	 * Extracts any possible join model information from the provided possible_join_string.
4049
+	 * This method will read the provided $possible_join_string value and determine if there are any possible model join
4050
+	 * parts that should be added to the query.
4051
+	 *
4052
+	 * @param EE_Model_Query_Info_Carrier $query_info_carrier
4053
+	 * @param string                      $possible_join_string  Such as Registration.REG_ID, or Registration
4054
+	 * @param null|string                 $original_query_param
4055
+	 * @param string                      $query_parameter_type  The type for the source of the $possible_join_string
4056
+	 *                                                           ('where', 'order_by', 'group_by', 'custom_selects' etc.)
4057
+	 * @return bool  returns true if a join was added and false if not.
4058
+	 * @throws EE_Error
4059
+	 */
4060
+	private function extractJoinModelFromQueryParams(
4061
+		EE_Model_Query_Info_Carrier $query_info_carrier,
4062
+		$possible_join_string,
4063
+		$original_query_param,
4064
+		$query_parameter_type
4065
+	) {
4066
+		foreach ($this->_model_relations as $valid_related_model_name => $relation_obj) {
4067
+			if (strpos($possible_join_string, $valid_related_model_name . ".") === 0) {
4068
+				$this->_add_join_to_model($valid_related_model_name, $query_info_carrier, $original_query_param);
4069
+				$possible_join_string = substr($possible_join_string, strlen($valid_related_model_name . "."));
4070
+				if ($possible_join_string === '') {
4071
+					// nothing left to $query_param
4072
+					// we should actually end in a field name, not a model like this!
4073
+					throw new EE_Error(
4074
+						sprintf(
4075
+							esc_html__(
4076
+								"Query param '%s' (of type %s on model %s) shouldn't end on a period (.) ",
4077
+								"event_espresso"
4078
+							),
4079
+							$possible_join_string,
4080
+							$query_parameter_type,
4081
+							get_class($this),
4082
+							$valid_related_model_name
4083
+						)
4084
+					);
4085
+				}
4086
+				$related_model_obj = $this->get_related_model_obj($valid_related_model_name);
4087
+				$related_model_obj->_extract_related_model_info_from_query_param(
4088
+					$possible_join_string,
4089
+					$query_info_carrier,
4090
+					$query_parameter_type,
4091
+					$original_query_param
4092
+				);
4093
+				return true;
4094
+			}
4095
+			if ($possible_join_string === $valid_related_model_name) {
4096
+				$this->_add_join_to_model(
4097
+					$valid_related_model_name,
4098
+					$query_info_carrier,
4099
+					$original_query_param
4100
+				);
4101
+				return true;
4102
+			}
4103
+		}
4104
+		return false;
4105
+	}
4106
+
4107
+
4108
+	/**
4109
+	 * Extracts related models from Custom Selects and sets up any joins for those related models.
4110
+	 * @param EE_Model_Query_Info_Carrier $query_info_carrier
4111
+	 * @throws EE_Error
4112
+	 */
4113
+	private function extractRelatedModelsFromCustomSelects(EE_Model_Query_Info_Carrier $query_info_carrier)
4114
+	{
4115
+		if (
4116
+			$this->_custom_selections instanceof CustomSelects
4117
+			&& ($this->_custom_selections->type() === CustomSelects::TYPE_STRUCTURED
4118
+				|| $this->_custom_selections->type() == CustomSelects::TYPE_COMPLEX
4119
+			)
4120
+		) {
4121
+			$original_selects = $this->_custom_selections->originalSelects();
4122
+			foreach ($original_selects as $alias => $select_configuration) {
4123
+				$this->extractJoinModelFromQueryParams(
4124
+					$query_info_carrier,
4125
+					$select_configuration[0],
4126
+					$select_configuration[0],
4127
+					'custom_selects'
4128
+				);
4129
+			}
4130
+		}
4131
+	}
4132
+
4133
+
4134
+
4135
+	/**
4136
+	 * Privately used by _extract_related_model_info_from_query_param to add a join to $model_name
4137
+	 * and store it on $passed_in_query_info
4138
+	 *
4139
+	 * @param string                      $model_name
4140
+	 * @param EE_Model_Query_Info_Carrier $passed_in_query_info
4141
+	 * @param string                      $original_query_param used to extract the relation chain between the queried
4142
+	 *                                                          model and $model_name. Eg, if we are querying Event,
4143
+	 *                                                          and are adding a join to 'Payment' with the original
4144
+	 *                                                          query param key
4145
+	 *                                                          'Registration.Transaction.Payment.PAY_amount', we want
4146
+	 *                                                          to extract 'Registration.Transaction.Payment', in case
4147
+	 *                                                          Payment wants to add default query params so that it
4148
+	 *                                                          will know what models to prepend onto its default query
4149
+	 *                                                          params or in case it wants to rename tables (in case
4150
+	 *                                                          there are multiple joins to the same table)
4151
+	 * @return void
4152
+	 * @throws EE_Error
4153
+	 */
4154
+	private function _add_join_to_model(
4155
+		$model_name,
4156
+		EE_Model_Query_Info_Carrier $passed_in_query_info,
4157
+		$original_query_param
4158
+	) {
4159
+		$relation_obj = $this->related_settings_for($model_name);
4160
+		$model_relation_chain = EE_Model_Parser::extract_model_relation_chain($model_name, $original_query_param);
4161
+		// check if the relation is HABTM, because then we're essentially doing two joins
4162
+		// If so, join first to the JOIN table, and add its data types, and then continue as normal
4163
+		if ($relation_obj instanceof EE_HABTM_Relation) {
4164
+			$join_model_obj = $relation_obj->get_join_model();
4165
+			// replace the model specified with the join model for this relation chain, whi
4166
+			$relation_chain_to_join_model = EE_Model_Parser::replace_model_name_with_join_model_name_in_model_relation_chain(
4167
+				$model_name,
4168
+				$join_model_obj->get_this_model_name(),
4169
+				$model_relation_chain
4170
+			);
4171
+			$passed_in_query_info->merge(
4172
+				new EE_Model_Query_Info_Carrier(
4173
+					array($relation_chain_to_join_model => $join_model_obj->get_this_model_name()),
4174
+					$relation_obj->get_join_to_intermediate_model_statement($relation_chain_to_join_model)
4175
+				)
4176
+			);
4177
+		}
4178
+		// now just join to the other table pointed to by the relation object, and add its data types
4179
+		$passed_in_query_info->merge(
4180
+			new EE_Model_Query_Info_Carrier(
4181
+				array($model_relation_chain => $model_name),
4182
+				$relation_obj->get_join_statement($model_relation_chain)
4183
+			)
4184
+		);
4185
+	}
4186
+
4187
+
4188
+
4189
+	/**
4190
+	 * Constructs SQL for where clause, like "WHERE Event.ID = 23 AND Transaction.amount > 100" etc.
4191
+	 *
4192
+	 * @param array $where_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4193
+	 * @return string of SQL
4194
+	 * @throws EE_Error
4195
+	 */
4196
+	private function _construct_where_clause($where_params)
4197
+	{
4198
+		$SQL = $this->_construct_condition_clause_recursive($where_params, ' AND ');
4199
+		if ($SQL) {
4200
+			return " WHERE " . $SQL;
4201
+		}
4202
+		return '';
4203
+	}
4204
+
4205
+
4206
+
4207
+	/**
4208
+	 * Just like the _construct_where_clause, except prepends 'HAVING' instead of 'WHERE',
4209
+	 * and should be passed HAVING parameters, not WHERE parameters
4210
+	 *
4211
+	 * @param array $having_params
4212
+	 * @return string
4213
+	 * @throws EE_Error
4214
+	 */
4215
+	private function _construct_having_clause($having_params)
4216
+	{
4217
+		$SQL = $this->_construct_condition_clause_recursive($having_params, ' AND ');
4218
+		if ($SQL) {
4219
+			return " HAVING " . $SQL;
4220
+		}
4221
+		return '';
4222
+	}
4223
+
4224
+
4225
+	/**
4226
+	 * Used for creating nested WHERE conditions. Eg "WHERE ! (Event.ID = 3 OR ( Event_Meta.meta_key = 'bob' AND
4227
+	 * Event_Meta.meta_value = 'foo'))"
4228
+	 *
4229
+	 * @param array  $where_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4230
+	 * @param string $glue         joins each subclause together. Should really only be " AND " or " OR "...
4231
+	 * @throws EE_Error
4232
+	 * @return string of SQL
4233
+	 */
4234
+	private function _construct_condition_clause_recursive($where_params, $glue = ' AND')
4235
+	{
4236
+		$where_clauses = array();
4237
+		foreach ($where_params as $query_param => $op_and_value_or_sub_condition) {
4238
+			$query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);// str_replace("*",'',$query_param);
4239
+			if (in_array($query_param, $this->_logic_query_param_keys)) {
4240
+				switch ($query_param) {
4241
+					case 'not':
4242
+					case 'NOT':
4243
+						$where_clauses[] = "! ("
4244
+										   . $this->_construct_condition_clause_recursive(
4245
+											   $op_and_value_or_sub_condition,
4246
+											   $glue
4247
+										   )
4248
+										   . ")";
4249
+						break;
4250
+					case 'and':
4251
+					case 'AND':
4252
+						$where_clauses[] = " ("
4253
+										   . $this->_construct_condition_clause_recursive(
4254
+											   $op_and_value_or_sub_condition,
4255
+											   ' AND '
4256
+										   )
4257
+										   . ")";
4258
+						break;
4259
+					case 'or':
4260
+					case 'OR':
4261
+						$where_clauses[] = " ("
4262
+										   . $this->_construct_condition_clause_recursive(
4263
+											   $op_and_value_or_sub_condition,
4264
+											   ' OR '
4265
+										   )
4266
+										   . ")";
4267
+						break;
4268
+				}
4269
+			} else {
4270
+				$field_obj = $this->_deduce_field_from_query_param($query_param);
4271
+				// if it's not a normal field, maybe it's a custom selection?
4272
+				if (! $field_obj) {
4273
+					if ($this->_custom_selections instanceof CustomSelects) {
4274
+						$field_obj = $this->_custom_selections->getDataTypeForAlias($query_param);
4275
+					} else {
4276
+						throw new EE_Error(sprintf(__(
4277
+							"%s is neither a valid model field name, nor a custom selection",
4278
+							"event_espresso"
4279
+						), $query_param));
4280
+					}
4281
+				}
4282
+				$op_and_value_sql = $this->_construct_op_and_value($op_and_value_or_sub_condition, $field_obj);
4283
+				$where_clauses[] = $this->_deduce_column_name_from_query_param($query_param) . SP . $op_and_value_sql;
4284
+			}
4285
+		}
4286
+		return $where_clauses ? implode($glue, $where_clauses) : '';
4287
+	}
4288
+
4289
+
4290
+
4291
+	/**
4292
+	 * Takes the input parameter and extract the table name (alias) and column name
4293
+	 *
4294
+	 * @param string $query_param like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4295
+	 * @throws EE_Error
4296
+	 * @return string table alias and column name for SQL, eg "Transaction.TXN_ID"
4297
+	 */
4298
+	private function _deduce_column_name_from_query_param($query_param)
4299
+	{
4300
+		$field = $this->_deduce_field_from_query_param($query_param);
4301
+		if ($field) {
4302
+			$table_alias_prefix = EE_Model_Parser::extract_table_alias_model_relation_chain_from_query_param(
4303
+				$field->get_model_name(),
4304
+				$query_param
4305
+			);
4306
+			return $table_alias_prefix . $field->get_qualified_column();
4307
+		}
4308
+		if (
4309
+			$this->_custom_selections instanceof CustomSelects
4310
+			&& in_array($query_param, $this->_custom_selections->columnAliases(), true)
4311
+		) {
4312
+			// maybe it's custom selection item?
4313
+			// if so, just use it as the "column name"
4314
+			return $query_param;
4315
+		}
4316
+		$custom_select_aliases = $this->_custom_selections instanceof CustomSelects
4317
+			? implode(',', $this->_custom_selections->columnAliases())
4318
+			: '';
4319
+		throw new EE_Error(
4320
+			sprintf(
4321
+				__(
4322
+					"%s is not a valid field on this model, nor a custom selection (%s)",
4323
+					"event_espresso"
4324
+				),
4325
+				$query_param,
4326
+				$custom_select_aliases
4327
+			)
4328
+		);
4329
+	}
4330
+
4331
+
4332
+
4333
+	/**
4334
+	 * Removes the * and anything after it from the condition query param key. It is useful to add the * to condition
4335
+	 * query param keys (eg, 'OR*', 'EVT_ID') in order for the array keys to still be unique, so that they don't get
4336
+	 * overwritten Takes a string like 'Event.EVT_ID*', 'TXN_total**', 'OR*1st', and 'DTT_reg_start*foobar' to
4337
+	 * 'Event.EVT_ID', 'TXN_total', 'OR', and 'DTT_reg_start', respectively.
4338
+	 *
4339
+	 * @param string $condition_query_param_key
4340
+	 * @return string
4341
+	 */
4342
+	private function _remove_stars_and_anything_after_from_condition_query_param_key($condition_query_param_key)
4343
+	{
4344
+		$pos_of_star = strpos($condition_query_param_key, '*');
4345
+		if ($pos_of_star === false) {
4346
+			return $condition_query_param_key;
4347
+		}
4348
+		$condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
4349
+		return $condition_query_param_sans_star;
4350
+	}
4351
+
4352
+
4353
+
4354
+	/**
4355
+	 * creates the SQL for the operator and the value in a WHERE clause, eg "< 23" or "LIKE '%monkey%'"
4356
+	 *
4357
+	 * @param                            mixed      array | string    $op_and_value
4358
+	 * @param EE_Model_Field_Base|string $field_obj . If string, should be one of EEM_Base::_valid_wpdb_data_types
4359
+	 * @throws EE_Error
4360
+	 * @return string
4361
+	 */
4362
+	private function _construct_op_and_value($op_and_value, $field_obj)
4363
+	{
4364
+		if (is_array($op_and_value)) {
4365
+			$operator = isset($op_and_value[0]) ? $this->_prepare_operator_for_sql($op_and_value[0]) : null;
4366
+			if (! $operator) {
4367
+				$php_array_like_string = array();
4368
+				foreach ($op_and_value as $key => $value) {
4369
+					$php_array_like_string[] = "$key=>$value";
4370
+				}
4371
+				throw new EE_Error(
4372
+					sprintf(
4373
+						__(
4374
+							"You setup a query parameter like you were going to specify an operator, but didn't. You provided '(%s)', but the operator should be at array key index 0 (eg array('>',32))",
4375
+							"event_espresso"
4376
+						),
4377
+						implode(",", $php_array_like_string)
4378
+					)
4379
+				);
4380
+			}
4381
+			$value = isset($op_and_value[1]) ? $op_and_value[1] : null;
4382
+		} else {
4383
+			$operator = '=';
4384
+			$value = $op_and_value;
4385
+		}
4386
+		// check to see if the value is actually another field
4387
+		if (is_array($op_and_value) && isset($op_and_value[2]) && $op_and_value[2] == true) {
4388
+			return $operator . SP . $this->_deduce_column_name_from_query_param($value);
4389
+		}
4390
+		if (in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4391
+			// in this case, the value should be an array, or at least a comma-separated list
4392
+			// it will need to handle a little differently
4393
+			$cleaned_value = $this->_construct_in_value($value, $field_obj);
4394
+			// note: $cleaned_value has already been run through $wpdb->prepare()
4395
+			return $operator . SP . $cleaned_value;
4396
+		}
4397
+		if (in_array($operator, $this->valid_between_style_operators()) && is_array($value)) {
4398
+			// the value should be an array with count of two.
4399
+			if (count($value) !== 2) {
4400
+				throw new EE_Error(
4401
+					sprintf(
4402
+						__(
4403
+							"The '%s' operator must be used with an array of values and there must be exactly TWO values in that array.",
4404
+							'event_espresso'
4405
+						),
4406
+						"BETWEEN"
4407
+					)
4408
+				);
4409
+			}
4410
+			$cleaned_value = $this->_construct_between_value($value, $field_obj);
4411
+			return $operator . SP . $cleaned_value;
4412
+		}
4413
+		if (in_array($operator, $this->valid_null_style_operators())) {
4414
+			if ($value !== null) {
4415
+				throw new EE_Error(
4416
+					sprintf(
4417
+						__(
4418
+							"You attempted to give a value  (%s) while using a NULL-style operator (%s). That isn't valid",
4419
+							"event_espresso"
4420
+						),
4421
+						$value,
4422
+						$operator
4423
+					)
4424
+				);
4425
+			}
4426
+			return $operator;
4427
+		}
4428
+		if (in_array($operator, $this->valid_like_style_operators()) && ! is_array($value)) {
4429
+			// if the operator is 'LIKE', we want to allow percent signs (%) and not
4430
+			// remove other junk. So just treat it as a string.
4431
+			return $operator . SP . $this->_wpdb_prepare_using_field($value, '%s');
4432
+		}
4433
+		if (! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4434
+			return $operator . SP . $this->_wpdb_prepare_using_field($value, $field_obj);
4435
+		}
4436
+		if (in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4437
+			throw new EE_Error(
4438
+				sprintf(
4439
+					__(
4440
+						"Operator '%s' must be used with an array of values, eg 'Registration.REG_ID' => array('%s',array(1,2,3))",
4441
+						'event_espresso'
4442
+					),
4443
+					$operator,
4444
+					$operator
4445
+				)
4446
+			);
4447
+		}
4448
+		if (! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4449
+			throw new EE_Error(
4450
+				sprintf(
4451
+					__(
4452
+						"Operator '%s' must be used with a single value, not an array. Eg 'Registration.REG_ID => array('%s',23))",
4453
+						'event_espresso'
4454
+					),
4455
+					$operator,
4456
+					$operator
4457
+				)
4458
+			);
4459
+		}
4460
+		throw new EE_Error(
4461
+			sprintf(
4462
+				__(
4463
+					"It appears you've provided some totally invalid query parameters. Operator and value were:'%s', which isn't right at all",
4464
+					"event_espresso"
4465
+				),
4466
+				http_build_query($op_and_value)
4467
+			)
4468
+		);
4469
+	}
4470
+
4471
+
4472
+
4473
+	/**
4474
+	 * Creates the operands to be used in a BETWEEN query, eg "'2014-12-31 20:23:33' AND '2015-01-23 12:32:54'"
4475
+	 *
4476
+	 * @param array                      $values
4477
+	 * @param EE_Model_Field_Base|string $field_obj if string, it should be the datatype to be used when querying, eg
4478
+	 *                                              '%s'
4479
+	 * @return string
4480
+	 * @throws EE_Error
4481
+	 */
4482
+	public function _construct_between_value($values, $field_obj)
4483
+	{
4484
+		$cleaned_values = array();
4485
+		foreach ($values as $value) {
4486
+			$cleaned_values[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4487
+		}
4488
+		return $cleaned_values[0] . " AND " . $cleaned_values[1];
4489
+	}
4490
+
4491
+
4492
+	/**
4493
+	 * Takes an array or a comma-separated list of $values and cleans them
4494
+	 * according to $data_type using $wpdb->prepare, and then makes the list a
4495
+	 * string surrounded by ( and ). Eg, _construct_in_value(array(1,2,3),'%d') would
4496
+	 * return '(1,2,3)'; _construct_in_value("1,2,hack",'%d') would return '(1,2,1)' (assuming
4497
+	 * I'm right that a string, when interpreted as a digit, becomes a 1. It might become a 0)
4498
+	 *
4499
+	 * @param mixed                      $values    array or comma-separated string
4500
+	 * @param EE_Model_Field_Base|string $field_obj if string, it should be a wpdb data type like '%s', or '%d'
4501
+	 * @return string of SQL to follow an 'IN' or 'NOT IN' operator
4502
+	 * @throws EE_Error
4503
+	 */
4504
+	public function _construct_in_value($values, $field_obj)
4505
+	{
4506
+		$prepped = [];
4507
+		// check if the value is a CSV list
4508
+		if (is_string($values)) {
4509
+			// in which case, turn it into an array
4510
+			$values = explode(',', $values);
4511
+		}
4512
+		// make sure we only have one of each value in the list
4513
+		$values = array_unique($values);
4514
+		foreach ($values as $value) {
4515
+			$prepped[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4516
+		}
4517
+		// we would just LOVE to leave $cleaned_values as an empty array, and return the value as "()",
4518
+		// but unfortunately that's invalid SQL. So instead we return a string which we KNOW will evaluate to be the empty set
4519
+		// which is effectively equivalent to returning "()". We don't return "(0)" because that only works for auto-incrementing columns
4520
+		if (empty($prepped)) {
4521
+			$all_fields = $this->field_settings();
4522
+			$first_field    = reset($all_fields);
4523
+			$main_table = $this->_get_main_table();
4524
+			$prepped[]  = "SELECT {$first_field->get_table_column()} FROM {$main_table->get_table_name()} WHERE FALSE";
4525
+		}
4526
+		return '(' . implode(',', $prepped) . ')';
4527
+	}
4528
+
4529
+
4530
+
4531
+	/**
4532
+	 * @param mixed                      $value
4533
+	 * @param EE_Model_Field_Base|string $field_obj if string it should be a wpdb data type like '%d'
4534
+	 * @throws EE_Error
4535
+	 * @return false|null|string
4536
+	 */
4537
+	private function _wpdb_prepare_using_field($value, $field_obj)
4538
+	{
4539
+		/** @type WPDB $wpdb */
4540
+		global $wpdb;
4541
+		if ($field_obj instanceof EE_Model_Field_Base) {
4542
+			return $wpdb->prepare(
4543
+				$field_obj->get_wpdb_data_type(),
4544
+				$this->_prepare_value_for_use_in_db($value, $field_obj)
4545
+			);
4546
+		} //$field_obj should really just be a data type
4547
+		if (! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4548
+			throw new EE_Error(
4549
+				sprintf(
4550
+					__("%s is not a valid wpdb datatype. Valid ones are %s", "event_espresso"),
4551
+					$field_obj,
4552
+					implode(",", $this->_valid_wpdb_data_types)
4553
+				)
4554
+			);
4555
+		}
4556
+		return $wpdb->prepare($field_obj, $value);
4557
+	}
4558
+
4559
+
4560
+
4561
+	/**
4562
+	 * Takes the input parameter and finds the model field that it indicates.
4563
+	 *
4564
+	 * @param string $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4565
+	 * @throws EE_Error
4566
+	 * @return EE_Model_Field_Base
4567
+	 */
4568
+	protected function _deduce_field_from_query_param($query_param_name)
4569
+	{
4570
+		// ok, now proceed with deducing which part is the model's name, and which is the field's name
4571
+		// which will help us find the database table and column
4572
+		$query_param_parts = explode(".", $query_param_name);
4573
+		if (empty($query_param_parts)) {
4574
+			throw new EE_Error(sprintf(__(
4575
+				"_extract_column_name is empty when trying to extract column and table name from %s",
4576
+				'event_espresso'
4577
+			), $query_param_name));
4578
+		}
4579
+		$number_of_parts = count($query_param_parts);
4580
+		$last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
4581
+		if ($number_of_parts === 1) {
4582
+			$field_name = $last_query_param_part;
4583
+			$model_obj = $this;
4584
+		} else {// $number_of_parts >= 2
4585
+			// the last part is the column name, and there are only 2parts. therefore...
4586
+			$field_name = $last_query_param_part;
4587
+			$model_obj = $this->get_related_model_obj($query_param_parts[ $number_of_parts - 2 ]);
4588
+		}
4589
+		try {
4590
+			return $model_obj->field_settings_for($field_name);
4591
+		} catch (EE_Error $e) {
4592
+			return null;
4593
+		}
4594
+	}
4595
+
4596
+
4597
+
4598
+	/**
4599
+	 * Given a field's name (ie, a key in $this->field_settings()), uses the EE_Model_Field object to get the table's
4600
+	 * alias and column which corresponds to it
4601
+	 *
4602
+	 * @param string $field_name
4603
+	 * @throws EE_Error
4604
+	 * @return string
4605
+	 */
4606
+	public function _get_qualified_column_for_field($field_name)
4607
+	{
4608
+		$all_fields = $this->field_settings();
4609
+		$field = isset($all_fields[ $field_name ]) ? $all_fields[ $field_name ] : false;
4610
+		if ($field) {
4611
+			return $field->get_qualified_column();
4612
+		}
4613
+		throw new EE_Error(
4614
+			sprintf(
4615
+				__(
4616
+					"There is no field titled %s on model %s. Either the query trying to use it is bad, or you need to add it to the list of fields on the model.",
4617
+					'event_espresso'
4618
+				),
4619
+				$field_name,
4620
+				get_class($this)
4621
+			)
4622
+		);
4623
+	}
4624
+
4625
+
4626
+
4627
+	/**
4628
+	 * similar to \EEM_Base::_get_qualified_column_for_field() but returns an array with data for ALL fields.
4629
+	 * Example usage:
4630
+	 * EEM_Ticket::instance()->get_all_wpdb_results(
4631
+	 *      array(),
4632
+	 *      ARRAY_A,
4633
+	 *      EEM_Ticket::instance()->get_qualified_columns_for_all_fields()
4634
+	 *  );
4635
+	 * is equivalent to
4636
+	 *  EEM_Ticket::instance()->get_all_wpdb_results( array(), ARRAY_A, '*' );
4637
+	 * and
4638
+	 *  EEM_Event::instance()->get_all_wpdb_results(
4639
+	 *      array(
4640
+	 *          array(
4641
+	 *              'Datetime.Ticket.TKT_ID' => array( '<', 100 ),
4642
+	 *          ),
4643
+	 *          ARRAY_A,
4644
+	 *          implode(
4645
+	 *              ', ',
4646
+	 *              array_merge(
4647
+	 *                  EEM_Event::instance()->get_qualified_columns_for_all_fields( '', false ),
4648
+	 *                  EEM_Ticket::instance()->get_qualified_columns_for_all_fields( 'Datetime', false )
4649
+	 *              )
4650
+	 *          )
4651
+	 *      )
4652
+	 *  );
4653
+	 * selects rows from the database, selecting all the event and ticket columns, where the ticket ID is below 100
4654
+	 *
4655
+	 * @param string $model_relation_chain        the chain of models used to join between the model you want to query
4656
+	 *                                            and the one whose fields you are selecting for example: when querying
4657
+	 *                                            tickets model and selecting fields from the tickets model you would
4658
+	 *                                            leave this parameter empty, because no models are needed to join
4659
+	 *                                            between the queried model and the selected one. Likewise when
4660
+	 *                                            querying the datetime model and selecting fields from the tickets
4661
+	 *                                            model, it would also be left empty, because there is a direct
4662
+	 *                                            relation from datetimes to tickets, so no model is needed to join
4663
+	 *                                            them together. However, when querying from the event model and
4664
+	 *                                            selecting fields from the ticket model, you should provide the string
4665
+	 *                                            'Datetime', indicating that the event model must first join to the
4666
+	 *                                            datetime model in order to find its relation to ticket model.
4667
+	 *                                            Also, when querying from the venue model and selecting fields from
4668
+	 *                                            the ticket model, you should provide the string 'Event.Datetime',
4669
+	 *                                            indicating you need to join the venue model to the event model,
4670
+	 *                                            to the datetime model, in order to find its relation to the ticket model.
4671
+	 *                                            This string is used to deduce the prefix that gets added onto the
4672
+	 *                                            models' tables qualified columns
4673
+	 * @param bool   $return_string               if true, will return a string with qualified column names separated
4674
+	 *                                            by ', ' if false, will simply return a numerically indexed array of
4675
+	 *                                            qualified column names
4676
+	 * @return array|string
4677
+	 */
4678
+	public function get_qualified_columns_for_all_fields($model_relation_chain = '', $return_string = true)
4679
+	{
4680
+		$table_prefix = str_replace('.', '__', $model_relation_chain) . (empty($model_relation_chain) ? '' : '__');
4681
+		$qualified_columns = array();
4682
+		foreach ($this->field_settings() as $field_name => $field) {
4683
+			$qualified_columns[] = $table_prefix . $field->get_qualified_column();
4684
+		}
4685
+		return $return_string ? implode(', ', $qualified_columns) : $qualified_columns;
4686
+	}
4687
+
4688
+
4689
+
4690
+	/**
4691
+	 * constructs the select use on special limit joins
4692
+	 * NOTE: for now this has only been tested and will work when the  table alias is for the PRIMARY table. Although
4693
+	 * its setup so the select query will be setup on and just doing the special select join off of the primary table
4694
+	 * (as that is typically where the limits would be set).
4695
+	 *
4696
+	 * @param  string       $table_alias The table the select is being built for
4697
+	 * @param  mixed|string $limit       The limit for this select
4698
+	 * @return string                The final select join element for the query.
4699
+	 */
4700
+	public function _construct_limit_join_select($table_alias, $limit)
4701
+	{
4702
+		$SQL = '';
4703
+		foreach ($this->_tables as $table_obj) {
4704
+			if ($table_obj instanceof EE_Primary_Table) {
4705
+				$SQL .= $table_alias === $table_obj->get_table_alias()
4706
+					? $table_obj->get_select_join_limit($limit)
4707
+					: SP . $table_obj->get_table_name() . " AS " . $table_obj->get_table_alias() . SP;
4708
+			} elseif ($table_obj instanceof EE_Secondary_Table) {
4709
+				$SQL .= $table_alias === $table_obj->get_table_alias()
4710
+					? $table_obj->get_select_join_limit_join($limit)
4711
+					: SP . $table_obj->get_join_sql($table_alias) . SP;
4712
+			}
4713
+		}
4714
+		return $SQL;
4715
+	}
4716
+
4717
+
4718
+
4719
+	/**
4720
+	 * Constructs the internal join if there are multiple tables, or simply the table's name and alias
4721
+	 * Eg "wp_post AS Event" or "wp_post AS Event INNER JOIN wp_postmeta Event_Meta ON Event.ID = Event_Meta.post_id"
4722
+	 *
4723
+	 * @return string SQL
4724
+	 * @throws EE_Error
4725
+	 */
4726
+	public function _construct_internal_join()
4727
+	{
4728
+		$SQL = $this->_get_main_table()->get_table_sql();
4729
+		$SQL .= $this->_construct_internal_join_to_table_with_alias($this->_get_main_table()->get_table_alias());
4730
+		return $SQL;
4731
+	}
4732
+
4733
+
4734
+
4735
+	/**
4736
+	 * Constructs the SQL for joining all the tables on this model.
4737
+	 * Normally $alias should be the primary table's alias, but in cases where
4738
+	 * we have already joined to a secondary table (eg, the secondary table has a foreign key and is joined before the
4739
+	 * primary table) then we should provide that secondary table's alias. Eg, with $alias being the primary table's
4740
+	 * alias, this will construct SQL like:
4741
+	 * " INNER JOIN wp_esp_secondary_table AS Secondary_Table ON Primary_Table.pk = Secondary_Table.fk".
4742
+	 * With $alias being a secondary table's alias, this will construct SQL like:
4743
+	 * " INNER JOIN wp_esp_primary_table AS Primary_Table ON Primary_Table.pk = Secondary_Table.fk".
4744
+	 *
4745
+	 * @param string $alias_prefixed table alias to join to (this table should already be in the FROM SQL clause)
4746
+	 * @return string
4747
+	 */
4748
+	public function _construct_internal_join_to_table_with_alias($alias_prefixed)
4749
+	{
4750
+		$SQL = '';
4751
+		$alias_sans_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($alias_prefixed);
4752
+		foreach ($this->_tables as $table_obj) {
4753
+			if ($table_obj instanceof EE_Secondary_Table) {// table is secondary table
4754
+				if ($alias_sans_prefix === $table_obj->get_table_alias()) {
4755
+					// so we're joining to this table, meaning the table is already in
4756
+					// the FROM statement, BUT the primary table isn't. So we want
4757
+					// to add the inverse join sql
4758
+					$SQL .= $table_obj->get_inverse_join_sql($alias_prefixed);
4759
+				} else {
4760
+					// just add a regular JOIN to this table from the primary table
4761
+					$SQL .= $table_obj->get_join_sql($alias_prefixed);
4762
+				}
4763
+			}//if it's a primary table, dont add any SQL. it should already be in the FROM statement
4764
+		}
4765
+		return $SQL;
4766
+	}
4767
+
4768
+
4769
+
4770
+	/**
4771
+	 * Gets an array for storing all the data types on the next-to-be-executed-query.
4772
+	 * This should be a growing array of keys being table-columns (eg 'EVT_ID' and 'Event.EVT_ID'), and values being
4773
+	 * their data type (eg, '%s', '%d', etc)
4774
+	 *
4775
+	 * @return array
4776
+	 */
4777
+	public function _get_data_types()
4778
+	{
4779
+		$data_types = array();
4780
+		foreach ($this->field_settings() as $field_obj) {
4781
+			// $data_types[$field_obj->get_table_column()] = $field_obj->get_wpdb_data_type();
4782
+			/** @var $field_obj EE_Model_Field_Base */
4783
+			$data_types[ $field_obj->get_qualified_column() ] = $field_obj->get_wpdb_data_type();
4784
+		}
4785
+		return $data_types;
4786
+	}
4787
+
4788
+
4789
+
4790
+	/**
4791
+	 * Gets the model object given the relation's name / model's name (eg, 'Event', 'Registration',etc. Always singular)
4792
+	 *
4793
+	 * @param string $model_name
4794
+	 * @throws EE_Error
4795
+	 * @return EEM_Base
4796
+	 */
4797
+	public function get_related_model_obj($model_name)
4798
+	{
4799
+		$model_classname = "EEM_" . $model_name;
4800
+		if (! class_exists($model_classname)) {
4801
+			throw new EE_Error(sprintf(__(
4802
+				"You specified a related model named %s in your query. No such model exists, if it did, it would have the classname %s",
4803
+				'event_espresso'
4804
+			), $model_name, $model_classname));
4805
+		}
4806
+		return call_user_func($model_classname . "::instance");
4807
+	}
4808
+
4809
+
4810
+
4811
+	/**
4812
+	 * Returns the array of EE_ModelRelations for this model.
4813
+	 *
4814
+	 * @return EE_Model_Relation_Base[]
4815
+	 */
4816
+	public function relation_settings()
4817
+	{
4818
+		return $this->_model_relations;
4819
+	}
4820
+
4821
+
4822
+
4823
+	/**
4824
+	 * Gets all related models that this model BELONGS TO. Handy to know sometimes
4825
+	 * because without THOSE models, this model probably doesn't have much purpose.
4826
+	 * (Eg, without an event, datetimes have little purpose.)
4827
+	 *
4828
+	 * @return EE_Belongs_To_Relation[]
4829
+	 */
4830
+	public function belongs_to_relations()
4831
+	{
4832
+		$belongs_to_relations = array();
4833
+		foreach ($this->relation_settings() as $model_name => $relation_obj) {
4834
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
4835
+				$belongs_to_relations[ $model_name ] = $relation_obj;
4836
+			}
4837
+		}
4838
+		return $belongs_to_relations;
4839
+	}
4840
+
4841
+
4842
+
4843
+	/**
4844
+	 * Returns the specified EE_Model_Relation, or throws an exception
4845
+	 *
4846
+	 * @param string $relation_name name of relation, key in $this->_relatedModels
4847
+	 * @throws EE_Error
4848
+	 * @return EE_Model_Relation_Base
4849
+	 */
4850
+	public function related_settings_for($relation_name)
4851
+	{
4852
+		$relatedModels = $this->relation_settings();
4853
+		if (! array_key_exists($relation_name, $relatedModels)) {
4854
+			throw new EE_Error(
4855
+				sprintf(
4856
+					__(
4857
+						'Cannot get %s related to %s. There is no model relation of that type. There is, however, %s...',
4858
+						'event_espresso'
4859
+					),
4860
+					$relation_name,
4861
+					$this->_get_class_name(),
4862
+					implode(', ', array_keys($relatedModels))
4863
+				)
4864
+			);
4865
+		}
4866
+		return $relatedModels[ $relation_name ];
4867
+	}
4868
+
4869
+
4870
+
4871
+	/**
4872
+	 * A convenience method for getting a specific field's settings, instead of getting all field settings for all
4873
+	 * fields
4874
+	 *
4875
+	 * @param string $fieldName
4876
+	 * @param boolean $include_db_only_fields
4877
+	 * @throws EE_Error
4878
+	 * @return EE_Model_Field_Base
4879
+	 */
4880
+	public function field_settings_for($fieldName, $include_db_only_fields = true)
4881
+	{
4882
+		$fieldSettings = $this->field_settings($include_db_only_fields);
4883
+		if (! array_key_exists($fieldName, $fieldSettings)) {
4884
+			throw new EE_Error(sprintf(
4885
+				__("There is no field/column '%s' on '%s'", 'event_espresso'),
4886
+				$fieldName,
4887
+				get_class($this)
4888
+			));
4889
+		}
4890
+		return $fieldSettings[ $fieldName ];
4891
+	}
4892
+
4893
+
4894
+
4895
+	/**
4896
+	 * Checks if this field exists on this model
4897
+	 *
4898
+	 * @param string $fieldName a key in the model's _field_settings array
4899
+	 * @return boolean
4900
+	 */
4901
+	public function has_field($fieldName)
4902
+	{
4903
+		$fieldSettings = $this->field_settings(true);
4904
+		if (isset($fieldSettings[ $fieldName ])) {
4905
+			return true;
4906
+		}
4907
+		return false;
4908
+	}
4909
+
4910
+
4911
+
4912
+	/**
4913
+	 * Returns whether or not this model has a relation to the specified model
4914
+	 *
4915
+	 * @param string $relation_name possibly one of the keys in the relation_settings array
4916
+	 * @return boolean
4917
+	 */
4918
+	public function has_relation($relation_name)
4919
+	{
4920
+		$relations = $this->relation_settings();
4921
+		if (isset($relations[ $relation_name ])) {
4922
+			return true;
4923
+		}
4924
+		return false;
4925
+	}
4926
+
4927
+
4928
+
4929
+	/**
4930
+	 * gets the field object of type 'primary_key' from the fieldsSettings attribute.
4931
+	 * Eg, on EE_Answer that would be ANS_ID field object
4932
+	 *
4933
+	 * @param $field_obj
4934
+	 * @return boolean
4935
+	 */
4936
+	public function is_primary_key_field($field_obj)
4937
+	{
4938
+		return $field_obj instanceof EE_Primary_Key_Field_Base ? true : false;
4939
+	}
4940
+
4941
+
4942
+
4943
+	/**
4944
+	 * gets the field object of type 'primary_key' from the fieldsSettings attribute.
4945
+	 * Eg, on EE_Answer that would be ANS_ID field object
4946
+	 *
4947
+	 * @return EE_Model_Field_Base
4948
+	 * @throws EE_Error
4949
+	 */
4950
+	public function get_primary_key_field()
4951
+	{
4952
+		if ($this->_primary_key_field === null) {
4953
+			foreach ($this->field_settings(true) as $field_obj) {
4954
+				if ($this->is_primary_key_field($field_obj)) {
4955
+					$this->_primary_key_field = $field_obj;
4956
+					break;
4957
+				}
4958
+			}
4959
+			if (! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
4960
+				throw new EE_Error(sprintf(
4961
+					__("There is no Primary Key defined on model %s", 'event_espresso'),
4962
+					get_class($this)
4963
+				));
4964
+			}
4965
+		}
4966
+		return $this->_primary_key_field;
4967
+	}
4968
+
4969
+
4970
+
4971
+	/**
4972
+	 * Returns whether or not not there is a primary key on this model.
4973
+	 * Internally does some caching.
4974
+	 *
4975
+	 * @return boolean
4976
+	 */
4977
+	public function has_primary_key_field()
4978
+	{
4979
+		if ($this->_has_primary_key_field === null) {
4980
+			try {
4981
+				$this->get_primary_key_field();
4982
+				$this->_has_primary_key_field = true;
4983
+			} catch (EE_Error $e) {
4984
+				$this->_has_primary_key_field = false;
4985
+			}
4986
+		}
4987
+		return $this->_has_primary_key_field;
4988
+	}
4989
+
4990
+
4991
+
4992
+	/**
4993
+	 * Finds the first field of type $field_class_name.
4994
+	 *
4995
+	 * @param string $field_class_name class name of field that you want to find. Eg, EE_Datetime_Field,
4996
+	 *                                 EE_Foreign_Key_Field, etc
4997
+	 * @return EE_Model_Field_Base or null if none is found
4998
+	 */
4999
+	public function get_a_field_of_type($field_class_name)
5000
+	{
5001
+		foreach ($this->field_settings() as $field) {
5002
+			if ($field instanceof $field_class_name) {
5003
+				return $field;
5004
+			}
5005
+		}
5006
+		return null;
5007
+	}
5008
+
5009
+
5010
+
5011
+	/**
5012
+	 * Gets a foreign key field pointing to model.
5013
+	 *
5014
+	 * @param string $model_name eg Event, Registration, not EEM_Event
5015
+	 * @return EE_Foreign_Key_Field_Base
5016
+	 * @throws EE_Error
5017
+	 */
5018
+	public function get_foreign_key_to($model_name)
5019
+	{
5020
+		if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5021
+			foreach ($this->field_settings() as $field) {
5022
+				if (
5023
+					$field instanceof EE_Foreign_Key_Field_Base
5024
+					&& in_array($model_name, $field->get_model_names_pointed_to())
5025
+				) {
5026
+					$this->_cache_foreign_key_to_fields[ $model_name ] = $field;
5027
+					break;
5028
+				}
5029
+			}
5030
+			if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5031
+				throw new EE_Error(sprintf(__(
5032
+					"There is no foreign key field pointing to model %s on model %s",
5033
+					'event_espresso'
5034
+				), $model_name, get_class($this)));
5035
+			}
5036
+		}
5037
+		return $this->_cache_foreign_key_to_fields[ $model_name ];
5038
+	}
5039
+
5040
+
5041
+
5042
+	/**
5043
+	 * Gets the table name (including $wpdb->prefix) for the table alias
5044
+	 *
5045
+	 * @param string $table_alias eg Event, Event_Meta, Registration, Transaction, but maybe
5046
+	 *                            a table alias with a model chain prefix, like 'Venue__Event_Venue___Event_Meta'.
5047
+	 *                            Either one works
5048
+	 * @return string
5049
+	 */
5050
+	public function get_table_for_alias($table_alias)
5051
+	{
5052
+		$table_alias_sans_model_relation_chain_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($table_alias);
5053
+		return $this->_tables[ $table_alias_sans_model_relation_chain_prefix ]->get_table_name();
5054
+	}
5055
+
5056
+
5057
+
5058
+	/**
5059
+	 * Returns a flat array of all field son this model, instead of organizing them
5060
+	 * by table_alias as they are in the constructor.
5061
+	 *
5062
+	 * @param bool $include_db_only_fields flag indicating whether or not to include the db-only fields
5063
+	 * @return EE_Model_Field_Base[] where the keys are the field's name
5064
+	 */
5065
+	public function field_settings($include_db_only_fields = false)
5066
+	{
5067
+		if ($include_db_only_fields) {
5068
+			if ($this->_cached_fields === null) {
5069
+				$this->_cached_fields = array();
5070
+				foreach ($this->_fields as $fields_corresponding_to_table) {
5071
+					foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5072
+						$this->_cached_fields[ $field_name ] = $field_obj;
5073
+					}
5074
+				}
5075
+			}
5076
+			return $this->_cached_fields;
5077
+		}
5078
+		if ($this->_cached_fields_non_db_only === null) {
5079
+			$this->_cached_fields_non_db_only = array();
5080
+			foreach ($this->_fields as $fields_corresponding_to_table) {
5081
+				foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5082
+					/** @var $field_obj EE_Model_Field_Base */
5083
+					if (! $field_obj->is_db_only_field()) {
5084
+						$this->_cached_fields_non_db_only[ $field_name ] = $field_obj;
5085
+					}
5086
+				}
5087
+			}
5088
+		}
5089
+		return $this->_cached_fields_non_db_only;
5090
+	}
5091
+
5092
+
5093
+
5094
+	/**
5095
+	 *        cycle though array of attendees and create objects out of each item
5096
+	 *
5097
+	 * @access        private
5098
+	 * @param        array $rows of results of $wpdb->get_results($query,ARRAY_A)
5099
+	 * @return \EE_Base_Class[] array keys are primary keys (if there is a primary key on the model. if not,
5100
+	 *                           numerically indexed)
5101
+	 * @throws EE_Error
5102
+	 */
5103
+	protected function _create_objects($rows = array())
5104
+	{
5105
+		$array_of_objects = array();
5106
+		if (empty($rows)) {
5107
+			return array();
5108
+		}
5109
+		$count_if_model_has_no_primary_key = 0;
5110
+		$has_primary_key = $this->has_primary_key_field();
5111
+		$primary_key_field = $has_primary_key ? $this->get_primary_key_field() : null;
5112
+		foreach ((array) $rows as $row) {
5113
+			if (empty($row)) {
5114
+				// wp did its weird thing where it returns an array like array(0=>null), which is totally not helpful...
5115
+				return array();
5116
+			}
5117
+			// check if we've already set this object in the results array,
5118
+			// in which case there's no need to process it further (again)
5119
+			if ($has_primary_key) {
5120
+				$table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5121
+					$row,
5122
+					$primary_key_field->get_qualified_column(),
5123
+					$primary_key_field->get_table_column()
5124
+				);
5125
+				if ($table_pk_value && isset($array_of_objects[ $table_pk_value ])) {
5126
+					continue;
5127
+				}
5128
+			}
5129
+			$classInstance = $this->instantiate_class_from_array_or_object($row);
5130
+			if (! $classInstance) {
5131
+				throw new EE_Error(
5132
+					sprintf(
5133
+						__('Could not create instance of class %s from row %s', 'event_espresso'),
5134
+						$this->get_this_model_name(),
5135
+						http_build_query($row)
5136
+					)
5137
+				);
5138
+			}
5139
+			// set the timezone on the instantiated objects
5140
+			$classInstance->set_timezone($this->_timezone);
5141
+			// make sure if there is any timezone setting present that we set the timezone for the object
5142
+			$key = $has_primary_key ? $classInstance->ID() : $count_if_model_has_no_primary_key++;
5143
+			$array_of_objects[ $key ] = $classInstance;
5144
+			// also, for all the relations of type BelongsTo, see if we can cache
5145
+			// those related models
5146
+			// (we could do this for other relations too, but if there are conditions
5147
+			// that filtered out some fo the results, then we'd be caching an incomplete set
5148
+			// so it requires a little more thought than just caching them immediately...)
5149
+			foreach ($this->_model_relations as $modelName => $relation_obj) {
5150
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
5151
+					// check if this model's INFO is present. If so, cache it on the model
5152
+					$other_model = $relation_obj->get_other_model();
5153
+					$other_model_obj_maybe = $other_model->instantiate_class_from_array_or_object($row);
5154
+					// if we managed to make a model object from the results, cache it on the main model object
5155
+					if ($other_model_obj_maybe) {
5156
+						// set timezone on these other model objects if they are present
5157
+						$other_model_obj_maybe->set_timezone($this->_timezone);
5158
+						$classInstance->cache($modelName, $other_model_obj_maybe);
5159
+					}
5160
+				}
5161
+			}
5162
+			// also, if this was a custom select query, let's see if there are any results for the custom select fields
5163
+			// and add them to the object as well.  We'll convert according to the set data_type if there's any set for
5164
+			// the field in the CustomSelects object
5165
+			if ($this->_custom_selections instanceof CustomSelects) {
5166
+				$classInstance->setCustomSelectsValues(
5167
+					$this->getValuesForCustomSelectAliasesFromResults($row)
5168
+				);
5169
+			}
5170
+		}
5171
+		return $array_of_objects;
5172
+	}
5173
+
5174
+
5175
+	/**
5176
+	 * This will parse a given row of results from the db and see if any keys in the results match an alias within the
5177
+	 * current CustomSelects object. This will be used to build an array of values indexed by those keys.
5178
+	 *
5179
+	 * @param array $db_results_row
5180
+	 * @return array
5181
+	 */
5182
+	protected function getValuesForCustomSelectAliasesFromResults(array $db_results_row)
5183
+	{
5184
+		$results = array();
5185
+		if ($this->_custom_selections instanceof CustomSelects) {
5186
+			foreach ($this->_custom_selections->columnAliases() as $alias) {
5187
+				if (isset($db_results_row[ $alias ])) {
5188
+					$results[ $alias ] = $this->convertValueToDataType(
5189
+						$db_results_row[ $alias ],
5190
+						$this->_custom_selections->getDataTypeForAlias($alias)
5191
+					);
5192
+				}
5193
+			}
5194
+		}
5195
+		return $results;
5196
+	}
5197
+
5198
+
5199
+	/**
5200
+	 * This will set the value for the given alias
5201
+	 * @param string $value
5202
+	 * @param string $datatype (one of %d, %s, %f)
5203
+	 * @return int|string|float (int for %d, string for %s, float for %f)
5204
+	 */
5205
+	protected function convertValueToDataType($value, $datatype)
5206
+	{
5207
+		switch ($datatype) {
5208
+			case '%f':
5209
+				return (float) $value;
5210
+			case '%d':
5211
+				return (int) $value;
5212
+			default:
5213
+				return (string) $value;
5214
+		}
5215
+	}
5216
+
5217
+
5218
+	/**
5219
+	 * The purpose of this method is to allow us to create a model object that is not in the db that holds default
5220
+	 * values. A typical example of where this is used is when creating a new item and the initial load of a form.  We
5221
+	 * dont' necessarily want to test for if the object is present but just assume it is BUT load the defaults from the
5222
+	 * object (as set in the model_field!).
5223
+	 *
5224
+	 * @return EE_Base_Class single EE_Base_Class object with default values for the properties.
5225
+	 */
5226
+	public function create_default_object()
5227
+	{
5228
+		$this_model_fields_and_values = array();
5229
+		// setup the row using default values;
5230
+		foreach ($this->field_settings() as $field_name => $field_obj) {
5231
+			$this_model_fields_and_values[ $field_name ] = $field_obj->get_default_value();
5232
+		}
5233
+		$className = $this->_get_class_name();
5234
+		$classInstance = EE_Registry::instance()
5235
+									->load_class($className, array($this_model_fields_and_values), false, false);
5236
+		return $classInstance;
5237
+	}
5238
+
5239
+
5240
+
5241
+	/**
5242
+	 * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
5243
+	 *                             or an stdClass where each property is the name of a column,
5244
+	 * @return EE_Base_Class
5245
+	 * @throws EE_Error
5246
+	 */
5247
+	public function instantiate_class_from_array_or_object($cols_n_values)
5248
+	{
5249
+		if (! is_array($cols_n_values) && is_object($cols_n_values)) {
5250
+			$cols_n_values = get_object_vars($cols_n_values);
5251
+		}
5252
+		$primary_key = null;
5253
+		// make sure the array only has keys that are fields/columns on this model
5254
+		$this_model_fields_n_values = $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5255
+		if ($this->has_primary_key_field() && isset($this_model_fields_n_values[ $this->primary_key_name() ])) {
5256
+			$primary_key = $this_model_fields_n_values[ $this->primary_key_name() ];
5257
+		}
5258
+		$className = $this->_get_class_name();
5259
+		// check we actually found results that we can use to build our model object
5260
+		// if not, return null
5261
+		if ($this->has_primary_key_field()) {
5262
+			if (empty($this_model_fields_n_values[ $this->primary_key_name() ])) {
5263
+				return null;
5264
+			}
5265
+		} elseif ($this->unique_indexes()) {
5266
+			$first_column = reset($this_model_fields_n_values);
5267
+			if (empty($first_column)) {
5268
+				return null;
5269
+			}
5270
+		}
5271
+		// if there is no primary key or the object doesn't already exist in the entity map, then create a new instance
5272
+		if ($primary_key) {
5273
+			$classInstance = $this->get_from_entity_map($primary_key);
5274
+			if (! $classInstance) {
5275
+				$classInstance = EE_Registry::instance()
5276
+											->load_class(
5277
+												$className,
5278
+												array($this_model_fields_n_values, $this->_timezone),
5279
+												true,
5280
+												false
5281
+											);
5282
+				// add this new object to the entity map
5283
+				$classInstance = $this->add_to_entity_map($classInstance);
5284
+			}
5285
+		} else {
5286
+			$classInstance = EE_Registry::instance()
5287
+										->load_class(
5288
+											$className,
5289
+											array($this_model_fields_n_values, $this->_timezone),
5290
+											true,
5291
+											false
5292
+										);
5293
+		}
5294
+		return $classInstance;
5295
+	}
5296
+
5297
+
5298
+
5299
+	/**
5300
+	 * Gets the model object from the  entity map if it exists
5301
+	 *
5302
+	 * @param int|string $id the ID of the model object
5303
+	 * @return EE_Base_Class
5304
+	 */
5305
+	public function get_from_entity_map($id)
5306
+	{
5307
+		return isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])
5308
+			? $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] : null;
5309
+	}
5310
+
5311
+
5312
+
5313
+	/**
5314
+	 * add_to_entity_map
5315
+	 * Adds the object to the model's entity mappings
5316
+	 *        Effectively tells the models "Hey, this model object is the most up-to-date representation of the data,
5317
+	 *        and for the remainder of the request, it's even more up-to-date than what's in the database.
5318
+	 *        So, if the database doesn't agree with what's in the entity mapper, ignore the database"
5319
+	 *        If the database gets updated directly and you want the entity mapper to reflect that change,
5320
+	 *        then this method should be called immediately after the update query
5321
+	 * Note: The map is indexed by whatever the current blog id is set (via EEM_Base::$_model_query_blog_id).  This is
5322
+	 * so on multisite, the entity map is specific to the query being done for a specific site.
5323
+	 *
5324
+	 * @param    EE_Base_Class $object
5325
+	 * @throws EE_Error
5326
+	 * @return \EE_Base_Class
5327
+	 */
5328
+	public function add_to_entity_map(EE_Base_Class $object)
5329
+	{
5330
+		$className = $this->_get_class_name();
5331
+		if (! $object instanceof $className) {
5332
+			throw new EE_Error(sprintf(
5333
+				__("You tried adding a %s to a mapping of %ss", "event_espresso"),
5334
+				is_object($object) ? get_class($object) : $object,
5335
+				$className
5336
+			));
5337
+		}
5338
+		/** @var $object EE_Base_Class */
5339
+		if (! $object->ID()) {
5340
+			throw new EE_Error(sprintf(__(
5341
+				"You tried storing a model object with NO ID in the %s entity mapper.",
5342
+				"event_espresso"
5343
+			), get_class($this)));
5344
+		}
5345
+		// double check it's not already there
5346
+		$classInstance = $this->get_from_entity_map($object->ID());
5347
+		if ($classInstance) {
5348
+			return $classInstance;
5349
+		}
5350
+		$this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $object->ID() ] = $object;
5351
+		return $object;
5352
+	}
5353
+
5354
+
5355
+
5356
+	/**
5357
+	 * if a valid identifier is provided, then that entity is unset from the entity map,
5358
+	 * if no identifier is provided, then the entire entity map is emptied
5359
+	 *
5360
+	 * @param int|string $id the ID of the model object
5361
+	 * @return boolean
5362
+	 */
5363
+	public function clear_entity_map($id = null)
5364
+	{
5365
+		if (empty($id)) {
5366
+			$this->_entity_map[ EEM_Base::$_model_query_blog_id ] = array();
5367
+			return true;
5368
+		}
5369
+		if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
5370
+			unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
5371
+			return true;
5372
+		}
5373
+		return false;
5374
+	}
5375
+
5376
+
5377
+
5378
+	/**
5379
+	 * Public wrapper for _deduce_fields_n_values_from_cols_n_values.
5380
+	 * Given an array where keys are column (or column alias) names and values,
5381
+	 * returns an array of their corresponding field names and database values
5382
+	 *
5383
+	 * @param array $cols_n_values
5384
+	 * @return array
5385
+	 */
5386
+	public function deduce_fields_n_values_from_cols_n_values($cols_n_values)
5387
+	{
5388
+		return $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5389
+	}
5390
+
5391
+
5392
+
5393
+	/**
5394
+	 * _deduce_fields_n_values_from_cols_n_values
5395
+	 * Given an array where keys are column (or column alias) names and values,
5396
+	 * returns an array of their corresponding field names and database values
5397
+	 *
5398
+	 * @param string $cols_n_values
5399
+	 * @return array
5400
+	 */
5401
+	protected function _deduce_fields_n_values_from_cols_n_values($cols_n_values)
5402
+	{
5403
+		$this_model_fields_n_values = array();
5404
+		foreach ($this->get_tables() as $table_alias => $table_obj) {
5405
+			$table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5406
+				$cols_n_values,
5407
+				$table_obj->get_fully_qualified_pk_column(),
5408
+				$table_obj->get_pk_column()
5409
+			);
5410
+			// there is a primary key on this table and its not set. Use defaults for all its columns
5411
+			if ($table_pk_value === null && $table_obj->get_pk_column()) {
5412
+				foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5413
+					if (! $field_obj->is_db_only_field()) {
5414
+						// prepare field as if its coming from db
5415
+						$prepared_value = $field_obj->prepare_for_set($field_obj->get_default_value());
5416
+						$this_model_fields_n_values[ $field_name ] = $field_obj->prepare_for_use_in_db($prepared_value);
5417
+					}
5418
+				}
5419
+			} else {
5420
+				// the table's rows existed. Use their values
5421
+				foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5422
+					if (! $field_obj->is_db_only_field()) {
5423
+						$this_model_fields_n_values[ $field_name ] = $this->_get_column_value_with_table_alias_or_not(
5424
+							$cols_n_values,
5425
+							$field_obj->get_qualified_column(),
5426
+							$field_obj->get_table_column()
5427
+						);
5428
+					}
5429
+				}
5430
+			}
5431
+		}
5432
+		return $this_model_fields_n_values;
5433
+	}
5434
+
5435
+
5436
+	/**
5437
+	 * @param $cols_n_values
5438
+	 * @param $qualified_column
5439
+	 * @param $regular_column
5440
+	 * @return null
5441
+	 * @throws EE_Error
5442
+	 * @throws ReflectionException
5443
+	 */
5444
+	protected function _get_column_value_with_table_alias_or_not($cols_n_values, $qualified_column, $regular_column)
5445
+	{
5446
+		$value = null;
5447
+		// ask the field what it think it's table_name.column_name should be, and call it the "qualified column"
5448
+		// does the field on the model relate to this column retrieved from the db?
5449
+		// or is it a db-only field? (not relating to the model)
5450
+		if (isset($cols_n_values[ $qualified_column ])) {
5451
+			$value = $cols_n_values[ $qualified_column ];
5452
+		} elseif (isset($cols_n_values[ $regular_column ])) {
5453
+			$value = $cols_n_values[ $regular_column ];
5454
+		} elseif (! empty($this->foreign_key_aliases)) {
5455
+			// no PK?  ok check if there is a foreign key alias set for this table
5456
+			// then check if that alias exists in the incoming data
5457
+			// AND that the actual PK the $FK_alias represents matches the $qualified_column (full PK)
5458
+			foreach ($this->foreign_key_aliases as $FK_alias => $PK_column) {
5459
+				if ($PK_column === $qualified_column && isset($cols_n_values[ $FK_alias ])) {
5460
+					$value = $cols_n_values[ $FK_alias ];
5461
+					list($pk_class) = explode('.', $PK_column);
5462
+					$pk_model_name = "EEM_{$pk_class}";
5463
+					/** @var EEM_Base $pk_model */
5464
+					$pk_model = EE_Registry::instance()->load_model($pk_model_name);
5465
+					if ($pk_model instanceof EEM_Base) {
5466
+						// make sure object is pulled from db and added to entity map
5467
+						$pk_model->get_one_by_ID($value);
5468
+					}
5469
+					break;
5470
+				}
5471
+			}
5472
+		}
5473
+		return $value;
5474
+	}
5475
+
5476
+
5477
+
5478
+	/**
5479
+	 * refresh_entity_map_from_db
5480
+	 * Makes sure the model object in the entity map at $id assumes the values
5481
+	 * of the database (opposite of EE_base_Class::save())
5482
+	 *
5483
+	 * @param int|string $id
5484
+	 * @return EE_Base_Class
5485
+	 * @throws EE_Error
5486
+	 */
5487
+	public function refresh_entity_map_from_db($id)
5488
+	{
5489
+		$obj_in_map = $this->get_from_entity_map($id);
5490
+		if ($obj_in_map) {
5491
+			$wpdb_results = $this->_get_all_wpdb_results(
5492
+				array(array($this->get_primary_key_field()->get_name() => $id), 'limit' => 1)
5493
+			);
5494
+			if ($wpdb_results && is_array($wpdb_results)) {
5495
+				$one_row = reset($wpdb_results);
5496
+				foreach ($this->_deduce_fields_n_values_from_cols_n_values($one_row) as $field_name => $db_value) {
5497
+					$obj_in_map->set_from_db($field_name, $db_value);
5498
+				}
5499
+				// clear the cache of related model objects
5500
+				foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5501
+					$obj_in_map->clear_cache($relation_name, null, true);
5502
+				}
5503
+			}
5504
+			$this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] = $obj_in_map;
5505
+			return $obj_in_map;
5506
+		}
5507
+		return $this->get_one_by_ID($id);
5508
+	}
5509
+
5510
+
5511
+
5512
+	/**
5513
+	 * refresh_entity_map_with
5514
+	 * Leaves the entry in the entity map alone, but updates it to match the provided
5515
+	 * $replacing_model_obj (which we assume to be its equivalent but somehow NOT in the entity map).
5516
+	 * This is useful if you have a model object you want to make authoritative over what's in the entity map currently.
5517
+	 * Note: The old $replacing_model_obj should now be destroyed as it's now un-authoritative
5518
+	 *
5519
+	 * @param int|string    $id
5520
+	 * @param EE_Base_Class $replacing_model_obj
5521
+	 * @return \EE_Base_Class
5522
+	 * @throws EE_Error
5523
+	 */
5524
+	public function refresh_entity_map_with($id, $replacing_model_obj)
5525
+	{
5526
+		$obj_in_map = $this->get_from_entity_map($id);
5527
+		if ($obj_in_map) {
5528
+			if ($replacing_model_obj instanceof EE_Base_Class) {
5529
+				foreach ($replacing_model_obj->model_field_array() as $field_name => $value) {
5530
+					$obj_in_map->set($field_name, $value);
5531
+				}
5532
+				// make the model object in the entity map's cache match the $replacing_model_obj
5533
+				foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5534
+					$obj_in_map->clear_cache($relation_name, null, true);
5535
+					foreach ($replacing_model_obj->get_all_from_cache($relation_name) as $cache_id => $cached_obj) {
5536
+						$obj_in_map->cache($relation_name, $cached_obj, $cache_id);
5537
+					}
5538
+				}
5539
+			}
5540
+			return $obj_in_map;
5541
+		}
5542
+		$this->add_to_entity_map($replacing_model_obj);
5543
+		return $replacing_model_obj;
5544
+	}
5545
+
5546
+
5547
+
5548
+	/**
5549
+	 * Gets the EE class that corresponds to this model. Eg, for EEM_Answer that
5550
+	 * would be EE_Answer.To import that class, you'd just add ".class.php" to the name, like so
5551
+	 * require_once($this->_getClassName().".class.php");
5552
+	 *
5553
+	 * @return string
5554
+	 */
5555
+	private function _get_class_name()
5556
+	{
5557
+		return "EE_" . $this->get_this_model_name();
5558
+	}
5559
+
5560
+
5561
+
5562
+	/**
5563
+	 * Get the name of the items this model represents, for the quantity specified. Eg,
5564
+	 * if $quantity==1, on EEM_Event, it would 'Event' (internationalized), otherwise
5565
+	 * it would be 'Events'.
5566
+	 *
5567
+	 * @param int $quantity
5568
+	 * @return string
5569
+	 */
5570
+	public function item_name($quantity = 1)
5571
+	{
5572
+		return (int) $quantity === 1 ? $this->singular_item : $this->plural_item;
5573
+	}
5574
+
5575
+
5576
+
5577
+	/**
5578
+	 * Very handy general function to allow for plugins to extend any child of EE_TempBase.
5579
+	 * If a method is called on a child of EE_TempBase that doesn't exist, this function is called
5580
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments. Instead of
5581
+	 * requiring a plugin to extend the EE_TempBase (which works fine is there's only 1 plugin, but when will that
5582
+	 * happen?) they can add a hook onto 'filters_hook_espresso__{className}__{methodName}' (eg,
5583
+	 * filters_hook_espresso__EE_Answer__my_great_function) and accepts 2 arguments: the object on which the function
5584
+	 * was called, and an array of the original arguments passed to the function. Whatever their callback function
5585
+	 * returns will be returned by this function. Example: in functions.php (or in a plugin):
5586
+	 * add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3); function
5587
+	 * my_callback($previousReturnValue,EE_TempBase $object,$argsArray){
5588
+	 * $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
5589
+	 *        return $previousReturnValue.$returnString;
5590
+	 * }
5591
+	 * require('EEM_Answer.model.php');
5592
+	 * $answer=EEM_Answer::instance();
5593
+	 * echo $answer->my_callback('monkeys',100);
5594
+	 * //will output "you called my_callback! and passed args:monkeys,100"
5595
+	 *
5596
+	 * @param string $methodName name of method which was called on a child of EE_TempBase, but which
5597
+	 * @param array  $args       array of original arguments passed to the function
5598
+	 * @throws EE_Error
5599
+	 * @return mixed whatever the plugin which calls add_filter decides
5600
+	 */
5601
+	public function __call($methodName, $args)
5602
+	{
5603
+		$className = get_class($this);
5604
+		$tagName = "FHEE__{$className}__{$methodName}";
5605
+		if (! has_filter($tagName)) {
5606
+			throw new EE_Error(
5607
+				sprintf(
5608
+					__(
5609
+						'Method %1$s on model %2$s does not exist! You can create one with the following code in functions.php or in a plugin: %4$s function my_callback(%4$s \$previousReturnValue, EEM_Base \$object\ $argsArray=NULL ){%4$s     /*function body*/%4$s      return \$whatever;%4$s }%4$s add_filter( \'%3$s\', \'my_callback\', 10, 3 );',
5610
+						'event_espresso'
5611
+					),
5612
+					$methodName,
5613
+					$className,
5614
+					$tagName,
5615
+					'<br />'
5616
+				)
5617
+			);
5618
+		}
5619
+		return apply_filters($tagName, null, $this, $args);
5620
+	}
5621
+
5622
+
5623
+
5624
+	/**
5625
+	 * Ensures $base_class_obj_or_id is of the EE_Base_Class child that corresponds ot this model.
5626
+	 * If not, assumes its an ID, and uses $this->get_one_by_ID() to get the EE_Base_Class.
5627
+	 *
5628
+	 * @param EE_Base_Class|string|int $base_class_obj_or_id either:
5629
+	 *                                                       the EE_Base_Class object that corresponds to this Model,
5630
+	 *                                                       the object's class name
5631
+	 *                                                       or object's ID
5632
+	 * @param boolean                  $ensure_is_in_db      if set, we will also verify this model object
5633
+	 *                                                       exists in the database. If it does not, we add it
5634
+	 * @throws EE_Error
5635
+	 * @return EE_Base_Class
5636
+	 */
5637
+	public function ensure_is_obj($base_class_obj_or_id, $ensure_is_in_db = false)
5638
+	{
5639
+		$className = $this->_get_class_name();
5640
+		if ($base_class_obj_or_id instanceof $className) {
5641
+			$model_object = $base_class_obj_or_id;
5642
+		} else {
5643
+			$primary_key_field = $this->get_primary_key_field();
5644
+			if (
5645
+				$primary_key_field instanceof EE_Primary_Key_Int_Field
5646
+				&& (
5647
+					is_int($base_class_obj_or_id)
5648
+					|| is_string($base_class_obj_or_id)
5649
+				)
5650
+			) {
5651
+				// assume it's an ID.
5652
+				// either a proper integer or a string representing an integer (eg "101" instead of 101)
5653
+				$model_object = $this->get_one_by_ID($base_class_obj_or_id);
5654
+			} elseif (
5655
+				$primary_key_field instanceof EE_Primary_Key_String_Field
5656
+				&& is_string($base_class_obj_or_id)
5657
+			) {
5658
+				// assume its a string representation of the object
5659
+				$model_object = $this->get_one_by_ID($base_class_obj_or_id);
5660
+			} else {
5661
+				throw new EE_Error(
5662
+					sprintf(
5663
+						__(
5664
+							"'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5665
+							'event_espresso'
5666
+						),
5667
+						$base_class_obj_or_id,
5668
+						$this->_get_class_name(),
5669
+						print_r($base_class_obj_or_id, true)
5670
+					)
5671
+				);
5672
+			}
5673
+		}
5674
+		if ($ensure_is_in_db && $model_object->ID() !== null) {
5675
+			$model_object->save();
5676
+		}
5677
+		return $model_object;
5678
+	}
5679
+
5680
+
5681
+
5682
+	/**
5683
+	 * Similar to ensure_is_obj(), this method makes sure $base_class_obj_or_id
5684
+	 * is a value of the this model's primary key. If it's an EE_Base_Class child,
5685
+	 * returns it ID.
5686
+	 *
5687
+	 * @param EE_Base_Class|int|string $base_class_obj_or_id
5688
+	 * @return int|string depending on the type of this model object's ID
5689
+	 * @throws EE_Error
5690
+	 */
5691
+	public function ensure_is_ID($base_class_obj_or_id)
5692
+	{
5693
+		$className = $this->_get_class_name();
5694
+		if ($base_class_obj_or_id instanceof $className) {
5695
+			/** @var $base_class_obj_or_id EE_Base_Class */
5696
+			$id = $base_class_obj_or_id->ID();
5697
+		} elseif (is_int($base_class_obj_or_id)) {
5698
+			// assume it's an ID
5699
+			$id = $base_class_obj_or_id;
5700
+		} elseif (is_string($base_class_obj_or_id)) {
5701
+			// assume its a string representation of the object
5702
+			$id = $base_class_obj_or_id;
5703
+		} else {
5704
+			throw new EE_Error(sprintf(
5705
+				__(
5706
+					"'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5707
+					'event_espresso'
5708
+				),
5709
+				$base_class_obj_or_id,
5710
+				$this->_get_class_name(),
5711
+				print_r($base_class_obj_or_id, true)
5712
+			));
5713
+		}
5714
+		return $id;
5715
+	}
5716
+
5717
+
5718
+
5719
+	/**
5720
+	 * Sets whether the values passed to the model (eg, values in WHERE, values in INSERT, UPDATE, etc)
5721
+	 * have already been ran through the appropriate model field's prepare_for_use_in_db method. IE, they have
5722
+	 * been sanitized and converted into the appropriate domain.
5723
+	 * Usually the only place you'll want to change the default (which is to assume values have NOT been sanitized by
5724
+	 * the model object/model field) is when making a method call from WITHIN a model object, which has direct access
5725
+	 * to its sanitized values. Note: after changing this setting, you should set it back to its previous value (using
5726
+	 * get_assumption_concerning_values_already_prepared_by_model_object()) eg.
5727
+	 * $EVT = EEM_Event::instance(); $old_setting =
5728
+	 * $EVT->get_assumption_concerning_values_already_prepared_by_model_object();
5729
+	 * $EVT->assume_values_already_prepared_by_model_object(true);
5730
+	 * $EVT->update(array('foo'=>'bar'),array(array('foo'=>'monkey')));
5731
+	 * $EVT->assume_values_already_prepared_by_model_object($old_setting);
5732
+	 *
5733
+	 * @param int $values_already_prepared like one of the constants on EEM_Base
5734
+	 * @return void
5735
+	 */
5736
+	public function assume_values_already_prepared_by_model_object(
5737
+		$values_already_prepared = self::not_prepared_by_model_object
5738
+	) {
5739
+		$this->_values_already_prepared_by_model_object = $values_already_prepared;
5740
+	}
5741
+
5742
+
5743
+
5744
+	/**
5745
+	 * Read comments for assume_values_already_prepared_by_model_object()
5746
+	 *
5747
+	 * @return int
5748
+	 */
5749
+	public function get_assumption_concerning_values_already_prepared_by_model_object()
5750
+	{
5751
+		return $this->_values_already_prepared_by_model_object;
5752
+	}
5753
+
5754
+
5755
+
5756
+	/**
5757
+	 * Gets all the indexes on this model
5758
+	 *
5759
+	 * @return EE_Index[]
5760
+	 */
5761
+	public function indexes()
5762
+	{
5763
+		return $this->_indexes;
5764
+	}
5765
+
5766
+
5767
+
5768
+	/**
5769
+	 * Gets all the Unique Indexes on this model
5770
+	 *
5771
+	 * @return EE_Unique_Index[]
5772
+	 */
5773
+	public function unique_indexes()
5774
+	{
5775
+		$unique_indexes = array();
5776
+		foreach ($this->_indexes as $name => $index) {
5777
+			if ($index instanceof EE_Unique_Index) {
5778
+				$unique_indexes [ $name ] = $index;
5779
+			}
5780
+		}
5781
+		return $unique_indexes;
5782
+	}
5783
+
5784
+
5785
+
5786
+	/**
5787
+	 * Gets all the fields which, when combined, make the primary key.
5788
+	 * This is usually just an array with 1 element (the primary key), but in cases
5789
+	 * where there is no primary key, it's a combination of fields as defined
5790
+	 * on a primary index
5791
+	 *
5792
+	 * @return EE_Model_Field_Base[] indexed by the field's name
5793
+	 * @throws EE_Error
5794
+	 */
5795
+	public function get_combined_primary_key_fields()
5796
+	{
5797
+		foreach ($this->indexes() as $index) {
5798
+			if ($index instanceof EE_Primary_Key_Index) {
5799
+				return $index->fields();
5800
+			}
5801
+		}
5802
+		return array($this->primary_key_name() => $this->get_primary_key_field());
5803
+	}
5804
+
5805
+
5806
+
5807
+	/**
5808
+	 * Used to build a primary key string (when the model has no primary key),
5809
+	 * which can be used a unique string to identify this model object.
5810
+	 *
5811
+	 * @param array $fields_n_values keys are field names, values are their values.
5812
+	 *                               Note: if you have results from `EEM_Base::get_all_wpdb_results()`, you need to
5813
+	 *                               run it through `EEM_Base::deduce_fields_n_values_from_cols_n_values()`
5814
+	 *                               before passing it to this function (that will convert it from columns-n-values
5815
+	 *                               to field-names-n-values).
5816
+	 * @return string
5817
+	 * @throws EE_Error
5818
+	 */
5819
+	public function get_index_primary_key_string($fields_n_values)
5820
+	{
5821
+		$cols_n_values_for_primary_key_index = array_intersect_key(
5822
+			$fields_n_values,
5823
+			$this->get_combined_primary_key_fields()
5824
+		);
5825
+		return http_build_query($cols_n_values_for_primary_key_index);
5826
+	}
5827
+
5828
+
5829
+
5830
+	/**
5831
+	 * Gets the field values from the primary key string
5832
+	 *
5833
+	 * @see EEM_Base::get_combined_primary_key_fields() and EEM_Base::get_index_primary_key_string()
5834
+	 * @param string $index_primary_key_string
5835
+	 * @return null|array
5836
+	 * @throws EE_Error
5837
+	 */
5838
+	public function parse_index_primary_key_string($index_primary_key_string)
5839
+	{
5840
+		$key_fields = $this->get_combined_primary_key_fields();
5841
+		// check all of them are in the $id
5842
+		$key_vals_in_combined_pk = array();
5843
+		parse_str($index_primary_key_string, $key_vals_in_combined_pk);
5844
+		foreach ($key_fields as $key_field_name => $field_obj) {
5845
+			if (! isset($key_vals_in_combined_pk[ $key_field_name ])) {
5846
+				return null;
5847
+			}
5848
+		}
5849
+		return $key_vals_in_combined_pk;
5850
+	}
5851
+
5852
+
5853
+
5854
+	/**
5855
+	 * verifies that an array of key-value pairs for model fields has a key
5856
+	 * for each field comprising the primary key index
5857
+	 *
5858
+	 * @param array $key_vals
5859
+	 * @return boolean
5860
+	 * @throws EE_Error
5861
+	 */
5862
+	public function has_all_combined_primary_key_fields($key_vals)
5863
+	{
5864
+		$keys_it_should_have = array_keys($this->get_combined_primary_key_fields());
5865
+		foreach ($keys_it_should_have as $key) {
5866
+			if (! isset($key_vals[ $key ])) {
5867
+				return false;
5868
+			}
5869
+		}
5870
+		return true;
5871
+	}
5872
+
5873
+
5874
+
5875
+	/**
5876
+	 * Finds all model objects in the DB that appear to be a copy of $model_object_or_attributes_array.
5877
+	 * We consider something to be a copy if all the attributes match (except the ID, of course).
5878
+	 *
5879
+	 * @param array|EE_Base_Class $model_object_or_attributes_array If its an array, it's field-value pairs
5880
+	 * @param array               $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
5881
+	 * @throws EE_Error
5882
+	 * @return \EE_Base_Class[] Array keys are object IDs (if there is a primary key on the model. if not, numerically
5883
+	 *                                                              indexed)
5884
+	 */
5885
+	public function get_all_copies($model_object_or_attributes_array, $query_params = array())
5886
+	{
5887
+		if ($model_object_or_attributes_array instanceof EE_Base_Class) {
5888
+			$attributes_array = $model_object_or_attributes_array->model_field_array();
5889
+		} elseif (is_array($model_object_or_attributes_array)) {
5890
+			$attributes_array = $model_object_or_attributes_array;
5891
+		} else {
5892
+			throw new EE_Error(sprintf(__(
5893
+				"get_all_copies should be provided with either a model object or an array of field-value-pairs, but was given %s",
5894
+				"event_espresso"
5895
+			), $model_object_or_attributes_array));
5896
+		}
5897
+		// even copies obviously won't have the same ID, so remove the primary key
5898
+		// from the WHERE conditions for finding copies (if there is a primary key, of course)
5899
+		if ($this->has_primary_key_field() && isset($attributes_array[ $this->primary_key_name() ])) {
5900
+			unset($attributes_array[ $this->primary_key_name() ]);
5901
+		}
5902
+		if (isset($query_params[0])) {
5903
+			$query_params[0] = array_merge($attributes_array, $query_params);
5904
+		} else {
5905
+			$query_params[0] = $attributes_array;
5906
+		}
5907
+		return $this->get_all($query_params);
5908
+	}
5909
+
5910
+
5911
+
5912
+	/**
5913
+	 * Gets the first copy we find. See get_all_copies for more details
5914
+	 *
5915
+	 * @param       mixed EE_Base_Class | array        $model_object_or_attributes_array
5916
+	 * @param array $query_params
5917
+	 * @return EE_Base_Class
5918
+	 * @throws EE_Error
5919
+	 */
5920
+	public function get_one_copy($model_object_or_attributes_array, $query_params = array())
5921
+	{
5922
+		if (! is_array($query_params)) {
5923
+			EE_Error::doing_it_wrong(
5924
+				'EEM_Base::get_one_copy',
5925
+				sprintf(
5926
+					__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
5927
+					gettype($query_params)
5928
+				),
5929
+				'4.6.0'
5930
+			);
5931
+			$query_params = array();
5932
+		}
5933
+		$query_params['limit'] = 1;
5934
+		$copies = $this->get_all_copies($model_object_or_attributes_array, $query_params);
5935
+		if (is_array($copies)) {
5936
+			return array_shift($copies);
5937
+		}
5938
+		return null;
5939
+	}
5940
+
5941
+
5942
+
5943
+	/**
5944
+	 * Updates the item with the specified id. Ignores default query parameters because
5945
+	 * we have specified the ID, and its assumed we KNOW what we're doing
5946
+	 *
5947
+	 * @param array      $fields_n_values keys are field names, values are their new values
5948
+	 * @param int|string $id              the value of the primary key to update
5949
+	 * @return int number of rows updated
5950
+	 * @throws EE_Error
5951
+	 */
5952
+	public function update_by_ID($fields_n_values, $id)
5953
+	{
5954
+		$query_params = array(
5955
+			0                          => array($this->get_primary_key_field()->get_name() => $id),
5956
+			'default_where_conditions' => EEM_Base::default_where_conditions_others_only,
5957
+		);
5958
+		return $this->update($fields_n_values, $query_params);
5959
+	}
5960
+
5961
+
5962
+
5963
+	/**
5964
+	 * Changes an operator which was supplied to the models into one usable in SQL
5965
+	 *
5966
+	 * @param string $operator_supplied
5967
+	 * @return string an operator which can be used in SQL
5968
+	 * @throws EE_Error
5969
+	 */
5970
+	private function _prepare_operator_for_sql($operator_supplied)
5971
+	{
5972
+		$sql_operator = isset($this->_valid_operators[ $operator_supplied ]) ? $this->_valid_operators[ $operator_supplied ]
5973
+			: null;
5974
+		if ($sql_operator) {
5975
+			return $sql_operator;
5976
+		}
5977
+		throw new EE_Error(
5978
+			sprintf(
5979
+				__(
5980
+					"The operator '%s' is not in the list of valid operators: %s",
5981
+					"event_espresso"
5982
+				),
5983
+				$operator_supplied,
5984
+				implode(",", array_keys($this->_valid_operators))
5985
+			)
5986
+		);
5987
+	}
5988
+
5989
+
5990
+
5991
+	/**
5992
+	 * Gets the valid operators
5993
+	 * @return array keys are accepted strings, values are the SQL they are converted to
5994
+	 */
5995
+	public function valid_operators()
5996
+	{
5997
+		return $this->_valid_operators;
5998
+	}
5999
+
6000
+
6001
+
6002
+	/**
6003
+	 * Gets the between-style operators (take 2 arguments).
6004
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6005
+	 */
6006
+	public function valid_between_style_operators()
6007
+	{
6008
+		return array_intersect(
6009
+			$this->valid_operators(),
6010
+			$this->_between_style_operators
6011
+		);
6012
+	}
6013
+
6014
+	/**
6015
+	 * Gets the "like"-style operators (take a single argument, but it may contain wildcards)
6016
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6017
+	 */
6018
+	public function valid_like_style_operators()
6019
+	{
6020
+		return array_intersect(
6021
+			$this->valid_operators(),
6022
+			$this->_like_style_operators
6023
+		);
6024
+	}
6025
+
6026
+	/**
6027
+	 * Gets the "in"-style operators
6028
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6029
+	 */
6030
+	public function valid_in_style_operators()
6031
+	{
6032
+		return array_intersect(
6033
+			$this->valid_operators(),
6034
+			$this->_in_style_operators
6035
+		);
6036
+	}
6037
+
6038
+	/**
6039
+	 * Gets the "null"-style operators (accept no arguments)
6040
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6041
+	 */
6042
+	public function valid_null_style_operators()
6043
+	{
6044
+		return array_intersect(
6045
+			$this->valid_operators(),
6046
+			$this->_null_style_operators
6047
+		);
6048
+	}
6049
+
6050
+	/**
6051
+	 * Gets an array where keys are the primary keys and values are their 'names'
6052
+	 * (as determined by the model object's name() function, which is often overridden)
6053
+	 *
6054
+	 * @param array $query_params like get_all's
6055
+	 * @return string[]
6056
+	 * @throws EE_Error
6057
+	 */
6058
+	public function get_all_names($query_params = array())
6059
+	{
6060
+		$objs = $this->get_all($query_params);
6061
+		$names = array();
6062
+		foreach ($objs as $obj) {
6063
+			$names[ $obj->ID() ] = $obj->name();
6064
+		}
6065
+		return $names;
6066
+	}
6067
+
6068
+
6069
+
6070
+	/**
6071
+	 * Gets an array of primary keys from the model objects. If you acquired the model objects
6072
+	 * using EEM_Base::get_all() you don't need to call this (and probably shouldn't because
6073
+	 * this is duplicated effort and reduces efficiency) you would be better to use
6074
+	 * array_keys() on $model_objects.
6075
+	 *
6076
+	 * @param \EE_Base_Class[] $model_objects
6077
+	 * @param boolean          $filter_out_empty_ids if a model object has an ID of '' or 0, don't bother including it
6078
+	 *                                               in the returned array
6079
+	 * @return array
6080
+	 * @throws EE_Error
6081
+	 */
6082
+	public function get_IDs($model_objects, $filter_out_empty_ids = false)
6083
+	{
6084
+		if (! $this->has_primary_key_field()) {
6085
+			if (WP_DEBUG) {
6086
+				EE_Error::add_error(
6087
+					__('Trying to get IDs from a model than has no primary key', 'event_espresso'),
6088
+					__FILE__,
6089
+					__FUNCTION__,
6090
+					__LINE__
6091
+				);
6092
+			}
6093
+		}
6094
+		$IDs = array();
6095
+		foreach ($model_objects as $model_object) {
6096
+			$id = $model_object->ID();
6097
+			if (! $id) {
6098
+				if ($filter_out_empty_ids) {
6099
+					continue;
6100
+				}
6101
+				if (WP_DEBUG) {
6102
+					EE_Error::add_error(
6103
+						__(
6104
+							'Called %1$s on a model object that has no ID and so probably hasn\'t been saved to the database',
6105
+							'event_espresso'
6106
+						),
6107
+						__FILE__,
6108
+						__FUNCTION__,
6109
+						__LINE__
6110
+					);
6111
+				}
6112
+			}
6113
+			$IDs[] = $id;
6114
+		}
6115
+		return $IDs;
6116
+	}
6117
+
6118
+
6119
+
6120
+	/**
6121
+	 * Returns the string used in capabilities relating to this model. If there
6122
+	 * are no capabilities that relate to this model returns false
6123
+	 *
6124
+	 * @return string|false
6125
+	 */
6126
+	public function cap_slug()
6127
+	{
6128
+		return apply_filters('FHEE__EEM_Base__cap_slug', $this->_caps_slug, $this);
6129
+	}
6130
+
6131
+
6132
+
6133
+	/**
6134
+	 * Returns the capability-restrictions array (@see EEM_Base::_cap_restrictions).
6135
+	 * If $context is provided (which should be set to one of EEM_Base::valid_cap_contexts())
6136
+	 * only returns the cap restrictions array in that context (ie, the array
6137
+	 * at that key)
6138
+	 *
6139
+	 * @param string $context
6140
+	 * @return EE_Default_Where_Conditions[] indexed by associated capability
6141
+	 * @throws EE_Error
6142
+	 */
6143
+	public function cap_restrictions($context = EEM_Base::caps_read)
6144
+	{
6145
+		EEM_Base::verify_is_valid_cap_context($context);
6146
+		// check if we ought to run the restriction generator first
6147
+		if (
6148
+			isset($this->_cap_restriction_generators[ $context ])
6149
+			&& $this->_cap_restriction_generators[ $context ] instanceof EE_Restriction_Generator_Base
6150
+			&& ! $this->_cap_restriction_generators[ $context ]->has_generated_cap_restrictions()
6151
+		) {
6152
+			$this->_cap_restrictions[ $context ] = array_merge(
6153
+				$this->_cap_restrictions[ $context ],
6154
+				$this->_cap_restriction_generators[ $context ]->generate_restrictions()
6155
+			);
6156
+		}
6157
+		// and make sure we've finalized the construction of each restriction
6158
+		foreach ($this->_cap_restrictions[ $context ] as $where_conditions_obj) {
6159
+			if ($where_conditions_obj instanceof EE_Default_Where_Conditions) {
6160
+				$where_conditions_obj->_finalize_construct($this);
6161
+			}
6162
+		}
6163
+		return $this->_cap_restrictions[ $context ];
6164
+	}
6165
+
6166
+
6167
+
6168
+	/**
6169
+	 * Indicating whether or not this model thinks its a wp core model
6170
+	 *
6171
+	 * @return boolean
6172
+	 */
6173
+	public function is_wp_core_model()
6174
+	{
6175
+		return $this->_wp_core_model;
6176
+	}
6177
+
6178
+
6179
+
6180
+	/**
6181
+	 * Gets all the caps that are missing which impose a restriction on
6182
+	 * queries made in this context
6183
+	 *
6184
+	 * @param string $context one of EEM_Base::caps_ constants
6185
+	 * @return EE_Default_Where_Conditions[] indexed by capability name
6186
+	 * @throws EE_Error
6187
+	 */
6188
+	public function caps_missing($context = EEM_Base::caps_read)
6189
+	{
6190
+		$missing_caps = array();
6191
+		$cap_restrictions = $this->cap_restrictions($context);
6192
+		foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
6193
+			if (
6194
+				! EE_Capabilities::instance()
6195
+								 ->current_user_can($cap, $this->get_this_model_name() . '_model_applying_caps')
6196
+			) {
6197
+				$missing_caps[ $cap ] = $restriction_if_no_cap;
6198
+			}
6199
+		}
6200
+		return $missing_caps;
6201
+	}
6202
+
6203
+
6204
+
6205
+	/**
6206
+	 * Gets the mapping from capability contexts to action strings used in capability names
6207
+	 *
6208
+	 * @return array keys are one of EEM_Base::valid_cap_contexts(), and values are usually
6209
+	 * one of 'read', 'edit', or 'delete'
6210
+	 */
6211
+	public function cap_contexts_to_cap_action_map()
6212
+	{
6213
+		return apply_filters(
6214
+			'FHEE__EEM_Base__cap_contexts_to_cap_action_map',
6215
+			$this->_cap_contexts_to_cap_action_map,
6216
+			$this
6217
+		);
6218
+	}
6219
+
6220
+
6221
+
6222
+	/**
6223
+	 * Gets the action string for the specified capability context
6224
+	 *
6225
+	 * @param string $context
6226
+	 * @return string one of EEM_Base::cap_contexts_to_cap_action_map() values
6227
+	 * @throws EE_Error
6228
+	 */
6229
+	public function cap_action_for_context($context)
6230
+	{
6231
+		$mapping = $this->cap_contexts_to_cap_action_map();
6232
+		if (isset($mapping[ $context ])) {
6233
+			return $mapping[ $context ];
6234
+		}
6235
+		if ($action = apply_filters('FHEE__EEM_Base__cap_action_for_context', null, $this, $mapping, $context)) {
6236
+			return $action;
6237
+		}
6238
+		throw new EE_Error(
6239
+			sprintf(
6240
+				__('Cannot find capability restrictions for context "%1$s", allowed values are:%2$s', 'event_espresso'),
6241
+				$context,
6242
+				implode(',', array_keys($this->cap_contexts_to_cap_action_map()))
6243
+			)
6244
+		);
6245
+	}
6246
+
6247
+
6248
+
6249
+	/**
6250
+	 * Returns all the capability contexts which are valid when querying models
6251
+	 *
6252
+	 * @return array
6253
+	 */
6254
+	public static function valid_cap_contexts()
6255
+	{
6256
+		return apply_filters('FHEE__EEM_Base__valid_cap_contexts', array(
6257
+			self::caps_read,
6258
+			self::caps_read_admin,
6259
+			self::caps_edit,
6260
+			self::caps_delete,
6261
+		));
6262
+	}
6263
+
6264
+
6265
+
6266
+	/**
6267
+	 * Returns all valid options for 'default_where_conditions'
6268
+	 *
6269
+	 * @return array
6270
+	 */
6271
+	public static function valid_default_where_conditions()
6272
+	{
6273
+		return array(
6274
+			EEM_Base::default_where_conditions_all,
6275
+			EEM_Base::default_where_conditions_this_only,
6276
+			EEM_Base::default_where_conditions_others_only,
6277
+			EEM_Base::default_where_conditions_minimum_all,
6278
+			EEM_Base::default_where_conditions_minimum_others,
6279
+			EEM_Base::default_where_conditions_none
6280
+		);
6281
+	}
6282
+
6283
+	// public static function default_where_conditions_full
6284
+	/**
6285
+	 * Verifies $context is one of EEM_Base::valid_cap_contexts(), if not it throws an exception
6286
+	 *
6287
+	 * @param string $context
6288
+	 * @return bool
6289
+	 * @throws EE_Error
6290
+	 */
6291
+	public static function verify_is_valid_cap_context($context)
6292
+	{
6293
+		$valid_cap_contexts = EEM_Base::valid_cap_contexts();
6294
+		if (in_array($context, $valid_cap_contexts)) {
6295
+			return true;
6296
+		}
6297
+		throw new EE_Error(
6298
+			sprintf(
6299
+				__(
6300
+					'Context "%1$s" passed into model "%2$s" is not a valid context. They are: %3$s',
6301
+					'event_espresso'
6302
+				),
6303
+				$context,
6304
+				'EEM_Base',
6305
+				implode(',', $valid_cap_contexts)
6306
+			)
6307
+		);
6308
+	}
6309
+
6310
+
6311
+
6312
+	/**
6313
+	 * Clears all the models field caches. This is only useful when a sub-class
6314
+	 * might have added a field or something and these caches might be invalidated
6315
+	 */
6316
+	protected function _invalidate_field_caches()
6317
+	{
6318
+		$this->_cache_foreign_key_to_fields = array();
6319
+		$this->_cached_fields = null;
6320
+		$this->_cached_fields_non_db_only = null;
6321
+	}
6322
+
6323
+
6324
+
6325
+	/**
6326
+	 * Gets the list of all the where query param keys that relate to logic instead of field names
6327
+	 * (eg "and", "or", "not").
6328
+	 *
6329
+	 * @return array
6330
+	 */
6331
+	public function logic_query_param_keys()
6332
+	{
6333
+		return $this->_logic_query_param_keys;
6334
+	}
6335
+
6336
+
6337
+
6338
+	/**
6339
+	 * Determines whether or not the where query param array key is for a logic query param.
6340
+	 * Eg 'OR', 'not*', and 'and*because-i-say-so' should all return true, whereas
6341
+	 * 'ATT_fname', 'EVT_name*not-you-or-me', and 'ORG_name' should return false
6342
+	 *
6343
+	 * @param $query_param_key
6344
+	 * @return bool
6345
+	 */
6346
+	public function is_logic_query_param_key($query_param_key)
6347
+	{
6348
+		foreach ($this->logic_query_param_keys() as $logic_query_param_key) {
6349
+			if (
6350
+				$query_param_key === $logic_query_param_key
6351
+				|| strpos($query_param_key, $logic_query_param_key . '*') === 0
6352
+			) {
6353
+				return true;
6354
+			}
6355
+		}
6356
+		return false;
6357
+	}
6358
+
6359
+	/**
6360
+	 * Returns true if this model has a password field on it (regardless of whether that password field has any content)
6361
+	 * @since 4.9.74.p
6362
+	 * @return boolean
6363
+	 */
6364
+	public function hasPassword()
6365
+	{
6366
+		// if we don't yet know if there's a password field, find out and remember it for next time.
6367
+		if ($this->has_password_field === null) {
6368
+			$password_field = $this->getPasswordField();
6369
+			$this->has_password_field = $password_field instanceof EE_Password_Field ? true : false;
6370
+		}
6371
+		return $this->has_password_field;
6372
+	}
6373
+
6374
+	/**
6375
+	 * Returns the password field on this model, if there is one
6376
+	 * @since 4.9.74.p
6377
+	 * @return EE_Password_Field|null
6378
+	 */
6379
+	public function getPasswordField()
6380
+	{
6381
+		// if we definetely already know there is a password field or not (because has_password_field is true or false)
6382
+		// there's no need to search for it. If we don't know yet, then find out
6383
+		if ($this->has_password_field === null && $this->password_field === null) {
6384
+			$this->password_field = $this->get_a_field_of_type('EE_Password_Field');
6385
+		}
6386
+		// don't bother setting has_password_field because that's hasPassword()'s job.
6387
+		return $this->password_field;
6388
+	}
6389
+
6390
+
6391
+	/**
6392
+	 * Returns the list of field (as EE_Model_Field_Bases) that are protected by the password
6393
+	 * @since 4.9.74.p
6394
+	 * @return EE_Model_Field_Base[]
6395
+	 * @throws EE_Error
6396
+	 */
6397
+	public function getPasswordProtectedFields()
6398
+	{
6399
+		$password_field = $this->getPasswordField();
6400
+		$fields = array();
6401
+		if ($password_field instanceof EE_Password_Field) {
6402
+			$field_names = $password_field->protectedFields();
6403
+			foreach ($field_names as $field_name) {
6404
+				$fields[ $field_name ] = $this->field_settings_for($field_name);
6405
+			}
6406
+		}
6407
+		return $fields;
6408
+	}
6409
+
6410
+
6411
+	/**
6412
+	 * Checks if the current user can perform the requested action on this model
6413
+	 * @since 4.9.74.p
6414
+	 * @param string $cap_to_check one of the array keys from _cap_contexts_to_cap_action_map
6415
+	 * @param EE_Base_Class|array $model_obj_or_fields_n_values
6416
+	 * @return bool
6417
+	 * @throws EE_Error
6418
+	 * @throws InvalidArgumentException
6419
+	 * @throws InvalidDataTypeException
6420
+	 * @throws InvalidInterfaceException
6421
+	 * @throws ReflectionException
6422
+	 * @throws UnexpectedEntityException
6423
+	 */
6424
+	public function currentUserCan($cap_to_check, $model_obj_or_fields_n_values)
6425
+	{
6426
+		if ($model_obj_or_fields_n_values instanceof EE_Base_Class) {
6427
+			$model_obj_or_fields_n_values = $model_obj_or_fields_n_values->model_field_array();
6428
+		}
6429
+		if (!is_array($model_obj_or_fields_n_values)) {
6430
+			throw new UnexpectedEntityException(
6431
+				$model_obj_or_fields_n_values,
6432
+				'EE_Base_Class',
6433
+				sprintf(
6434
+					esc_html__('%1$s must be passed an `EE_Base_Class or an array of fields names with their values. You passed in something different.', 'event_espresso'),
6435
+					__FUNCTION__
6436
+				)
6437
+			);
6438
+		}
6439
+		return $this->exists(
6440
+			$this->alter_query_params_to_restrict_by_ID(
6441
+				$this->get_index_primary_key_string($model_obj_or_fields_n_values),
6442
+				array(
6443
+					'default_where_conditions' => 'none',
6444
+					'caps'                     => $cap_to_check,
6445
+				)
6446
+			)
6447
+		);
6448
+	}
6449
+
6450
+	/**
6451
+	 * Returns the query param where conditions key to the password affecting this model.
6452
+	 * Eg on EEM_Event this would just be "password", on EEM_Datetime this would be "Event.password", etc.
6453
+	 * @since 4.9.74.p
6454
+	 * @return null|string
6455
+	 * @throws EE_Error
6456
+	 * @throws InvalidArgumentException
6457
+	 * @throws InvalidDataTypeException
6458
+	 * @throws InvalidInterfaceException
6459
+	 * @throws ModelConfigurationException
6460
+	 * @throws ReflectionException
6461
+	 */
6462
+	public function modelChainAndPassword()
6463
+	{
6464
+		if ($this->model_chain_to_password === null) {
6465
+			throw new ModelConfigurationException(
6466
+				$this,
6467
+				esc_html_x(
6468
+				// @codingStandardsIgnoreStart
6469
+					'Cannot exclude protected data because the model has not specified which model has the password.',
6470
+					// @codingStandardsIgnoreEnd
6471
+					'1: model name',
6472
+					'event_espresso'
6473
+				)
6474
+			);
6475
+		}
6476
+		if ($this->model_chain_to_password === '') {
6477
+			$model_with_password = $this;
6478
+		} else {
6479
+			if ($pos_of_period = strrpos($this->model_chain_to_password, '.')) {
6480
+				$last_model_in_chain = substr($this->model_chain_to_password, $pos_of_period + 1);
6481
+			} else {
6482
+				$last_model_in_chain = $this->model_chain_to_password;
6483
+			}
6484
+			$model_with_password = EE_Registry::instance()->load_model($last_model_in_chain);
6485
+		}
6486
+
6487
+		$password_field = $model_with_password->getPasswordField();
6488
+		if ($password_field instanceof EE_Password_Field) {
6489
+			$password_field_name = $password_field->get_name();
6490
+		} else {
6491
+			throw new ModelConfigurationException(
6492
+				$this,
6493
+				sprintf(
6494
+					esc_html_x(
6495
+						'This model claims related model "%1$s" should have a password field on it, but none was found. The model relation chain is "%2$s"',
6496
+						'1: model name, 2: special string',
6497
+						'event_espresso'
6498
+					),
6499
+					$model_with_password->get_this_model_name(),
6500
+					$this->model_chain_to_password
6501
+				)
6502
+			);
6503
+		}
6504
+		return ($this->model_chain_to_password ? $this->model_chain_to_password . '.' : '') . $password_field_name;
6505
+	}
6506
+
6507
+	/**
6508
+	 * Returns true if there is a password on a related model which restricts access to some of this model's rows,
6509
+	 * or if this model itself has a password affecting access to some of its other fields.
6510
+	 * @since 4.9.74.p
6511
+	 * @return boolean
6512
+	 */
6513
+	public function restrictedByRelatedModelPassword()
6514
+	{
6515
+		return $this->model_chain_to_password !== null;
6516
+	}
6517 6517
 }
Please login to merge, or discard this patch.
Spacing   +227 added lines, -227 removed lines patch added patch discarded remove patch
@@ -554,7 +554,7 @@  discard block
 block discarded – undo
554 554
     protected function __construct($timezone = null)
555 555
     {
556 556
         // check that the model has not been loaded too soon
557
-        if (! did_action('AHEE__EE_System__load_espresso_addons')) {
557
+        if ( ! did_action('AHEE__EE_System__load_espresso_addons')) {
558 558
             throw new EE_Error(
559 559
                 sprintf(
560 560
                     __(
@@ -577,7 +577,7 @@  discard block
 block discarded – undo
577 577
          *
578 578
          * @var EE_Table_Base[] $_tables
579 579
          */
580
-        $this->_tables = (array) apply_filters('FHEE__' . get_class($this) . '__construct__tables', $this->_tables);
580
+        $this->_tables = (array) apply_filters('FHEE__'.get_class($this).'__construct__tables', $this->_tables);
581 581
         foreach ($this->_tables as $table_alias => $table_obj) {
582 582
             /** @var $table_obj EE_Table_Base */
583 583
             $table_obj->_construct_finalize_with_alias($table_alias);
@@ -592,10 +592,10 @@  discard block
 block discarded – undo
592 592
          *
593 593
          * @param EE_Model_Field_Base[] $_fields
594 594
          */
595
-        $this->_fields = (array) apply_filters('FHEE__' . get_class($this) . '__construct__fields', $this->_fields);
595
+        $this->_fields = (array) apply_filters('FHEE__'.get_class($this).'__construct__fields', $this->_fields);
596 596
         $this->_invalidate_field_caches();
597 597
         foreach ($this->_fields as $table_alias => $fields_for_table) {
598
-            if (! array_key_exists($table_alias, $this->_tables)) {
598
+            if ( ! array_key_exists($table_alias, $this->_tables)) {
599 599
                 throw new EE_Error(sprintf(__(
600 600
                     "Table alias %s does not exist in EEM_Base child's _tables array. Only tables defined are %s",
601 601
                     'event_espresso'
@@ -626,7 +626,7 @@  discard block
 block discarded – undo
626 626
          * @param EE_Model_Relation_Base[] $_model_relations
627 627
          */
628 628
         $this->_model_relations = (array) apply_filters(
629
-            'FHEE__' . get_class($this) . '__construct__model_relations',
629
+            'FHEE__'.get_class($this).'__construct__model_relations',
630 630
             $this->_model_relations
631 631
         );
632 632
         foreach ($this->_model_relations as $model_name => $relation_obj) {
@@ -639,12 +639,12 @@  discard block
 block discarded – undo
639 639
         }
640 640
         $this->set_timezone($timezone);
641 641
         // finalize default where condition strategy, or set default
642
-        if (! $this->_default_where_conditions_strategy) {
642
+        if ( ! $this->_default_where_conditions_strategy) {
643 643
             // nothing was set during child constructor, so set default
644 644
             $this->_default_where_conditions_strategy = new EE_Default_Where_Conditions();
645 645
         }
646 646
         $this->_default_where_conditions_strategy->_finalize_construct($this);
647
-        if (! $this->_minimum_where_conditions_strategy) {
647
+        if ( ! $this->_minimum_where_conditions_strategy) {
648 648
             // nothing was set during child constructor, so set default
649 649
             $this->_minimum_where_conditions_strategy = new EE_Default_Where_Conditions();
650 650
         }
@@ -657,8 +657,8 @@  discard block
 block discarded – undo
657 657
         // initialize the standard cap restriction generators if none were specified by the child constructor
658 658
         if ($this->_cap_restriction_generators !== false) {
659 659
             foreach ($this->cap_contexts_to_cap_action_map() as $cap_context => $action) {
660
-                if (! isset($this->_cap_restriction_generators[ $cap_context ])) {
661
-                    $this->_cap_restriction_generators[ $cap_context ] = apply_filters(
660
+                if ( ! isset($this->_cap_restriction_generators[$cap_context])) {
661
+                    $this->_cap_restriction_generators[$cap_context] = apply_filters(
662 662
                         'FHEE__EEM_Base___construct__standard_cap_restriction_generator',
663 663
                         new EE_Restriction_Generator_Protected(),
664 664
                         $cap_context,
@@ -670,10 +670,10 @@  discard block
 block discarded – undo
670 670
         // if there are cap restriction generators, use them to make the default cap restrictions
671 671
         if ($this->_cap_restriction_generators !== false) {
672 672
             foreach ($this->_cap_restriction_generators as $context => $generator_object) {
673
-                if (! $generator_object) {
673
+                if ( ! $generator_object) {
674 674
                     continue;
675 675
                 }
676
-                if (! $generator_object instanceof EE_Restriction_Generator_Base) {
676
+                if ( ! $generator_object instanceof EE_Restriction_Generator_Base) {
677 677
                     throw new EE_Error(
678 678
                         sprintf(
679 679
                             __(
@@ -686,12 +686,12 @@  discard block
 block discarded – undo
686 686
                     );
687 687
                 }
688 688
                 $action = $this->cap_action_for_context($context);
689
-                if (! $generator_object->construction_finalized()) {
689
+                if ( ! $generator_object->construction_finalized()) {
690 690
                     $generator_object->_construct_finalize($this, $action);
691 691
                 }
692 692
             }
693 693
         }
694
-        do_action('AHEE__' . get_class($this) . '__construct__end');
694
+        do_action('AHEE__'.get_class($this).'__construct__end');
695 695
     }
696 696
 
697 697
 
@@ -738,7 +738,7 @@  discard block
 block discarded – undo
738 738
     public static function instance($timezone = null)
739 739
     {
740 740
         // check if instance of Espresso_model already exists
741
-        if (! static::$_instance instanceof static) {
741
+        if ( ! static::$_instance instanceof static) {
742 742
             // instantiate Espresso_model
743 743
             static::$_instance = new static(
744 744
                 $timezone,
@@ -777,7 +777,7 @@  discard block
 block discarded – undo
777 777
             foreach ($r->getDefaultProperties() as $property => $value) {
778 778
                 // don't set instance to null like it was originally,
779 779
                 // but it's static anyways, and we're ignoring static properties (for now at least)
780
-                if (! isset($static_properties[ $property ])) {
780
+                if ( ! isset($static_properties[$property])) {
781 781
                     static::$_instance->{$property} = $value;
782 782
                 }
783 783
             }
@@ -801,7 +801,7 @@  discard block
 block discarded – undo
801 801
      */
802 802
     private static function getLoader()
803 803
     {
804
-        if (! EEM_Base::$loader instanceof LoaderInterface) {
804
+        if ( ! EEM_Base::$loader instanceof LoaderInterface) {
805 805
             EEM_Base::$loader = LoaderFactory::getLoader();
806 806
         }
807 807
         return EEM_Base::$loader;
@@ -821,7 +821,7 @@  discard block
 block discarded – undo
821 821
      */
822 822
     public function status_array($translated = false)
823 823
     {
824
-        if (! array_key_exists('Status', $this->_model_relations)) {
824
+        if ( ! array_key_exists('Status', $this->_model_relations)) {
825 825
             return array();
826 826
         }
827 827
         $model_name = $this->get_this_model_name();
@@ -829,7 +829,7 @@  discard block
 block discarded – undo
829 829
         $stati = EEM_Status::instance()->get_all(array(array('STS_type' => $status_type)));
830 830
         $status_array = array();
831 831
         foreach ($stati as $status) {
832
-            $status_array[ $status->ID() ] = $status->get('STS_code');
832
+            $status_array[$status->ID()] = $status->get('STS_code');
833 833
         }
834 834
         return $translated
835 835
             ? EEM_Status::instance()->localized_status($status_array, false, 'sentence')
@@ -890,7 +890,7 @@  discard block
 block discarded – undo
890 890
     {
891 891
         $wp_user_field_name = $this->wp_user_field_name();
892 892
         if ($wp_user_field_name) {
893
-            $query_params[0][ $wp_user_field_name ] = get_current_user_id();
893
+            $query_params[0][$wp_user_field_name] = get_current_user_id();
894 894
         }
895 895
         return $query_params;
896 896
     }
@@ -908,17 +908,17 @@  discard block
 block discarded – undo
908 908
     public function wp_user_field_name()
909 909
     {
910 910
         try {
911
-            if (! empty($this->_model_chain_to_wp_user)) {
911
+            if ( ! empty($this->_model_chain_to_wp_user)) {
912 912
                 $models_to_follow_to_wp_users = explode('.', $this->_model_chain_to_wp_user);
913 913
                 $last_model_name = end($models_to_follow_to_wp_users);
914 914
                 $model_with_fk_to_wp_users = EE_Registry::instance()->load_model($last_model_name);
915
-                $model_chain_to_wp_user = $this->_model_chain_to_wp_user . '.';
915
+                $model_chain_to_wp_user = $this->_model_chain_to_wp_user.'.';
916 916
             } else {
917 917
                 $model_with_fk_to_wp_users = $this;
918 918
                 $model_chain_to_wp_user = '';
919 919
             }
920 920
             $wp_user_field = $model_with_fk_to_wp_users->get_foreign_key_to('WP_User');
921
-            return $model_chain_to_wp_user . $wp_user_field->get_name();
921
+            return $model_chain_to_wp_user.$wp_user_field->get_name();
922 922
         } catch (EE_Error $e) {
923 923
             return false;
924 924
         }
@@ -995,11 +995,11 @@  discard block
 block discarded – undo
995 995
         if ($this->_custom_selections instanceof CustomSelects) {
996 996
             $custom_expressions = $this->_custom_selections->columnsToSelectExpression();
997 997
             $select_expressions .= $select_expressions
998
-                ? ', ' . $custom_expressions
998
+                ? ', '.$custom_expressions
999 999
                 : $custom_expressions;
1000 1000
         }
1001 1001
 
1002
-        $SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1002
+        $SQL = "SELECT $select_expressions ".$this->_construct_2nd_half_of_select_query($model_query_info);
1003 1003
         return $this->_do_wpdb_query('get_results', array($SQL, $output));
1004 1004
     }
1005 1005
 
@@ -1016,7 +1016,7 @@  discard block
 block discarded – undo
1016 1016
      */
1017 1017
     protected function getCustomSelection(array $query_params, $columns_to_select = null)
1018 1018
     {
1019
-        if (! isset($query_params['extra_selects']) && $columns_to_select === null) {
1019
+        if ( ! isset($query_params['extra_selects']) && $columns_to_select === null) {
1020 1020
             return null;
1021 1021
         }
1022 1022
         $selects = isset($query_params['extra_selects']) ? $query_params['extra_selects'] : $columns_to_select;
@@ -1065,7 +1065,7 @@  discard block
 block discarded – undo
1065 1065
         if (is_array($columns_to_select)) {
1066 1066
             $select_sql_array = array();
1067 1067
             foreach ($columns_to_select as $alias => $selection_and_datatype) {
1068
-                if (! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1068
+                if ( ! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1069 1069
                     throw new EE_Error(
1070 1070
                         sprintf(
1071 1071
                             __(
@@ -1077,7 +1077,7 @@  discard block
 block discarded – undo
1077 1077
                         )
1078 1078
                     );
1079 1079
                 }
1080
-                if (! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1080
+                if ( ! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1081 1081
                     throw new EE_Error(
1082 1082
                         sprintf(
1083 1083
                             esc_html__(
@@ -1156,12 +1156,12 @@  discard block
 block discarded – undo
1156 1156
      */
1157 1157
     public function alter_query_params_to_restrict_by_ID($id, $query_params = array())
1158 1158
     {
1159
-        if (! isset($query_params[0])) {
1159
+        if ( ! isset($query_params[0])) {
1160 1160
             $query_params[0] = array();
1161 1161
         }
1162 1162
         $conditions_from_id = $this->parse_index_primary_key_string($id);
1163 1163
         if ($conditions_from_id === null) {
1164
-            $query_params[0][ $this->primary_key_name() ] = $id;
1164
+            $query_params[0][$this->primary_key_name()] = $id;
1165 1165
         } else {
1166 1166
             // no primary key, so the $id must be from the get_index_primary_key_string()
1167 1167
             $query_params[0] = array_replace_recursive($query_params[0], $this->parse_index_primary_key_string($id));
@@ -1181,7 +1181,7 @@  discard block
 block discarded – undo
1181 1181
      */
1182 1182
     public function get_one($query_params = array())
1183 1183
     {
1184
-        if (! is_array($query_params)) {
1184
+        if ( ! is_array($query_params)) {
1185 1185
             EE_Error::doing_it_wrong(
1186 1186
                 'EEM_Base::get_one',
1187 1187
                 sprintf(
@@ -1379,7 +1379,7 @@  discard block
 block discarded – undo
1379 1379
                 return array();
1380 1380
             }
1381 1381
         }
1382
-        if (! is_array($query_params)) {
1382
+        if ( ! is_array($query_params)) {
1383 1383
             EE_Error::doing_it_wrong(
1384 1384
                 'EEM_Base::_get_consecutive',
1385 1385
                 sprintf(
@@ -1391,7 +1391,7 @@  discard block
 block discarded – undo
1391 1391
             $query_params = array();
1392 1392
         }
1393 1393
         // let's add the where query param for consecutive look up.
1394
-        $query_params[0][ $field_to_order_by ] = array($operand, $current_field_value);
1394
+        $query_params[0][$field_to_order_by] = array($operand, $current_field_value);
1395 1395
         $query_params['limit'] = $limit;
1396 1396
         // set direction
1397 1397
         $incoming_orderby = isset($query_params['order_by']) ? (array) $query_params['order_by'] : array();
@@ -1472,7 +1472,7 @@  discard block
 block discarded – undo
1472 1472
     {
1473 1473
         $field_settings = $this->field_settings_for($field_name);
1474 1474
         // if not a valid EE_Datetime_Field then throw error
1475
-        if (! $field_settings instanceof EE_Datetime_Field) {
1475
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1476 1476
             throw new EE_Error(sprintf(__(
1477 1477
                 'The field sent into EEM_Base::get_formats_for (%s) is not registered as a EE_Datetime_Field. Please check the spelling and make sure you are submitting the right field name to retrieve date_formats for.',
1478 1478
                 'event_espresso'
@@ -1621,7 +1621,7 @@  discard block
 block discarded – undo
1621 1621
      */
1622 1622
     public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
1623 1623
     {
1624
-        if (! is_array($query_params)) {
1624
+        if ( ! is_array($query_params)) {
1625 1625
             EE_Error::doing_it_wrong(
1626 1626
                 'EEM_Base::update',
1627 1627
                 sprintf(
@@ -1669,7 +1669,7 @@  discard block
 block discarded – undo
1669 1669
             $wpdb_result = (array) $wpdb_result;
1670 1670
             // get the model object's PK, as we'll want this if we need to insert a row into secondary tables
1671 1671
             if ($this->has_primary_key_field()) {
1672
-                $main_table_pk_value = $wpdb_result[ $this->get_primary_key_field()->get_qualified_column() ];
1672
+                $main_table_pk_value = $wpdb_result[$this->get_primary_key_field()->get_qualified_column()];
1673 1673
             } else {
1674 1674
                 // if there's no primary key, we basically can't support having a 2nd table on the model (we could but it would be lots of work)
1675 1675
                 $main_table_pk_value = null;
@@ -1685,7 +1685,7 @@  discard block
 block discarded – undo
1685 1685
                     // in this table, right? so insert a row in the current table, using any fields available
1686 1686
                     if (
1687 1687
                         ! (array_key_exists($this_table_pk_column, $wpdb_result)
1688
-                           && $wpdb_result[ $this_table_pk_column ])
1688
+                           && $wpdb_result[$this_table_pk_column])
1689 1689
                     ) {
1690 1690
                         $success = $this->_insert_into_specific_table(
1691 1691
                             $table_obj,
@@ -1693,7 +1693,7 @@  discard block
 block discarded – undo
1693 1693
                             $main_table_pk_value
1694 1694
                         );
1695 1695
                         // if we died here, report the error
1696
-                        if (! $success) {
1696
+                        if ( ! $success) {
1697 1697
                             return false;
1698 1698
                         }
1699 1699
                     }
@@ -1721,10 +1721,10 @@  discard block
 block discarded – undo
1721 1721
                 $model_objs_affected_ids = array();
1722 1722
                 foreach ($models_affected_key_columns as $row) {
1723 1723
                     $combined_index_key = $this->get_index_primary_key_string($row);
1724
-                    $model_objs_affected_ids[ $combined_index_key ] = $combined_index_key;
1724
+                    $model_objs_affected_ids[$combined_index_key] = $combined_index_key;
1725 1725
                 }
1726 1726
             }
1727
-            if (! $model_objs_affected_ids) {
1727
+            if ( ! $model_objs_affected_ids) {
1728 1728
                 // wait wait wait- if nothing was affected let's stop here
1729 1729
                 return 0;
1730 1730
             }
@@ -1751,7 +1751,7 @@  discard block
 block discarded – undo
1751 1751
                . $model_query_info->get_full_join_sql()
1752 1752
                . " SET "
1753 1753
                . $this->_construct_update_sql($fields_n_values)
1754
-               . $model_query_info->get_where_sql();// note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1754
+               . $model_query_info->get_where_sql(); // note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1755 1755
         $rows_affected = $this->_do_wpdb_query('query', array($SQL));
1756 1756
         /**
1757 1757
          * Action called after a model update call has been made.
@@ -1762,7 +1762,7 @@  discard block
 block discarded – undo
1762 1762
          * @param int      $rows_affected
1763 1763
          */
1764 1764
         do_action('AHEE__EEM_Base__update__end', $this, $fields_n_values, $query_params, $rows_affected);
1765
-        return $rows_affected;// how many supposedly got updated
1765
+        return $rows_affected; // how many supposedly got updated
1766 1766
     }
1767 1767
 
1768 1768
 
@@ -1790,7 +1790,7 @@  discard block
 block discarded – undo
1790 1790
         }
1791 1791
         $model_query_info = $this->_create_model_query_info_carrier($query_params);
1792 1792
         $select_expressions = $field->get_qualified_column();
1793
-        $SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1793
+        $SQL = "SELECT $select_expressions ".$this->_construct_2nd_half_of_select_query($model_query_info);
1794 1794
         return $this->_do_wpdb_query('get_col', array($SQL));
1795 1795
     }
1796 1796
 
@@ -1808,7 +1808,7 @@  discard block
 block discarded – undo
1808 1808
     {
1809 1809
         $query_params['limit'] = 1;
1810 1810
         $col = $this->get_col($query_params, $field_to_select);
1811
-        if (! empty($col)) {
1811
+        if ( ! empty($col)) {
1812 1812
             return reset($col);
1813 1813
         }
1814 1814
         return null;
@@ -1839,7 +1839,7 @@  discard block
 block discarded – undo
1839 1839
             $prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
1840 1840
             $value_sql = $prepared_value === null ? 'NULL'
1841 1841
                 : $wpdb->prepare($field_obj->get_wpdb_data_type(), $prepared_value);
1842
-            $cols_n_values[] = $field_obj->get_qualified_column() . "=" . $value_sql;
1842
+            $cols_n_values[] = $field_obj->get_qualified_column()."=".$value_sql;
1843 1843
         }
1844 1844
         return implode(",", $cols_n_values);
1845 1845
     }
@@ -1983,12 +1983,12 @@  discard block
 block discarded – undo
1983 1983
         if (
1984 1984
             $this->has_primary_key_field()
1985 1985
             && $rows_deleted !== false
1986
-            && isset($columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ])
1986
+            && isset($columns_and_ids_for_deleting[$this->get_primary_key_field()->get_qualified_column()])
1987 1987
         ) {
1988
-            $ids_for_removal = $columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ];
1988
+            $ids_for_removal = $columns_and_ids_for_deleting[$this->get_primary_key_field()->get_qualified_column()];
1989 1989
             foreach ($ids_for_removal as $id) {
1990
-                if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
1991
-                    unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
1990
+                if (isset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id])) {
1991
+                    unset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id]);
1992 1992
                 }
1993 1993
             }
1994 1994
 
@@ -2025,7 +2025,7 @@  discard block
 block discarded – undo
2025 2025
          * @param int      $rows_deleted
2026 2026
          */
2027 2027
         do_action('AHEE__EEM_Base__delete__end', $this, $query_params, $rows_deleted, $columns_and_ids_for_deleting);
2028
-        return $rows_deleted;// how many supposedly got deleted
2028
+        return $rows_deleted; // how many supposedly got deleted
2029 2029
     }
2030 2030
 
2031 2031
 
@@ -2119,15 +2119,15 @@  discard block
 block discarded – undo
2119 2119
                 if (
2120 2120
                     $allow_blocking
2121 2121
                     && $this->delete_is_blocked_by_related_models(
2122
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ]
2122
+                        $item_to_delete[$primary_table->get_fully_qualified_pk_column()]
2123 2123
                     )
2124 2124
                 ) {
2125 2125
                     continue;
2126 2126
                 }
2127 2127
                 // primary table deletes
2128
-                if (isset($item_to_delete[ $primary_table->get_fully_qualified_pk_column() ])) {
2129
-                    $ids_to_delete_indexed_by_column[ $primary_table->get_fully_qualified_pk_column() ][] =
2130
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ];
2128
+                if (isset($item_to_delete[$primary_table->get_fully_qualified_pk_column()])) {
2129
+                    $ids_to_delete_indexed_by_column[$primary_table->get_fully_qualified_pk_column()][] =
2130
+                        $item_to_delete[$primary_table->get_fully_qualified_pk_column()];
2131 2131
                 }
2132 2132
             }
2133 2133
         } elseif (count($this->get_combined_primary_key_fields()) > 1) {
@@ -2136,8 +2136,8 @@  discard block
 block discarded – undo
2136 2136
                 $ids_to_delete_indexed_by_column_for_row = array();
2137 2137
                 foreach ($fields as $cpk_field) {
2138 2138
                     if ($cpk_field instanceof EE_Model_Field_Base) {
2139
-                        $ids_to_delete_indexed_by_column_for_row[ $cpk_field->get_qualified_column() ] =
2140
-                            $item_to_delete[ $cpk_field->get_qualified_column() ];
2139
+                        $ids_to_delete_indexed_by_column_for_row[$cpk_field->get_qualified_column()] =
2140
+                            $item_to_delete[$cpk_field->get_qualified_column()];
2141 2141
                     }
2142 2142
                 }
2143 2143
                 $ids_to_delete_indexed_by_column[] = $ids_to_delete_indexed_by_column_for_row;
@@ -2175,7 +2175,7 @@  discard block
 block discarded – undo
2175 2175
         } elseif ($this->has_primary_key_field()) {
2176 2176
             $query = array();
2177 2177
             foreach ($ids_to_delete_indexed_by_column as $column => $ids) {
2178
-                $query[] = $column . ' IN' . $this->_construct_in_value($ids, $this->_primary_key_field);
2178
+                $query[] = $column.' IN'.$this->_construct_in_value($ids, $this->_primary_key_field);
2179 2179
             }
2180 2180
             $query_part = ! empty($query) ? implode(' AND ', $query) : $query_part;
2181 2181
         } elseif (count($this->get_combined_primary_key_fields()) > 1) {
@@ -2183,7 +2183,7 @@  discard block
 block discarded – undo
2183 2183
             foreach ($ids_to_delete_indexed_by_column as $ids_to_delete_indexed_by_column_for_each_row) {
2184 2184
                 $values_for_each_combined_primary_key_for_a_row = array();
2185 2185
                 foreach ($ids_to_delete_indexed_by_column_for_each_row as $column => $id) {
2186
-                    $values_for_each_combined_primary_key_for_a_row[] = $column . '=' . $id;
2186
+                    $values_for_each_combined_primary_key_for_a_row[] = $column.'='.$id;
2187 2187
                 }
2188 2188
                 $ways_to_identify_a_row[] = '('
2189 2189
                                             . implode(' AND ', $values_for_each_combined_primary_key_for_a_row)
@@ -2255,8 +2255,8 @@  discard block
 block discarded – undo
2255 2255
                 $column_to_count = '*';
2256 2256
             }
2257 2257
         }
2258
-        $column_to_count = $distinct ? "DISTINCT " . $column_to_count : $column_to_count;
2259
-        $SQL = "SELECT COUNT(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2258
+        $column_to_count = $distinct ? "DISTINCT ".$column_to_count : $column_to_count;
2259
+        $SQL = "SELECT COUNT(".$column_to_count.")".$this->_construct_2nd_half_of_select_query($model_query_info);
2260 2260
         return (int) $this->_do_wpdb_query('get_var', array($SQL));
2261 2261
     }
2262 2262
 
@@ -2279,7 +2279,7 @@  discard block
 block discarded – undo
2279 2279
             $field_obj = $this->get_primary_key_field();
2280 2280
         }
2281 2281
         $column_to_count = $field_obj->get_qualified_column();
2282
-        $SQL = "SELECT SUM(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2282
+        $SQL = "SELECT SUM(".$column_to_count.")".$this->_construct_2nd_half_of_select_query($model_query_info);
2283 2283
         $return_value = $this->_do_wpdb_query('get_var', array($SQL));
2284 2284
         $data_type = $field_obj->get_wpdb_data_type();
2285 2285
         if ($data_type === '%d' || $data_type === '%s') {
@@ -2306,7 +2306,7 @@  discard block
 block discarded – undo
2306 2306
         // if we're in maintenance mode level 2, DON'T run any queries
2307 2307
         // because level 2 indicates the database needs updating and
2308 2308
         // is probably out of sync with the code
2309
-        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
2309
+        if ( ! EE_Maintenance_Mode::instance()->models_can_query()) {
2310 2310
             throw new EE_Error(sprintf(__(
2311 2311
                 "Event Espresso Level 2 Maintenance mode is active. That means EE can not run ANY database queries until the necessary migration scripts have run which will take EE out of maintenance mode level 2. Please inform support of this error.",
2312 2312
                 "event_espresso"
@@ -2314,7 +2314,7 @@  discard block
 block discarded – undo
2314 2314
         }
2315 2315
         /** @type WPDB $wpdb */
2316 2316
         global $wpdb;
2317
-        if (! method_exists($wpdb, $wpdb_method)) {
2317
+        if ( ! method_exists($wpdb, $wpdb_method)) {
2318 2318
             throw new EE_Error(sprintf(__(
2319 2319
                 'There is no method named "%s" on Wordpress\' $wpdb object',
2320 2320
                 'event_espresso'
@@ -2328,7 +2328,7 @@  discard block
 block discarded – undo
2328 2328
         $this->show_db_query_if_previously_requested($wpdb->last_query);
2329 2329
         if (WP_DEBUG) {
2330 2330
             $wpdb->show_errors($old_show_errors_value);
2331
-            if (! empty($wpdb->last_error)) {
2331
+            if ( ! empty($wpdb->last_error)) {
2332 2332
                 throw new EE_Error(sprintf(__('WPDB Error: "%s"', 'event_espresso'), $wpdb->last_error));
2333 2333
             }
2334 2334
             if ($result === false) {
@@ -2394,7 +2394,7 @@  discard block
 block discarded – undo
2394 2394
                     return $result;
2395 2395
                     break;
2396 2396
             }
2397
-            if (! empty($error_message)) {
2397
+            if ( ! empty($error_message)) {
2398 2398
                 EE_Log::instance()->log(__FILE__, __FUNCTION__, $error_message, 'error');
2399 2399
                 trigger_error($error_message);
2400 2400
             }
@@ -2474,11 +2474,11 @@  discard block
 block discarded – undo
2474 2474
      */
2475 2475
     private function _construct_2nd_half_of_select_query(EE_Model_Query_Info_Carrier $model_query_info)
2476 2476
     {
2477
-        return " FROM " . $model_query_info->get_full_join_sql() .
2478
-               $model_query_info->get_where_sql() .
2479
-               $model_query_info->get_group_by_sql() .
2480
-               $model_query_info->get_having_sql() .
2481
-               $model_query_info->get_order_by_sql() .
2477
+        return " FROM ".$model_query_info->get_full_join_sql().
2478
+               $model_query_info->get_where_sql().
2479
+               $model_query_info->get_group_by_sql().
2480
+               $model_query_info->get_having_sql().
2481
+               $model_query_info->get_order_by_sql().
2482 2482
                $model_query_info->get_limit_sql();
2483 2483
     }
2484 2484
 
@@ -2674,12 +2674,12 @@  discard block
 block discarded – undo
2674 2674
         $related_model = $this->get_related_model_obj($model_name);
2675 2675
         // we're just going to use the query params on the related model's normal get_all query,
2676 2676
         // except add a condition to say to match the current mod
2677
-        if (! isset($query_params['default_where_conditions'])) {
2677
+        if ( ! isset($query_params['default_where_conditions'])) {
2678 2678
             $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2679 2679
         }
2680 2680
         $this_model_name = $this->get_this_model_name();
2681 2681
         $this_pk_field_name = $this->get_primary_key_field()->get_name();
2682
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2682
+        $query_params[0][$this_model_name.".".$this_pk_field_name] = $id_or_obj;
2683 2683
         return $related_model->count($query_params, $field_to_count, $distinct);
2684 2684
     }
2685 2685
 
@@ -2699,7 +2699,7 @@  discard block
 block discarded – undo
2699 2699
     public function sum_related($id_or_obj, $model_name, $query_params, $field_to_sum = null)
2700 2700
     {
2701 2701
         $related_model = $this->get_related_model_obj($model_name);
2702
-        if (! is_array($query_params)) {
2702
+        if ( ! is_array($query_params)) {
2703 2703
             EE_Error::doing_it_wrong(
2704 2704
                 'EEM_Base::sum_related',
2705 2705
                 sprintf(
@@ -2712,12 +2712,12 @@  discard block
 block discarded – undo
2712 2712
         }
2713 2713
         // we're just going to use the query params on the related model's normal get_all query,
2714 2714
         // except add a condition to say to match the current mod
2715
-        if (! isset($query_params['default_where_conditions'])) {
2715
+        if ( ! isset($query_params['default_where_conditions'])) {
2716 2716
             $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2717 2717
         }
2718 2718
         $this_model_name = $this->get_this_model_name();
2719 2719
         $this_pk_field_name = $this->get_primary_key_field()->get_name();
2720
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2720
+        $query_params[0][$this_model_name.".".$this_pk_field_name] = $id_or_obj;
2721 2721
         return $related_model->sum($query_params, $field_to_sum);
2722 2722
     }
2723 2723
 
@@ -2770,7 +2770,7 @@  discard block
 block discarded – undo
2770 2770
                 $field_with_model_name = $field;
2771 2771
             }
2772 2772
         }
2773
-        if (! isset($field_with_model_name) || ! $field_with_model_name) {
2773
+        if ( ! isset($field_with_model_name) || ! $field_with_model_name) {
2774 2774
             throw new EE_Error(sprintf(
2775 2775
                 __("There is no EE_Any_Foreign_Model_Name field on model %s", "event_espresso"),
2776 2776
                 $this->get_this_model_name()
@@ -2907,13 +2907,13 @@  discard block
 block discarded – undo
2907 2907
                 || $this->get_primary_key_field()
2908 2908
                    instanceof
2909 2909
                    EE_Primary_Key_String_Field)
2910
-            && isset($fields_n_values[ $this->primary_key_name() ])
2910
+            && isset($fields_n_values[$this->primary_key_name()])
2911 2911
         ) {
2912
-            $query_params[0]['OR'][ $this->primary_key_name() ] = $fields_n_values[ $this->primary_key_name() ];
2912
+            $query_params[0]['OR'][$this->primary_key_name()] = $fields_n_values[$this->primary_key_name()];
2913 2913
         }
2914 2914
         foreach ($this->unique_indexes() as $unique_index_name => $unique_index) {
2915 2915
             $uniqueness_where_params = array_intersect_key($fields_n_values, $unique_index->fields());
2916
-            $query_params[0]['OR'][ 'AND*' . $unique_index_name ] = $uniqueness_where_params;
2916
+            $query_params[0]['OR']['AND*'.$unique_index_name] = $uniqueness_where_params;
2917 2917
         }
2918 2918
         // if there is nothing to base this search on, then we shouldn't find anything
2919 2919
         if (empty($query_params)) {
@@ -2991,15 +2991,15 @@  discard block
 block discarded – undo
2991 2991
             $prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
2992 2992
             // if the value we want to assign it to is NULL, just don't mention it for the insertion
2993 2993
             if ($prepared_value !== null) {
2994
-                $insertion_col_n_values[ $field_obj->get_table_column() ] = $prepared_value;
2994
+                $insertion_col_n_values[$field_obj->get_table_column()] = $prepared_value;
2995 2995
                 $format_for_insertion[] = $field_obj->get_wpdb_data_type();
2996 2996
             }
2997 2997
         }
2998 2998
         if ($table instanceof EE_Secondary_Table && $new_id) {
2999 2999
             // its not the main table, so we should have already saved the main table's PK which we just inserted
3000 3000
             // so add the fk to the main table as a column
3001
-            $insertion_col_n_values[ $table->get_fk_on_table() ] = $new_id;
3002
-            $format_for_insertion[] = '%d';// yes right now we're only allowing these foreign keys to be INTs
3001
+            $insertion_col_n_values[$table->get_fk_on_table()] = $new_id;
3002
+            $format_for_insertion[] = '%d'; // yes right now we're only allowing these foreign keys to be INTs
3003 3003
         }
3004 3004
         // insert the new entry
3005 3005
         $result = $this->_do_wpdb_query(
@@ -3016,7 +3016,7 @@  discard block
 block discarded – undo
3016 3016
             }
3017 3017
             // it's not an auto-increment primary key, so
3018 3018
             // it must have been supplied
3019
-            return $fields_n_values[ $this->get_primary_key_field()->get_name() ];
3019
+            return $fields_n_values[$this->get_primary_key_field()->get_name()];
3020 3020
         }
3021 3021
         // we can't return a  primary key because there is none. instead return
3022 3022
         // a unique string indicating this model
@@ -3041,14 +3041,14 @@  discard block
 block discarded – undo
3041 3041
         if (
3042 3042
             ! $field_obj->is_nullable()
3043 3043
             && (
3044
-                ! isset($fields_n_values[ $field_obj->get_name() ])
3045
-                || $fields_n_values[ $field_obj->get_name() ] === null
3044
+                ! isset($fields_n_values[$field_obj->get_name()])
3045
+                || $fields_n_values[$field_obj->get_name()] === null
3046 3046
             )
3047 3047
         ) {
3048
-            $fields_n_values[ $field_obj->get_name() ] = $field_obj->get_default_value();
3048
+            $fields_n_values[$field_obj->get_name()] = $field_obj->get_default_value();
3049 3049
         }
3050
-        $unprepared_value = isset($fields_n_values[ $field_obj->get_name() ])
3051
-            ? $fields_n_values[ $field_obj->get_name() ]
3050
+        $unprepared_value = isset($fields_n_values[$field_obj->get_name()])
3051
+            ? $fields_n_values[$field_obj->get_name()]
3052 3052
             : null;
3053 3053
         return $this->_prepare_value_for_use_in_db($unprepared_value, $field_obj);
3054 3054
     }
@@ -3149,7 +3149,7 @@  discard block
 block discarded – undo
3149 3149
      */
3150 3150
     public function get_table_obj_by_alias($table_alias = '')
3151 3151
     {
3152
-        return isset($this->_tables[ $table_alias ]) ? $this->_tables[ $table_alias ] : null;
3152
+        return isset($this->_tables[$table_alias]) ? $this->_tables[$table_alias] : null;
3153 3153
     }
3154 3154
 
3155 3155
 
@@ -3164,7 +3164,7 @@  discard block
 block discarded – undo
3164 3164
         $other_tables = array();
3165 3165
         foreach ($this->_tables as $table_alias => $table) {
3166 3166
             if ($table instanceof EE_Secondary_Table) {
3167
-                $other_tables[ $table_alias ] = $table;
3167
+                $other_tables[$table_alias] = $table;
3168 3168
             }
3169 3169
         }
3170 3170
         return $other_tables;
@@ -3180,7 +3180,7 @@  discard block
 block discarded – undo
3180 3180
      */
3181 3181
     public function _get_fields_for_table($table_alias)
3182 3182
     {
3183
-        return $this->_fields[ $table_alias ];
3183
+        return $this->_fields[$table_alias];
3184 3184
     }
3185 3185
 
3186 3186
 
@@ -3209,7 +3209,7 @@  discard block
 block discarded – undo
3209 3209
                     $query_info_carrier,
3210 3210
                     'group_by'
3211 3211
                 );
3212
-            } elseif (! empty($query_params['group_by'])) {
3212
+            } elseif ( ! empty($query_params['group_by'])) {
3213 3213
                 $this->_extract_related_model_info_from_query_param(
3214 3214
                     $query_params['group_by'],
3215 3215
                     $query_info_carrier,
@@ -3231,7 +3231,7 @@  discard block
 block discarded – undo
3231 3231
                     $query_info_carrier,
3232 3232
                     'order_by'
3233 3233
                 );
3234
-            } elseif (! empty($query_params['order_by'])) {
3234
+            } elseif ( ! empty($query_params['order_by'])) {
3235 3235
                 $this->_extract_related_model_info_from_query_param(
3236 3236
                     $query_params['order_by'],
3237 3237
                     $query_info_carrier,
@@ -3266,7 +3266,7 @@  discard block
 block discarded – undo
3266 3266
         EE_Model_Query_Info_Carrier $model_query_info_carrier,
3267 3267
         $query_param_type
3268 3268
     ) {
3269
-        if (! empty($sub_query_params)) {
3269
+        if ( ! empty($sub_query_params)) {
3270 3270
             $sub_query_params = (array) $sub_query_params;
3271 3271
             foreach ($sub_query_params as $param => $possibly_array_of_params) {
3272 3272
                 // $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
@@ -3281,7 +3281,7 @@  discard block
 block discarded – undo
3281 3281
                 // of array('Registration.TXN_ID'=>23)
3282 3282
                 $query_param_sans_stars = $this->_remove_stars_and_anything_after_from_condition_query_param_key($param);
3283 3283
                 if (in_array($query_param_sans_stars, $this->_logic_query_param_keys, true)) {
3284
-                    if (! is_array($possibly_array_of_params)) {
3284
+                    if ( ! is_array($possibly_array_of_params)) {
3285 3285
                         throw new EE_Error(sprintf(
3286 3286
                             __(
3287 3287
                                 "You used a special where query param %s, but the value isn't an array of where query params, it's just %s'. It should be an array, eg array('EVT_ID'=>23,'OR'=>array('Venue.VNU_ID'=>32,'Venue.VNU_name'=>'monkey_land'))",
@@ -3305,7 +3305,7 @@  discard block
 block discarded – undo
3305 3305
                     // then $possible_array_of_params looks something like array('<','DTT_sold',true)
3306 3306
                     // indicating that $possible_array_of_params[1] is actually a field name,
3307 3307
                     // from which we should extract query parameters!
3308
-                    if (! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3308
+                    if ( ! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3309 3309
                         throw new EE_Error(sprintf(__(
3310 3310
                             "Improperly formed query parameter %s. It should be numerically indexed like array('<','DTT_sold',true); but you provided %s",
3311 3311
                             "event_espresso"
@@ -3339,8 +3339,8 @@  discard block
 block discarded – undo
3339 3339
         EE_Model_Query_Info_Carrier $model_query_info_carrier,
3340 3340
         $query_param_type
3341 3341
     ) {
3342
-        if (! empty($sub_query_params)) {
3343
-            if (! is_array($sub_query_params)) {
3342
+        if ( ! empty($sub_query_params)) {
3343
+            if ( ! is_array($sub_query_params)) {
3344 3344
                 throw new EE_Error(sprintf(
3345 3345
                     __("Query parameter %s should be an array, but it isn't.", "event_espresso"),
3346 3346
                     $sub_query_params
@@ -3374,7 +3374,7 @@  discard block
 block discarded – undo
3374 3374
      */
3375 3375
     public function _create_model_query_info_carrier($query_params)
3376 3376
     {
3377
-        if (! is_array($query_params)) {
3377
+        if ( ! is_array($query_params)) {
3378 3378
             EE_Error::doing_it_wrong(
3379 3379
                 'EEM_Base::_create_model_query_info_carrier',
3380 3380
                 sprintf(
@@ -3407,7 +3407,7 @@  discard block
 block discarded – undo
3407 3407
             // only include if related to a cpt where no password has been set
3408 3408
             $query_params[0]['OR*nopassword'] = array(
3409 3409
                 $where_param_key_for_password => '',
3410
-                $where_param_key_for_password . '*' => array('IS_NULL')
3410
+                $where_param_key_for_password.'*' => array('IS_NULL')
3411 3411
             );
3412 3412
         }
3413 3413
         $query_object = $this->_extract_related_models_from_query($query_params);
@@ -3461,7 +3461,7 @@  discard block
 block discarded – undo
3461 3461
         // set limit
3462 3462
         if (array_key_exists('limit', $query_params)) {
3463 3463
             if (is_array($query_params['limit'])) {
3464
-                if (! isset($query_params['limit'][0], $query_params['limit'][1])) {
3464
+                if ( ! isset($query_params['limit'][0], $query_params['limit'][1])) {
3465 3465
                     $e = sprintf(
3466 3466
                         __(
3467 3467
                             "Invalid DB query. You passed '%s' for the LIMIT, but only the following are valid: an integer, string representing an integer, a string like 'int,int', or an array like array(int,int)",
@@ -3469,12 +3469,12 @@  discard block
 block discarded – undo
3469 3469
                         ),
3470 3470
                         http_build_query($query_params['limit'])
3471 3471
                     );
3472
-                    throw new EE_Error($e . "|" . $e);
3472
+                    throw new EE_Error($e."|".$e);
3473 3473
                 }
3474 3474
                 // they passed us an array for the limit. Assume it's like array(50,25), meaning offset by 50, and get 25
3475
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit'][0] . "," . $query_params['limit'][1]);
3476
-            } elseif (! empty($query_params['limit'])) {
3477
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit']);
3475
+                $query_object->set_limit_sql(" LIMIT ".$query_params['limit'][0].",".$query_params['limit'][1]);
3476
+            } elseif ( ! empty($query_params['limit'])) {
3477
+                $query_object->set_limit_sql(" LIMIT ".$query_params['limit']);
3478 3478
             }
3479 3479
         }
3480 3480
         // set order by
@@ -3506,10 +3506,10 @@  discard block
 block discarded – undo
3506 3506
                 $order_array = array();
3507 3507
                 foreach ($query_params['order_by'] as $field_name_to_order_by => $order) {
3508 3508
                     $order = $this->_extract_order($order);
3509
-                    $order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by) . SP . $order;
3509
+                    $order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by).SP.$order;
3510 3510
                 }
3511
-                $query_object->set_order_by_sql(" ORDER BY " . implode(",", $order_array));
3512
-            } elseif (! empty($query_params['order_by'])) {
3511
+                $query_object->set_order_by_sql(" ORDER BY ".implode(",", $order_array));
3512
+            } elseif ( ! empty($query_params['order_by'])) {
3513 3513
                 $this->_extract_related_model_info_from_query_param(
3514 3514
                     $query_params['order_by'],
3515 3515
                     $query_object,
@@ -3520,7 +3520,7 @@  discard block
 block discarded – undo
3520 3520
                     ? $this->_extract_order($query_params['order'])
3521 3521
                     : 'DESC';
3522 3522
                 $query_object->set_order_by_sql(
3523
-                    " ORDER BY " . $this->_deduce_column_name_from_query_param($query_params['order_by']) . SP . $order
3523
+                    " ORDER BY ".$this->_deduce_column_name_from_query_param($query_params['order_by']).SP.$order
3524 3524
                 );
3525 3525
             }
3526 3526
         }
@@ -3532,7 +3532,7 @@  discard block
 block discarded – undo
3532 3532
         ) {
3533 3533
             $pk_field = $this->get_primary_key_field();
3534 3534
             $order = $this->_extract_order($query_params['order']);
3535
-            $query_object->set_order_by_sql(" ORDER BY " . $pk_field->get_qualified_column() . SP . $order);
3535
+            $query_object->set_order_by_sql(" ORDER BY ".$pk_field->get_qualified_column().SP.$order);
3536 3536
         }
3537 3537
         // set group by
3538 3538
         if (array_key_exists('group_by', $query_params)) {
@@ -3542,10 +3542,10 @@  discard block
 block discarded – undo
3542 3542
                 foreach ($query_params['group_by'] as $field_name_to_group_by) {
3543 3543
                     $group_by_array[] = $this->_deduce_column_name_from_query_param($field_name_to_group_by);
3544 3544
                 }
3545
-                $query_object->set_group_by_sql(" GROUP BY " . implode(", ", $group_by_array));
3546
-            } elseif (! empty($query_params['group_by'])) {
3545
+                $query_object->set_group_by_sql(" GROUP BY ".implode(", ", $group_by_array));
3546
+            } elseif ( ! empty($query_params['group_by'])) {
3547 3547
                 $query_object->set_group_by_sql(
3548
-                    " GROUP BY " . $this->_deduce_column_name_from_query_param($query_params['group_by'])
3548
+                    " GROUP BY ".$this->_deduce_column_name_from_query_param($query_params['group_by'])
3549 3549
                 );
3550 3550
             }
3551 3551
         }
@@ -3555,7 +3555,7 @@  discard block
 block discarded – undo
3555 3555
         }
3556 3556
         // now, just verify they didn't pass anything wack
3557 3557
         foreach ($query_params as $query_key => $query_value) {
3558
-            if (! in_array($query_key, $this->_allowed_query_params, true)) {
3558
+            if ( ! in_array($query_key, $this->_allowed_query_params, true)) {
3559 3559
                 throw new EE_Error(
3560 3560
                     sprintf(
3561 3561
                         __(
@@ -3663,7 +3663,7 @@  discard block
 block discarded – undo
3663 3663
         $where_query_params = array()
3664 3664
     ) {
3665 3665
         $allowed_used_default_where_conditions_values = EEM_Base::valid_default_where_conditions();
3666
-        if (! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3666
+        if ( ! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3667 3667
             throw new EE_Error(sprintf(
3668 3668
                 __(
3669 3669
                     "You passed an invalid value to the query parameter 'default_where_conditions' of '%s'. Allowed values are %s",
@@ -3795,19 +3795,19 @@  discard block
 block discarded – undo
3795 3795
     ) {
3796 3796
         $null_friendly_where_conditions = array();
3797 3797
         $none_overridden = true;
3798
-        $or_condition_key_for_defaults = 'OR*' . get_class($model);
3798
+        $or_condition_key_for_defaults = 'OR*'.get_class($model);
3799 3799
         foreach ($default_where_conditions as $key => $val) {
3800
-            if (isset($provided_where_conditions[ $key ])) {
3800
+            if (isset($provided_where_conditions[$key])) {
3801 3801
                 $none_overridden = false;
3802 3802
             } else {
3803
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ]['AND'][ $key ] = $val;
3803
+                $null_friendly_where_conditions[$or_condition_key_for_defaults]['AND'][$key] = $val;
3804 3804
             }
3805 3805
         }
3806 3806
         if ($none_overridden && $default_where_conditions) {
3807 3807
             if ($model->has_primary_key_field()) {
3808
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ][ $model_relation_path
3808
+                $null_friendly_where_conditions[$or_condition_key_for_defaults][$model_relation_path
3809 3809
                                                                                 . "."
3810
-                                                                                . $model->primary_key_name() ] = array('IS NULL');
3810
+                                                                                . $model->primary_key_name()] = array('IS NULL');
3811 3811
             }/*else{
3812 3812
                 //@todo NO PK, use other defaults
3813 3813
             }*/
@@ -3912,7 +3912,7 @@  discard block
 block discarded – undo
3912 3912
             foreach ($tables as $table_obj) {
3913 3913
                 $qualified_pk_column = $table_alias_with_model_relation_chain_prefix
3914 3914
                                        . $table_obj->get_fully_qualified_pk_column();
3915
-                if (! in_array($qualified_pk_column, $selects)) {
3915
+                if ( ! in_array($qualified_pk_column, $selects)) {
3916 3916
                     $selects[] = "$qualified_pk_column AS '$qualified_pk_column'";
3917 3917
                 }
3918 3918
             }
@@ -4064,9 +4064,9 @@  discard block
 block discarded – undo
4064 4064
         $query_parameter_type
4065 4065
     ) {
4066 4066
         foreach ($this->_model_relations as $valid_related_model_name => $relation_obj) {
4067
-            if (strpos($possible_join_string, $valid_related_model_name . ".") === 0) {
4067
+            if (strpos($possible_join_string, $valid_related_model_name.".") === 0) {
4068 4068
                 $this->_add_join_to_model($valid_related_model_name, $query_info_carrier, $original_query_param);
4069
-                $possible_join_string = substr($possible_join_string, strlen($valid_related_model_name . "."));
4069
+                $possible_join_string = substr($possible_join_string, strlen($valid_related_model_name."."));
4070 4070
                 if ($possible_join_string === '') {
4071 4071
                     // nothing left to $query_param
4072 4072
                     // we should actually end in a field name, not a model like this!
@@ -4197,7 +4197,7 @@  discard block
 block discarded – undo
4197 4197
     {
4198 4198
         $SQL = $this->_construct_condition_clause_recursive($where_params, ' AND ');
4199 4199
         if ($SQL) {
4200
-            return " WHERE " . $SQL;
4200
+            return " WHERE ".$SQL;
4201 4201
         }
4202 4202
         return '';
4203 4203
     }
@@ -4216,7 +4216,7 @@  discard block
 block discarded – undo
4216 4216
     {
4217 4217
         $SQL = $this->_construct_condition_clause_recursive($having_params, ' AND ');
4218 4218
         if ($SQL) {
4219
-            return " HAVING " . $SQL;
4219
+            return " HAVING ".$SQL;
4220 4220
         }
4221 4221
         return '';
4222 4222
     }
@@ -4235,7 +4235,7 @@  discard block
 block discarded – undo
4235 4235
     {
4236 4236
         $where_clauses = array();
4237 4237
         foreach ($where_params as $query_param => $op_and_value_or_sub_condition) {
4238
-            $query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);// str_replace("*",'',$query_param);
4238
+            $query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param); // str_replace("*",'',$query_param);
4239 4239
             if (in_array($query_param, $this->_logic_query_param_keys)) {
4240 4240
                 switch ($query_param) {
4241 4241
                     case 'not':
@@ -4269,7 +4269,7 @@  discard block
 block discarded – undo
4269 4269
             } else {
4270 4270
                 $field_obj = $this->_deduce_field_from_query_param($query_param);
4271 4271
                 // if it's not a normal field, maybe it's a custom selection?
4272
-                if (! $field_obj) {
4272
+                if ( ! $field_obj) {
4273 4273
                     if ($this->_custom_selections instanceof CustomSelects) {
4274 4274
                         $field_obj = $this->_custom_selections->getDataTypeForAlias($query_param);
4275 4275
                     } else {
@@ -4280,7 +4280,7 @@  discard block
 block discarded – undo
4280 4280
                     }
4281 4281
                 }
4282 4282
                 $op_and_value_sql = $this->_construct_op_and_value($op_and_value_or_sub_condition, $field_obj);
4283
-                $where_clauses[] = $this->_deduce_column_name_from_query_param($query_param) . SP . $op_and_value_sql;
4283
+                $where_clauses[] = $this->_deduce_column_name_from_query_param($query_param).SP.$op_and_value_sql;
4284 4284
             }
4285 4285
         }
4286 4286
         return $where_clauses ? implode($glue, $where_clauses) : '';
@@ -4303,7 +4303,7 @@  discard block
 block discarded – undo
4303 4303
                 $field->get_model_name(),
4304 4304
                 $query_param
4305 4305
             );
4306
-            return $table_alias_prefix . $field->get_qualified_column();
4306
+            return $table_alias_prefix.$field->get_qualified_column();
4307 4307
         }
4308 4308
         if (
4309 4309
             $this->_custom_selections instanceof CustomSelects
@@ -4363,7 +4363,7 @@  discard block
 block discarded – undo
4363 4363
     {
4364 4364
         if (is_array($op_and_value)) {
4365 4365
             $operator = isset($op_and_value[0]) ? $this->_prepare_operator_for_sql($op_and_value[0]) : null;
4366
-            if (! $operator) {
4366
+            if ( ! $operator) {
4367 4367
                 $php_array_like_string = array();
4368 4368
                 foreach ($op_and_value as $key => $value) {
4369 4369
                     $php_array_like_string[] = "$key=>$value";
@@ -4385,14 +4385,14 @@  discard block
 block discarded – undo
4385 4385
         }
4386 4386
         // check to see if the value is actually another field
4387 4387
         if (is_array($op_and_value) && isset($op_and_value[2]) && $op_and_value[2] == true) {
4388
-            return $operator . SP . $this->_deduce_column_name_from_query_param($value);
4388
+            return $operator.SP.$this->_deduce_column_name_from_query_param($value);
4389 4389
         }
4390 4390
         if (in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4391 4391
             // in this case, the value should be an array, or at least a comma-separated list
4392 4392
             // it will need to handle a little differently
4393 4393
             $cleaned_value = $this->_construct_in_value($value, $field_obj);
4394 4394
             // note: $cleaned_value has already been run through $wpdb->prepare()
4395
-            return $operator . SP . $cleaned_value;
4395
+            return $operator.SP.$cleaned_value;
4396 4396
         }
4397 4397
         if (in_array($operator, $this->valid_between_style_operators()) && is_array($value)) {
4398 4398
             // the value should be an array with count of two.
@@ -4408,7 +4408,7 @@  discard block
 block discarded – undo
4408 4408
                 );
4409 4409
             }
4410 4410
             $cleaned_value = $this->_construct_between_value($value, $field_obj);
4411
-            return $operator . SP . $cleaned_value;
4411
+            return $operator.SP.$cleaned_value;
4412 4412
         }
4413 4413
         if (in_array($operator, $this->valid_null_style_operators())) {
4414 4414
             if ($value !== null) {
@@ -4428,10 +4428,10 @@  discard block
 block discarded – undo
4428 4428
         if (in_array($operator, $this->valid_like_style_operators()) && ! is_array($value)) {
4429 4429
             // if the operator is 'LIKE', we want to allow percent signs (%) and not
4430 4430
             // remove other junk. So just treat it as a string.
4431
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, '%s');
4431
+            return $operator.SP.$this->_wpdb_prepare_using_field($value, '%s');
4432 4432
         }
4433
-        if (! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4434
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, $field_obj);
4433
+        if ( ! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4434
+            return $operator.SP.$this->_wpdb_prepare_using_field($value, $field_obj);
4435 4435
         }
4436 4436
         if (in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4437 4437
             throw new EE_Error(
@@ -4445,7 +4445,7 @@  discard block
 block discarded – undo
4445 4445
                 )
4446 4446
             );
4447 4447
         }
4448
-        if (! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4448
+        if ( ! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4449 4449
             throw new EE_Error(
4450 4450
                 sprintf(
4451 4451
                     __(
@@ -4485,7 +4485,7 @@  discard block
 block discarded – undo
4485 4485
         foreach ($values as $value) {
4486 4486
             $cleaned_values[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4487 4487
         }
4488
-        return $cleaned_values[0] . " AND " . $cleaned_values[1];
4488
+        return $cleaned_values[0]." AND ".$cleaned_values[1];
4489 4489
     }
4490 4490
 
4491 4491
 
@@ -4519,11 +4519,11 @@  discard block
 block discarded – undo
4519 4519
         // which is effectively equivalent to returning "()". We don't return "(0)" because that only works for auto-incrementing columns
4520 4520
         if (empty($prepped)) {
4521 4521
             $all_fields = $this->field_settings();
4522
-            $first_field    = reset($all_fields);
4522
+            $first_field = reset($all_fields);
4523 4523
             $main_table = $this->_get_main_table();
4524 4524
             $prepped[]  = "SELECT {$first_field->get_table_column()} FROM {$main_table->get_table_name()} WHERE FALSE";
4525 4525
         }
4526
-        return '(' . implode(',', $prepped) . ')';
4526
+        return '('.implode(',', $prepped).')';
4527 4527
     }
4528 4528
 
4529 4529
 
@@ -4544,7 +4544,7 @@  discard block
 block discarded – undo
4544 4544
                 $this->_prepare_value_for_use_in_db($value, $field_obj)
4545 4545
             );
4546 4546
         } //$field_obj should really just be a data type
4547
-        if (! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4547
+        if ( ! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4548 4548
             throw new EE_Error(
4549 4549
                 sprintf(
4550 4550
                     __("%s is not a valid wpdb datatype. Valid ones are %s", "event_espresso"),
@@ -4577,14 +4577,14 @@  discard block
 block discarded – undo
4577 4577
             ), $query_param_name));
4578 4578
         }
4579 4579
         $number_of_parts = count($query_param_parts);
4580
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
4580
+        $last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
4581 4581
         if ($number_of_parts === 1) {
4582 4582
             $field_name = $last_query_param_part;
4583 4583
             $model_obj = $this;
4584 4584
         } else {// $number_of_parts >= 2
4585 4585
             // the last part is the column name, and there are only 2parts. therefore...
4586 4586
             $field_name = $last_query_param_part;
4587
-            $model_obj = $this->get_related_model_obj($query_param_parts[ $number_of_parts - 2 ]);
4587
+            $model_obj = $this->get_related_model_obj($query_param_parts[$number_of_parts - 2]);
4588 4588
         }
4589 4589
         try {
4590 4590
             return $model_obj->field_settings_for($field_name);
@@ -4606,7 +4606,7 @@  discard block
 block discarded – undo
4606 4606
     public function _get_qualified_column_for_field($field_name)
4607 4607
     {
4608 4608
         $all_fields = $this->field_settings();
4609
-        $field = isset($all_fields[ $field_name ]) ? $all_fields[ $field_name ] : false;
4609
+        $field = isset($all_fields[$field_name]) ? $all_fields[$field_name] : false;
4610 4610
         if ($field) {
4611 4611
             return $field->get_qualified_column();
4612 4612
         }
@@ -4677,10 +4677,10 @@  discard block
 block discarded – undo
4677 4677
      */
4678 4678
     public function get_qualified_columns_for_all_fields($model_relation_chain = '', $return_string = true)
4679 4679
     {
4680
-        $table_prefix = str_replace('.', '__', $model_relation_chain) . (empty($model_relation_chain) ? '' : '__');
4680
+        $table_prefix = str_replace('.', '__', $model_relation_chain).(empty($model_relation_chain) ? '' : '__');
4681 4681
         $qualified_columns = array();
4682 4682
         foreach ($this->field_settings() as $field_name => $field) {
4683
-            $qualified_columns[] = $table_prefix . $field->get_qualified_column();
4683
+            $qualified_columns[] = $table_prefix.$field->get_qualified_column();
4684 4684
         }
4685 4685
         return $return_string ? implode(', ', $qualified_columns) : $qualified_columns;
4686 4686
     }
@@ -4704,11 +4704,11 @@  discard block
 block discarded – undo
4704 4704
             if ($table_obj instanceof EE_Primary_Table) {
4705 4705
                 $SQL .= $table_alias === $table_obj->get_table_alias()
4706 4706
                     ? $table_obj->get_select_join_limit($limit)
4707
-                    : SP . $table_obj->get_table_name() . " AS " . $table_obj->get_table_alias() . SP;
4707
+                    : SP.$table_obj->get_table_name()." AS ".$table_obj->get_table_alias().SP;
4708 4708
             } elseif ($table_obj instanceof EE_Secondary_Table) {
4709 4709
                 $SQL .= $table_alias === $table_obj->get_table_alias()
4710 4710
                     ? $table_obj->get_select_join_limit_join($limit)
4711
-                    : SP . $table_obj->get_join_sql($table_alias) . SP;
4711
+                    : SP.$table_obj->get_join_sql($table_alias).SP;
4712 4712
             }
4713 4713
         }
4714 4714
         return $SQL;
@@ -4780,7 +4780,7 @@  discard block
 block discarded – undo
4780 4780
         foreach ($this->field_settings() as $field_obj) {
4781 4781
             // $data_types[$field_obj->get_table_column()] = $field_obj->get_wpdb_data_type();
4782 4782
             /** @var $field_obj EE_Model_Field_Base */
4783
-            $data_types[ $field_obj->get_qualified_column() ] = $field_obj->get_wpdb_data_type();
4783
+            $data_types[$field_obj->get_qualified_column()] = $field_obj->get_wpdb_data_type();
4784 4784
         }
4785 4785
         return $data_types;
4786 4786
     }
@@ -4796,14 +4796,14 @@  discard block
 block discarded – undo
4796 4796
      */
4797 4797
     public function get_related_model_obj($model_name)
4798 4798
     {
4799
-        $model_classname = "EEM_" . $model_name;
4800
-        if (! class_exists($model_classname)) {
4799
+        $model_classname = "EEM_".$model_name;
4800
+        if ( ! class_exists($model_classname)) {
4801 4801
             throw new EE_Error(sprintf(__(
4802 4802
                 "You specified a related model named %s in your query. No such model exists, if it did, it would have the classname %s",
4803 4803
                 'event_espresso'
4804 4804
             ), $model_name, $model_classname));
4805 4805
         }
4806
-        return call_user_func($model_classname . "::instance");
4806
+        return call_user_func($model_classname."::instance");
4807 4807
     }
4808 4808
 
4809 4809
 
@@ -4832,7 +4832,7 @@  discard block
 block discarded – undo
4832 4832
         $belongs_to_relations = array();
4833 4833
         foreach ($this->relation_settings() as $model_name => $relation_obj) {
4834 4834
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
4835
-                $belongs_to_relations[ $model_name ] = $relation_obj;
4835
+                $belongs_to_relations[$model_name] = $relation_obj;
4836 4836
             }
4837 4837
         }
4838 4838
         return $belongs_to_relations;
@@ -4850,7 +4850,7 @@  discard block
 block discarded – undo
4850 4850
     public function related_settings_for($relation_name)
4851 4851
     {
4852 4852
         $relatedModels = $this->relation_settings();
4853
-        if (! array_key_exists($relation_name, $relatedModels)) {
4853
+        if ( ! array_key_exists($relation_name, $relatedModels)) {
4854 4854
             throw new EE_Error(
4855 4855
                 sprintf(
4856 4856
                     __(
@@ -4863,7 +4863,7 @@  discard block
 block discarded – undo
4863 4863
                 )
4864 4864
             );
4865 4865
         }
4866
-        return $relatedModels[ $relation_name ];
4866
+        return $relatedModels[$relation_name];
4867 4867
     }
4868 4868
 
4869 4869
 
@@ -4880,14 +4880,14 @@  discard block
 block discarded – undo
4880 4880
     public function field_settings_for($fieldName, $include_db_only_fields = true)
4881 4881
     {
4882 4882
         $fieldSettings = $this->field_settings($include_db_only_fields);
4883
-        if (! array_key_exists($fieldName, $fieldSettings)) {
4883
+        if ( ! array_key_exists($fieldName, $fieldSettings)) {
4884 4884
             throw new EE_Error(sprintf(
4885 4885
                 __("There is no field/column '%s' on '%s'", 'event_espresso'),
4886 4886
                 $fieldName,
4887 4887
                 get_class($this)
4888 4888
             ));
4889 4889
         }
4890
-        return $fieldSettings[ $fieldName ];
4890
+        return $fieldSettings[$fieldName];
4891 4891
     }
4892 4892
 
4893 4893
 
@@ -4901,7 +4901,7 @@  discard block
 block discarded – undo
4901 4901
     public function has_field($fieldName)
4902 4902
     {
4903 4903
         $fieldSettings = $this->field_settings(true);
4904
-        if (isset($fieldSettings[ $fieldName ])) {
4904
+        if (isset($fieldSettings[$fieldName])) {
4905 4905
             return true;
4906 4906
         }
4907 4907
         return false;
@@ -4918,7 +4918,7 @@  discard block
 block discarded – undo
4918 4918
     public function has_relation($relation_name)
4919 4919
     {
4920 4920
         $relations = $this->relation_settings();
4921
-        if (isset($relations[ $relation_name ])) {
4921
+        if (isset($relations[$relation_name])) {
4922 4922
             return true;
4923 4923
         }
4924 4924
         return false;
@@ -4956,7 +4956,7 @@  discard block
 block discarded – undo
4956 4956
                     break;
4957 4957
                 }
4958 4958
             }
4959
-            if (! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
4959
+            if ( ! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
4960 4960
                 throw new EE_Error(sprintf(
4961 4961
                     __("There is no Primary Key defined on model %s", 'event_espresso'),
4962 4962
                     get_class($this)
@@ -5017,24 +5017,24 @@  discard block
 block discarded – undo
5017 5017
      */
5018 5018
     public function get_foreign_key_to($model_name)
5019 5019
     {
5020
-        if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5020
+        if ( ! isset($this->_cache_foreign_key_to_fields[$model_name])) {
5021 5021
             foreach ($this->field_settings() as $field) {
5022 5022
                 if (
5023 5023
                     $field instanceof EE_Foreign_Key_Field_Base
5024 5024
                     && in_array($model_name, $field->get_model_names_pointed_to())
5025 5025
                 ) {
5026
-                    $this->_cache_foreign_key_to_fields[ $model_name ] = $field;
5026
+                    $this->_cache_foreign_key_to_fields[$model_name] = $field;
5027 5027
                     break;
5028 5028
                 }
5029 5029
             }
5030
-            if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5030
+            if ( ! isset($this->_cache_foreign_key_to_fields[$model_name])) {
5031 5031
                 throw new EE_Error(sprintf(__(
5032 5032
                     "There is no foreign key field pointing to model %s on model %s",
5033 5033
                     'event_espresso'
5034 5034
                 ), $model_name, get_class($this)));
5035 5035
             }
5036 5036
         }
5037
-        return $this->_cache_foreign_key_to_fields[ $model_name ];
5037
+        return $this->_cache_foreign_key_to_fields[$model_name];
5038 5038
     }
5039 5039
 
5040 5040
 
@@ -5050,7 +5050,7 @@  discard block
 block discarded – undo
5050 5050
     public function get_table_for_alias($table_alias)
5051 5051
     {
5052 5052
         $table_alias_sans_model_relation_chain_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($table_alias);
5053
-        return $this->_tables[ $table_alias_sans_model_relation_chain_prefix ]->get_table_name();
5053
+        return $this->_tables[$table_alias_sans_model_relation_chain_prefix]->get_table_name();
5054 5054
     }
5055 5055
 
5056 5056
 
@@ -5069,7 +5069,7 @@  discard block
 block discarded – undo
5069 5069
                 $this->_cached_fields = array();
5070 5070
                 foreach ($this->_fields as $fields_corresponding_to_table) {
5071 5071
                     foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5072
-                        $this->_cached_fields[ $field_name ] = $field_obj;
5072
+                        $this->_cached_fields[$field_name] = $field_obj;
5073 5073
                     }
5074 5074
                 }
5075 5075
             }
@@ -5080,8 +5080,8 @@  discard block
 block discarded – undo
5080 5080
             foreach ($this->_fields as $fields_corresponding_to_table) {
5081 5081
                 foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5082 5082
                     /** @var $field_obj EE_Model_Field_Base */
5083
-                    if (! $field_obj->is_db_only_field()) {
5084
-                        $this->_cached_fields_non_db_only[ $field_name ] = $field_obj;
5083
+                    if ( ! $field_obj->is_db_only_field()) {
5084
+                        $this->_cached_fields_non_db_only[$field_name] = $field_obj;
5085 5085
                     }
5086 5086
                 }
5087 5087
             }
@@ -5122,12 +5122,12 @@  discard block
 block discarded – undo
5122 5122
                     $primary_key_field->get_qualified_column(),
5123 5123
                     $primary_key_field->get_table_column()
5124 5124
                 );
5125
-                if ($table_pk_value && isset($array_of_objects[ $table_pk_value ])) {
5125
+                if ($table_pk_value && isset($array_of_objects[$table_pk_value])) {
5126 5126
                     continue;
5127 5127
                 }
5128 5128
             }
5129 5129
             $classInstance = $this->instantiate_class_from_array_or_object($row);
5130
-            if (! $classInstance) {
5130
+            if ( ! $classInstance) {
5131 5131
                 throw new EE_Error(
5132 5132
                     sprintf(
5133 5133
                         __('Could not create instance of class %s from row %s', 'event_espresso'),
@@ -5140,7 +5140,7 @@  discard block
 block discarded – undo
5140 5140
             $classInstance->set_timezone($this->_timezone);
5141 5141
             // make sure if there is any timezone setting present that we set the timezone for the object
5142 5142
             $key = $has_primary_key ? $classInstance->ID() : $count_if_model_has_no_primary_key++;
5143
-            $array_of_objects[ $key ] = $classInstance;
5143
+            $array_of_objects[$key] = $classInstance;
5144 5144
             // also, for all the relations of type BelongsTo, see if we can cache
5145 5145
             // those related models
5146 5146
             // (we could do this for other relations too, but if there are conditions
@@ -5184,9 +5184,9 @@  discard block
 block discarded – undo
5184 5184
         $results = array();
5185 5185
         if ($this->_custom_selections instanceof CustomSelects) {
5186 5186
             foreach ($this->_custom_selections->columnAliases() as $alias) {
5187
-                if (isset($db_results_row[ $alias ])) {
5188
-                    $results[ $alias ] = $this->convertValueToDataType(
5189
-                        $db_results_row[ $alias ],
5187
+                if (isset($db_results_row[$alias])) {
5188
+                    $results[$alias] = $this->convertValueToDataType(
5189
+                        $db_results_row[$alias],
5190 5190
                         $this->_custom_selections->getDataTypeForAlias($alias)
5191 5191
                     );
5192 5192
                 }
@@ -5228,7 +5228,7 @@  discard block
 block discarded – undo
5228 5228
         $this_model_fields_and_values = array();
5229 5229
         // setup the row using default values;
5230 5230
         foreach ($this->field_settings() as $field_name => $field_obj) {
5231
-            $this_model_fields_and_values[ $field_name ] = $field_obj->get_default_value();
5231
+            $this_model_fields_and_values[$field_name] = $field_obj->get_default_value();
5232 5232
         }
5233 5233
         $className = $this->_get_class_name();
5234 5234
         $classInstance = EE_Registry::instance()
@@ -5246,20 +5246,20 @@  discard block
 block discarded – undo
5246 5246
      */
5247 5247
     public function instantiate_class_from_array_or_object($cols_n_values)
5248 5248
     {
5249
-        if (! is_array($cols_n_values) && is_object($cols_n_values)) {
5249
+        if ( ! is_array($cols_n_values) && is_object($cols_n_values)) {
5250 5250
             $cols_n_values = get_object_vars($cols_n_values);
5251 5251
         }
5252 5252
         $primary_key = null;
5253 5253
         // make sure the array only has keys that are fields/columns on this model
5254 5254
         $this_model_fields_n_values = $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5255
-        if ($this->has_primary_key_field() && isset($this_model_fields_n_values[ $this->primary_key_name() ])) {
5256
-            $primary_key = $this_model_fields_n_values[ $this->primary_key_name() ];
5255
+        if ($this->has_primary_key_field() && isset($this_model_fields_n_values[$this->primary_key_name()])) {
5256
+            $primary_key = $this_model_fields_n_values[$this->primary_key_name()];
5257 5257
         }
5258 5258
         $className = $this->_get_class_name();
5259 5259
         // check we actually found results that we can use to build our model object
5260 5260
         // if not, return null
5261 5261
         if ($this->has_primary_key_field()) {
5262
-            if (empty($this_model_fields_n_values[ $this->primary_key_name() ])) {
5262
+            if (empty($this_model_fields_n_values[$this->primary_key_name()])) {
5263 5263
                 return null;
5264 5264
             }
5265 5265
         } elseif ($this->unique_indexes()) {
@@ -5271,7 +5271,7 @@  discard block
 block discarded – undo
5271 5271
         // if there is no primary key or the object doesn't already exist in the entity map, then create a new instance
5272 5272
         if ($primary_key) {
5273 5273
             $classInstance = $this->get_from_entity_map($primary_key);
5274
-            if (! $classInstance) {
5274
+            if ( ! $classInstance) {
5275 5275
                 $classInstance = EE_Registry::instance()
5276 5276
                                             ->load_class(
5277 5277
                                                 $className,
@@ -5304,8 +5304,8 @@  discard block
 block discarded – undo
5304 5304
      */
5305 5305
     public function get_from_entity_map($id)
5306 5306
     {
5307
-        return isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])
5308
-            ? $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] : null;
5307
+        return isset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id])
5308
+            ? $this->_entity_map[EEM_Base::$_model_query_blog_id][$id] : null;
5309 5309
     }
5310 5310
 
5311 5311
 
@@ -5328,7 +5328,7 @@  discard block
 block discarded – undo
5328 5328
     public function add_to_entity_map(EE_Base_Class $object)
5329 5329
     {
5330 5330
         $className = $this->_get_class_name();
5331
-        if (! $object instanceof $className) {
5331
+        if ( ! $object instanceof $className) {
5332 5332
             throw new EE_Error(sprintf(
5333 5333
                 __("You tried adding a %s to a mapping of %ss", "event_espresso"),
5334 5334
                 is_object($object) ? get_class($object) : $object,
@@ -5336,7 +5336,7 @@  discard block
 block discarded – undo
5336 5336
             ));
5337 5337
         }
5338 5338
         /** @var $object EE_Base_Class */
5339
-        if (! $object->ID()) {
5339
+        if ( ! $object->ID()) {
5340 5340
             throw new EE_Error(sprintf(__(
5341 5341
                 "You tried storing a model object with NO ID in the %s entity mapper.",
5342 5342
                 "event_espresso"
@@ -5347,7 +5347,7 @@  discard block
 block discarded – undo
5347 5347
         if ($classInstance) {
5348 5348
             return $classInstance;
5349 5349
         }
5350
-        $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $object->ID() ] = $object;
5350
+        $this->_entity_map[EEM_Base::$_model_query_blog_id][$object->ID()] = $object;
5351 5351
         return $object;
5352 5352
     }
5353 5353
 
@@ -5363,11 +5363,11 @@  discard block
 block discarded – undo
5363 5363
     public function clear_entity_map($id = null)
5364 5364
     {
5365 5365
         if (empty($id)) {
5366
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ] = array();
5366
+            $this->_entity_map[EEM_Base::$_model_query_blog_id] = array();
5367 5367
             return true;
5368 5368
         }
5369
-        if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
5370
-            unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
5369
+        if (isset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id])) {
5370
+            unset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id]);
5371 5371
             return true;
5372 5372
         }
5373 5373
         return false;
@@ -5410,17 +5410,17 @@  discard block
 block discarded – undo
5410 5410
             // there is a primary key on this table and its not set. Use defaults for all its columns
5411 5411
             if ($table_pk_value === null && $table_obj->get_pk_column()) {
5412 5412
                 foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5413
-                    if (! $field_obj->is_db_only_field()) {
5413
+                    if ( ! $field_obj->is_db_only_field()) {
5414 5414
                         // prepare field as if its coming from db
5415 5415
                         $prepared_value = $field_obj->prepare_for_set($field_obj->get_default_value());
5416
-                        $this_model_fields_n_values[ $field_name ] = $field_obj->prepare_for_use_in_db($prepared_value);
5416
+                        $this_model_fields_n_values[$field_name] = $field_obj->prepare_for_use_in_db($prepared_value);
5417 5417
                     }
5418 5418
                 }
5419 5419
             } else {
5420 5420
                 // the table's rows existed. Use their values
5421 5421
                 foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5422
-                    if (! $field_obj->is_db_only_field()) {
5423
-                        $this_model_fields_n_values[ $field_name ] = $this->_get_column_value_with_table_alias_or_not(
5422
+                    if ( ! $field_obj->is_db_only_field()) {
5423
+                        $this_model_fields_n_values[$field_name] = $this->_get_column_value_with_table_alias_or_not(
5424 5424
                             $cols_n_values,
5425 5425
                             $field_obj->get_qualified_column(),
5426 5426
                             $field_obj->get_table_column()
@@ -5447,17 +5447,17 @@  discard block
 block discarded – undo
5447 5447
         // ask the field what it think it's table_name.column_name should be, and call it the "qualified column"
5448 5448
         // does the field on the model relate to this column retrieved from the db?
5449 5449
         // or is it a db-only field? (not relating to the model)
5450
-        if (isset($cols_n_values[ $qualified_column ])) {
5451
-            $value = $cols_n_values[ $qualified_column ];
5452
-        } elseif (isset($cols_n_values[ $regular_column ])) {
5453
-            $value = $cols_n_values[ $regular_column ];
5454
-        } elseif (! empty($this->foreign_key_aliases)) {
5450
+        if (isset($cols_n_values[$qualified_column])) {
5451
+            $value = $cols_n_values[$qualified_column];
5452
+        } elseif (isset($cols_n_values[$regular_column])) {
5453
+            $value = $cols_n_values[$regular_column];
5454
+        } elseif ( ! empty($this->foreign_key_aliases)) {
5455 5455
             // no PK?  ok check if there is a foreign key alias set for this table
5456 5456
             // then check if that alias exists in the incoming data
5457 5457
             // AND that the actual PK the $FK_alias represents matches the $qualified_column (full PK)
5458 5458
             foreach ($this->foreign_key_aliases as $FK_alias => $PK_column) {
5459
-                if ($PK_column === $qualified_column && isset($cols_n_values[ $FK_alias ])) {
5460
-                    $value = $cols_n_values[ $FK_alias ];
5459
+                if ($PK_column === $qualified_column && isset($cols_n_values[$FK_alias])) {
5460
+                    $value = $cols_n_values[$FK_alias];
5461 5461
                     list($pk_class) = explode('.', $PK_column);
5462 5462
                     $pk_model_name = "EEM_{$pk_class}";
5463 5463
                     /** @var EEM_Base $pk_model */
@@ -5501,7 +5501,7 @@  discard block
 block discarded – undo
5501 5501
                     $obj_in_map->clear_cache($relation_name, null, true);
5502 5502
                 }
5503 5503
             }
5504
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] = $obj_in_map;
5504
+            $this->_entity_map[EEM_Base::$_model_query_blog_id][$id] = $obj_in_map;
5505 5505
             return $obj_in_map;
5506 5506
         }
5507 5507
         return $this->get_one_by_ID($id);
@@ -5554,7 +5554,7 @@  discard block
 block discarded – undo
5554 5554
      */
5555 5555
     private function _get_class_name()
5556 5556
     {
5557
-        return "EE_" . $this->get_this_model_name();
5557
+        return "EE_".$this->get_this_model_name();
5558 5558
     }
5559 5559
 
5560 5560
 
@@ -5602,7 +5602,7 @@  discard block
 block discarded – undo
5602 5602
     {
5603 5603
         $className = get_class($this);
5604 5604
         $tagName = "FHEE__{$className}__{$methodName}";
5605
-        if (! has_filter($tagName)) {
5605
+        if ( ! has_filter($tagName)) {
5606 5606
             throw new EE_Error(
5607 5607
                 sprintf(
5608 5608
                     __(
@@ -5775,7 +5775,7 @@  discard block
 block discarded – undo
5775 5775
         $unique_indexes = array();
5776 5776
         foreach ($this->_indexes as $name => $index) {
5777 5777
             if ($index instanceof EE_Unique_Index) {
5778
-                $unique_indexes [ $name ] = $index;
5778
+                $unique_indexes [$name] = $index;
5779 5779
             }
5780 5780
         }
5781 5781
         return $unique_indexes;
@@ -5842,7 +5842,7 @@  discard block
 block discarded – undo
5842 5842
         $key_vals_in_combined_pk = array();
5843 5843
         parse_str($index_primary_key_string, $key_vals_in_combined_pk);
5844 5844
         foreach ($key_fields as $key_field_name => $field_obj) {
5845
-            if (! isset($key_vals_in_combined_pk[ $key_field_name ])) {
5845
+            if ( ! isset($key_vals_in_combined_pk[$key_field_name])) {
5846 5846
                 return null;
5847 5847
             }
5848 5848
         }
@@ -5863,7 +5863,7 @@  discard block
 block discarded – undo
5863 5863
     {
5864 5864
         $keys_it_should_have = array_keys($this->get_combined_primary_key_fields());
5865 5865
         foreach ($keys_it_should_have as $key) {
5866
-            if (! isset($key_vals[ $key ])) {
5866
+            if ( ! isset($key_vals[$key])) {
5867 5867
                 return false;
5868 5868
             }
5869 5869
         }
@@ -5896,8 +5896,8 @@  discard block
 block discarded – undo
5896 5896
         }
5897 5897
         // even copies obviously won't have the same ID, so remove the primary key
5898 5898
         // from the WHERE conditions for finding copies (if there is a primary key, of course)
5899
-        if ($this->has_primary_key_field() && isset($attributes_array[ $this->primary_key_name() ])) {
5900
-            unset($attributes_array[ $this->primary_key_name() ]);
5899
+        if ($this->has_primary_key_field() && isset($attributes_array[$this->primary_key_name()])) {
5900
+            unset($attributes_array[$this->primary_key_name()]);
5901 5901
         }
5902 5902
         if (isset($query_params[0])) {
5903 5903
             $query_params[0] = array_merge($attributes_array, $query_params);
@@ -5919,7 +5919,7 @@  discard block
 block discarded – undo
5919 5919
      */
5920 5920
     public function get_one_copy($model_object_or_attributes_array, $query_params = array())
5921 5921
     {
5922
-        if (! is_array($query_params)) {
5922
+        if ( ! is_array($query_params)) {
5923 5923
             EE_Error::doing_it_wrong(
5924 5924
                 'EEM_Base::get_one_copy',
5925 5925
                 sprintf(
@@ -5969,7 +5969,7 @@  discard block
 block discarded – undo
5969 5969
      */
5970 5970
     private function _prepare_operator_for_sql($operator_supplied)
5971 5971
     {
5972
-        $sql_operator = isset($this->_valid_operators[ $operator_supplied ]) ? $this->_valid_operators[ $operator_supplied ]
5972
+        $sql_operator = isset($this->_valid_operators[$operator_supplied]) ? $this->_valid_operators[$operator_supplied]
5973 5973
             : null;
5974 5974
         if ($sql_operator) {
5975 5975
             return $sql_operator;
@@ -6060,7 +6060,7 @@  discard block
 block discarded – undo
6060 6060
         $objs = $this->get_all($query_params);
6061 6061
         $names = array();
6062 6062
         foreach ($objs as $obj) {
6063
-            $names[ $obj->ID() ] = $obj->name();
6063
+            $names[$obj->ID()] = $obj->name();
6064 6064
         }
6065 6065
         return $names;
6066 6066
     }
@@ -6081,7 +6081,7 @@  discard block
 block discarded – undo
6081 6081
      */
6082 6082
     public function get_IDs($model_objects, $filter_out_empty_ids = false)
6083 6083
     {
6084
-        if (! $this->has_primary_key_field()) {
6084
+        if ( ! $this->has_primary_key_field()) {
6085 6085
             if (WP_DEBUG) {
6086 6086
                 EE_Error::add_error(
6087 6087
                     __('Trying to get IDs from a model than has no primary key', 'event_espresso'),
@@ -6094,7 +6094,7 @@  discard block
 block discarded – undo
6094 6094
         $IDs = array();
6095 6095
         foreach ($model_objects as $model_object) {
6096 6096
             $id = $model_object->ID();
6097
-            if (! $id) {
6097
+            if ( ! $id) {
6098 6098
                 if ($filter_out_empty_ids) {
6099 6099
                     continue;
6100 6100
                 }
@@ -6145,22 +6145,22 @@  discard block
 block discarded – undo
6145 6145
         EEM_Base::verify_is_valid_cap_context($context);
6146 6146
         // check if we ought to run the restriction generator first
6147 6147
         if (
6148
-            isset($this->_cap_restriction_generators[ $context ])
6149
-            && $this->_cap_restriction_generators[ $context ] instanceof EE_Restriction_Generator_Base
6150
-            && ! $this->_cap_restriction_generators[ $context ]->has_generated_cap_restrictions()
6148
+            isset($this->_cap_restriction_generators[$context])
6149
+            && $this->_cap_restriction_generators[$context] instanceof EE_Restriction_Generator_Base
6150
+            && ! $this->_cap_restriction_generators[$context]->has_generated_cap_restrictions()
6151 6151
         ) {
6152
-            $this->_cap_restrictions[ $context ] = array_merge(
6153
-                $this->_cap_restrictions[ $context ],
6154
-                $this->_cap_restriction_generators[ $context ]->generate_restrictions()
6152
+            $this->_cap_restrictions[$context] = array_merge(
6153
+                $this->_cap_restrictions[$context],
6154
+                $this->_cap_restriction_generators[$context]->generate_restrictions()
6155 6155
             );
6156 6156
         }
6157 6157
         // and make sure we've finalized the construction of each restriction
6158
-        foreach ($this->_cap_restrictions[ $context ] as $where_conditions_obj) {
6158
+        foreach ($this->_cap_restrictions[$context] as $where_conditions_obj) {
6159 6159
             if ($where_conditions_obj instanceof EE_Default_Where_Conditions) {
6160 6160
                 $where_conditions_obj->_finalize_construct($this);
6161 6161
             }
6162 6162
         }
6163
-        return $this->_cap_restrictions[ $context ];
6163
+        return $this->_cap_restrictions[$context];
6164 6164
     }
6165 6165
 
6166 6166
 
@@ -6192,9 +6192,9 @@  discard block
 block discarded – undo
6192 6192
         foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
6193 6193
             if (
6194 6194
                 ! EE_Capabilities::instance()
6195
-                                 ->current_user_can($cap, $this->get_this_model_name() . '_model_applying_caps')
6195
+                                 ->current_user_can($cap, $this->get_this_model_name().'_model_applying_caps')
6196 6196
             ) {
6197
-                $missing_caps[ $cap ] = $restriction_if_no_cap;
6197
+                $missing_caps[$cap] = $restriction_if_no_cap;
6198 6198
             }
6199 6199
         }
6200 6200
         return $missing_caps;
@@ -6229,8 +6229,8 @@  discard block
 block discarded – undo
6229 6229
     public function cap_action_for_context($context)
6230 6230
     {
6231 6231
         $mapping = $this->cap_contexts_to_cap_action_map();
6232
-        if (isset($mapping[ $context ])) {
6233
-            return $mapping[ $context ];
6232
+        if (isset($mapping[$context])) {
6233
+            return $mapping[$context];
6234 6234
         }
6235 6235
         if ($action = apply_filters('FHEE__EEM_Base__cap_action_for_context', null, $this, $mapping, $context)) {
6236 6236
             return $action;
@@ -6348,7 +6348,7 @@  discard block
 block discarded – undo
6348 6348
         foreach ($this->logic_query_param_keys() as $logic_query_param_key) {
6349 6349
             if (
6350 6350
                 $query_param_key === $logic_query_param_key
6351
-                || strpos($query_param_key, $logic_query_param_key . '*') === 0
6351
+                || strpos($query_param_key, $logic_query_param_key.'*') === 0
6352 6352
             ) {
6353 6353
                 return true;
6354 6354
             }
@@ -6401,7 +6401,7 @@  discard block
 block discarded – undo
6401 6401
         if ($password_field instanceof EE_Password_Field) {
6402 6402
             $field_names = $password_field->protectedFields();
6403 6403
             foreach ($field_names as $field_name) {
6404
-                $fields[ $field_name ] = $this->field_settings_for($field_name);
6404
+                $fields[$field_name] = $this->field_settings_for($field_name);
6405 6405
             }
6406 6406
         }
6407 6407
         return $fields;
@@ -6426,7 +6426,7 @@  discard block
 block discarded – undo
6426 6426
         if ($model_obj_or_fields_n_values instanceof EE_Base_Class) {
6427 6427
             $model_obj_or_fields_n_values = $model_obj_or_fields_n_values->model_field_array();
6428 6428
         }
6429
-        if (!is_array($model_obj_or_fields_n_values)) {
6429
+        if ( ! is_array($model_obj_or_fields_n_values)) {
6430 6430
             throw new UnexpectedEntityException(
6431 6431
                 $model_obj_or_fields_n_values,
6432 6432
                 'EE_Base_Class',
@@ -6501,7 +6501,7 @@  discard block
 block discarded – undo
6501 6501
                 )
6502 6502
             );
6503 6503
         }
6504
-        return ($this->model_chain_to_password ? $this->model_chain_to_password . '.' : '') . $password_field_name;
6504
+        return ($this->model_chain_to_password ? $this->model_chain_to_password.'.' : '').$password_field_name;
6505 6505
     }
6506 6506
 
6507 6507
     /**
Please login to merge, or discard this patch.