Completed
Branch FET-8385-datetime-ticket-selec... (a3aa53)
by
unknown
44:55 queued 34:41
created
core/db_classes/EE_Event.class.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1207,7 +1207,7 @@
 block discarded – undo
1207 1207
      *
1208 1208
      * @access public
1209 1209
      * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1210
-     * @return mixed void|string
1210
+     * @return string void|string
1211 1211
      * @throws \EE_Error
1212 1212
      */
1213 1213
     public function pretty_active_status($echo = true)
Please login to merge, or discard this patch.
Indentation   +1276 added lines, -1276 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -14,1368 +14,1368 @@  discard block
 block discarded – undo
14 14
 class EE_Event extends EE_CPT_Base implements EEI_Line_Item_Object, EEI_Admin_Links, EEI_Has_Icon, EEI_Event
15 15
 {
16 16
 
17
-    /**
18
-     * cached value for the the logical active status for the event
19
-     *
20
-     * @see get_active_status()
21
-     * @var string
22
-     */
23
-    protected $_active_status = '';
24
-
25
-    /**
26
-     * This is just used for caching the Primary Datetime for the Event on initial retrieval
27
-     *
28
-     * @var EE_Datetime
29
-     */
30
-    protected $_Primary_Datetime;
31
-
32
-
33
-
34
-    /**
35
-     * @param array  $props_n_values          incoming values
36
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
37
-     *                                        used.)
38
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
39
-     *                                        date_format and the second value is the time format
40
-     * @return EE_Event
41
-     */
42
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
43
-    {
44
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
45
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
46
-    }
47
-
48
-
49
-
50
-    /**
51
-     * @param array  $props_n_values  incoming values from the database
52
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
53
-     *                                the website will be used.
54
-     * @return EE_Event
55
-     */
56
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
57
-    {
58
-        return new self($props_n_values, true, $timezone);
59
-    }
60
-
61
-
62
-
63
-    /**
64
-     * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
65
-     *
66
-     * @param string $field_name
67
-     * @param mixed  $field_value
68
-     * @param bool   $use_default
69
-     */
70
-    public function set($field_name, $field_value, $use_default = false)
71
-    {
72
-        switch ($field_name) {
73
-            case 'status' :
74
-                $this->set_status($field_value, $use_default);
75
-                break;
76
-            default :
77
-                parent::set($field_name, $field_value, $use_default);
78
-        }
79
-    }
80
-
81
-
82
-
83
-    /**
84
-     *    set_status
85
-     * Checks if event status is being changed to SOLD OUT
86
-     * and updates event meta data with previous event status
87
-     * so that we can revert things if/when the event is no longer sold out
88
-     *
89
-     * @access public
90
-     * @param string $new_status
91
-     * @param bool   $use_default
92
-     * @return bool|void
93
-     * @throws \EE_Error
94
-     */
95
-    public function set_status($new_status = null, $use_default = false)
96
-    {
97
-        // get current Event status
98
-        $old_status = $this->status();
99
-        // if status has changed
100
-        if ($old_status != $new_status) {
101
-            // TO sold_out
102
-            if ($new_status == EEM_Event::sold_out) {
103
-                // save the previous event status so that we can revert if the event is no longer sold out
104
-                $this->add_post_meta('_previous_event_status', $old_status);
105
-                do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
106
-                // OR FROM  sold_out
107
-            } else if ($old_status == EEM_Event::sold_out) {
108
-                $this->delete_post_meta('_previous_event_status');
109
-                do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
110
-            }
111
-            // update status
112
-            parent::set('status', $new_status, $use_default);
113
-            do_action('AHEE__EE_Event__set_status__after_update', $this);
114
-            return true;
115
-        } else {
116
-            // even though the old value matches the new value, it's still good to
117
-            // allow the parent set method to have a say
118
-            parent::set('status', $new_status, $use_default);
119
-            return true;
120
-        }
121
-    }
122
-
123
-
124
-
125
-    /**
126
-     * Gets all the datetimes for this event
127
-     *
128
-     * @param array $query_params like EEM_Base::get_all
129
-     * @return EE_Datetime[]
130
-     */
131
-    public function datetimes($query_params = array())
132
-    {
133
-        return $this->get_many_related('Datetime', $query_params);
134
-    }
135
-
136
-
137
-
138
-    /**
139
-     * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
140
-     *
141
-     * @return EE_Datetime[]
142
-     */
143
-    public function datetimes_in_chronological_order()
144
-    {
145
-        return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
146
-    }
147
-
148
-
149
-
150
-    /**
151
-     * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
152
-     * @darren, we should probably UNSET timezone on the EEM_Datetime model
153
-     * after running our query, so that this timezone isn't set for EVERY query
154
-     * on EEM_Datetime for the rest of the request, no?
155
-     *
156
-     * @param boolean $show_expired whether or not to include expired events
157
-     * @param boolean $show_deleted whether or not to include deleted events
158
-     * @param null    $limit
159
-     * @return \EE_Datetime[]
160
-     * @throws \EE_Error
161
-     */
162
-    public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
163
-    {
164
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
165
-            $this->ID(),
166
-            $show_expired,
167
-            $show_deleted,
168
-            $limit
169
-        );
170
-    }
171
-
172
-
173
-
174
-    /**
175
-     * Returns one related datetime. Mostly only used by some legacy code.
176
-     *
177
-     * @return EE_Datetime
178
-     */
179
-    public function first_datetime()
180
-    {
181
-        return $this->get_first_related('Datetime');
182
-    }
183
-
184
-
185
-
186
-    /**
187
-     * Returns the 'primary' datetime for the event
188
-     *
189
-     * @param bool $try_to_exclude_expired
190
-     * @param bool $try_to_exclude_deleted
191
-     * @return EE_Datetime
192
-     */
193
-    public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
194
-    {
195
-        if ( ! empty ($this->_Primary_Datetime)) {
196
-            return $this->_Primary_Datetime;
197
-        }
198
-        $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)
199
-                                               ->get_primary_datetime_for_event($this->ID(), $try_to_exclude_expired,
200
-                                                   $try_to_exclude_deleted);
201
-        return $this->_Primary_Datetime;
202
-    }
203
-
204
-
205
-
206
-    /**
207
-     * Gets all the tickets available for purchase of this event
208
-     *
209
-     * @param array $query_params like EEM_Base::get_all
210
-     * @return EE_Ticket[]
211
-     */
212
-    public function tickets($query_params = array())
213
-    {
214
-        //first get all datetimes
215
-        $datetimes = $this->datetimes_ordered();
216
-        if ( ! $datetimes) {
217
-            return array();
218
-        }
219
-        $datetime_ids = array();
220
-        foreach ($datetimes as $datetime) {
221
-            $datetime_ids[] = $datetime->ID();
222
-        }
223
-        $where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
224
-        //if incoming $query_params has where conditions let's merge but not override existing.
225
-        if (is_array($query_params) && isset($query_params[0])) {
226
-            $where_params = array_merge($query_params[0], $where_params);
227
-            unset($query_params[0]);
228
-        }
229
-        //now add $where_params to $query_params
230
-        $query_params[0] = $where_params;
231
-        return EEM_Ticket::instance()->get_all($query_params);
232
-    }
233
-
234
-
235
-
236
-    /**
237
-     * @return bool
238
-     */
239
-    public function additional_limit()
240
-    {
241
-        return $this->get('EVT_additional_limit');
242
-    }
243
-
244
-
245
-
246
-    /**
247
-     * @return bool
248
-     */
249
-    public function allow_overflow()
250
-    {
251
-        return $this->get('EVT_allow_overflow');
252
-    }
253
-
254
-
255
-
256
-    /**
257
-     * @return bool
258
-     */
259
-    public function created()
260
-    {
261
-        return $this->get('EVT_created');
262
-    }
263
-
264
-
265
-
266
-    /**
267
-     * @return bool
268
-     */
269
-    public function description()
270
-    {
271
-        return $this->get('EVT_desc');
272
-    }
273
-
274
-
275
-
276
-    /**
277
-     * Runs do_shortcode and wpautop on the description
278
-     *
279
-     * @return string of html
280
-     */
281
-    public function description_filtered()
282
-    {
283
-        return $this->get_pretty('EVT_desc');
284
-    }
285
-
286
-
287
-
288
-    /**
289
-     * @return bool
290
-     */
291
-    public function display_description()
292
-    {
293
-        return $this->get('EVT_display_desc');
294
-    }
295
-
296
-
297
-
298
-    /**
299
-     * @return bool
300
-     */
301
-    public function display_ticket_selector()
302
-    {
303
-        return (bool)$this->get('EVT_display_ticket_selector');
304
-    }
305
-
17
+	/**
18
+	 * cached value for the the logical active status for the event
19
+	 *
20
+	 * @see get_active_status()
21
+	 * @var string
22
+	 */
23
+	protected $_active_status = '';
24
+
25
+	/**
26
+	 * This is just used for caching the Primary Datetime for the Event on initial retrieval
27
+	 *
28
+	 * @var EE_Datetime
29
+	 */
30
+	protected $_Primary_Datetime;
31
+
32
+
33
+
34
+	/**
35
+	 * @param array  $props_n_values          incoming values
36
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
37
+	 *                                        used.)
38
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
39
+	 *                                        date_format and the second value is the time format
40
+	 * @return EE_Event
41
+	 */
42
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
43
+	{
44
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
45
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
46
+	}
47
+
48
+
49
+
50
+	/**
51
+	 * @param array  $props_n_values  incoming values from the database
52
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
53
+	 *                                the website will be used.
54
+	 * @return EE_Event
55
+	 */
56
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
57
+	{
58
+		return new self($props_n_values, true, $timezone);
59
+	}
60
+
61
+
62
+
63
+	/**
64
+	 * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
65
+	 *
66
+	 * @param string $field_name
67
+	 * @param mixed  $field_value
68
+	 * @param bool   $use_default
69
+	 */
70
+	public function set($field_name, $field_value, $use_default = false)
71
+	{
72
+		switch ($field_name) {
73
+			case 'status' :
74
+				$this->set_status($field_value, $use_default);
75
+				break;
76
+			default :
77
+				parent::set($field_name, $field_value, $use_default);
78
+		}
79
+	}
80
+
81
+
82
+
83
+	/**
84
+	 *    set_status
85
+	 * Checks if event status is being changed to SOLD OUT
86
+	 * and updates event meta data with previous event status
87
+	 * so that we can revert things if/when the event is no longer sold out
88
+	 *
89
+	 * @access public
90
+	 * @param string $new_status
91
+	 * @param bool   $use_default
92
+	 * @return bool|void
93
+	 * @throws \EE_Error
94
+	 */
95
+	public function set_status($new_status = null, $use_default = false)
96
+	{
97
+		// get current Event status
98
+		$old_status = $this->status();
99
+		// if status has changed
100
+		if ($old_status != $new_status) {
101
+			// TO sold_out
102
+			if ($new_status == EEM_Event::sold_out) {
103
+				// save the previous event status so that we can revert if the event is no longer sold out
104
+				$this->add_post_meta('_previous_event_status', $old_status);
105
+				do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
106
+				// OR FROM  sold_out
107
+			} else if ($old_status == EEM_Event::sold_out) {
108
+				$this->delete_post_meta('_previous_event_status');
109
+				do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
110
+			}
111
+			// update status
112
+			parent::set('status', $new_status, $use_default);
113
+			do_action('AHEE__EE_Event__set_status__after_update', $this);
114
+			return true;
115
+		} else {
116
+			// even though the old value matches the new value, it's still good to
117
+			// allow the parent set method to have a say
118
+			parent::set('status', $new_status, $use_default);
119
+			return true;
120
+		}
121
+	}
122
+
123
+
124
+
125
+	/**
126
+	 * Gets all the datetimes for this event
127
+	 *
128
+	 * @param array $query_params like EEM_Base::get_all
129
+	 * @return EE_Datetime[]
130
+	 */
131
+	public function datetimes($query_params = array())
132
+	{
133
+		return $this->get_many_related('Datetime', $query_params);
134
+	}
135
+
136
+
137
+
138
+	/**
139
+	 * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
140
+	 *
141
+	 * @return EE_Datetime[]
142
+	 */
143
+	public function datetimes_in_chronological_order()
144
+	{
145
+		return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
146
+	}
147
+
148
+
149
+
150
+	/**
151
+	 * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
152
+	 * @darren, we should probably UNSET timezone on the EEM_Datetime model
153
+	 * after running our query, so that this timezone isn't set for EVERY query
154
+	 * on EEM_Datetime for the rest of the request, no?
155
+	 *
156
+	 * @param boolean $show_expired whether or not to include expired events
157
+	 * @param boolean $show_deleted whether or not to include deleted events
158
+	 * @param null    $limit
159
+	 * @return \EE_Datetime[]
160
+	 * @throws \EE_Error
161
+	 */
162
+	public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
163
+	{
164
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
165
+			$this->ID(),
166
+			$show_expired,
167
+			$show_deleted,
168
+			$limit
169
+		);
170
+	}
171
+
172
+
173
+
174
+	/**
175
+	 * Returns one related datetime. Mostly only used by some legacy code.
176
+	 *
177
+	 * @return EE_Datetime
178
+	 */
179
+	public function first_datetime()
180
+	{
181
+		return $this->get_first_related('Datetime');
182
+	}
183
+
184
+
185
+
186
+	/**
187
+	 * Returns the 'primary' datetime for the event
188
+	 *
189
+	 * @param bool $try_to_exclude_expired
190
+	 * @param bool $try_to_exclude_deleted
191
+	 * @return EE_Datetime
192
+	 */
193
+	public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
194
+	{
195
+		if ( ! empty ($this->_Primary_Datetime)) {
196
+			return $this->_Primary_Datetime;
197
+		}
198
+		$this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)
199
+											   ->get_primary_datetime_for_event($this->ID(), $try_to_exclude_expired,
200
+												   $try_to_exclude_deleted);
201
+		return $this->_Primary_Datetime;
202
+	}
203
+
204
+
205
+
206
+	/**
207
+	 * Gets all the tickets available for purchase of this event
208
+	 *
209
+	 * @param array $query_params like EEM_Base::get_all
210
+	 * @return EE_Ticket[]
211
+	 */
212
+	public function tickets($query_params = array())
213
+	{
214
+		//first get all datetimes
215
+		$datetimes = $this->datetimes_ordered();
216
+		if ( ! $datetimes) {
217
+			return array();
218
+		}
219
+		$datetime_ids = array();
220
+		foreach ($datetimes as $datetime) {
221
+			$datetime_ids[] = $datetime->ID();
222
+		}
223
+		$where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
224
+		//if incoming $query_params has where conditions let's merge but not override existing.
225
+		if (is_array($query_params) && isset($query_params[0])) {
226
+			$where_params = array_merge($query_params[0], $where_params);
227
+			unset($query_params[0]);
228
+		}
229
+		//now add $where_params to $query_params
230
+		$query_params[0] = $where_params;
231
+		return EEM_Ticket::instance()->get_all($query_params);
232
+	}
233
+
234
+
235
+
236
+	/**
237
+	 * @return bool
238
+	 */
239
+	public function additional_limit()
240
+	{
241
+		return $this->get('EVT_additional_limit');
242
+	}
243
+
244
+
245
+
246
+	/**
247
+	 * @return bool
248
+	 */
249
+	public function allow_overflow()
250
+	{
251
+		return $this->get('EVT_allow_overflow');
252
+	}
253
+
254
+
255
+
256
+	/**
257
+	 * @return bool
258
+	 */
259
+	public function created()
260
+	{
261
+		return $this->get('EVT_created');
262
+	}
263
+
264
+
265
+
266
+	/**
267
+	 * @return bool
268
+	 */
269
+	public function description()
270
+	{
271
+		return $this->get('EVT_desc');
272
+	}
273
+
274
+
275
+
276
+	/**
277
+	 * Runs do_shortcode and wpautop on the description
278
+	 *
279
+	 * @return string of html
280
+	 */
281
+	public function description_filtered()
282
+	{
283
+		return $this->get_pretty('EVT_desc');
284
+	}
285
+
286
+
287
+
288
+	/**
289
+	 * @return bool
290
+	 */
291
+	public function display_description()
292
+	{
293
+		return $this->get('EVT_display_desc');
294
+	}
295
+
296
+
297
+
298
+	/**
299
+	 * @return bool
300
+	 */
301
+	public function display_ticket_selector()
302
+	{
303
+		return (bool)$this->get('EVT_display_ticket_selector');
304
+	}
305
+
306 306
 
307 307
 
308
-    /**
309
-     * @return bool
310
-     */
311
-    public function external_url()
312
-    {
313
-        return $this->get('EVT_external_URL');
314
-    }
308
+	/**
309
+	 * @return bool
310
+	 */
311
+	public function external_url()
312
+	{
313
+		return $this->get('EVT_external_URL');
314
+	}
315 315
 
316 316
 
317 317
 
318
-    /**
319
-     * @return bool
320
-     */
321
-    public function member_only()
322
-    {
323
-        return $this->get('EVT_member_only');
324
-    }
318
+	/**
319
+	 * @return bool
320
+	 */
321
+	public function member_only()
322
+	{
323
+		return $this->get('EVT_member_only');
324
+	}
325 325
 
326 326
 
327 327
 
328
-    /**
329
-     * @return bool
330
-     */
331
-    public function phone()
332
-    {
333
-        return $this->get('EVT_phone');
334
-    }
328
+	/**
329
+	 * @return bool
330
+	 */
331
+	public function phone()
332
+	{
333
+		return $this->get('EVT_phone');
334
+	}
335 335
 
336 336
 
337 337
 
338
-    /**
339
-     * @return bool
340
-     */
341
-    public function modified()
342
-    {
343
-        return $this->get('EVT_modified');
344
-    }
338
+	/**
339
+	 * @return bool
340
+	 */
341
+	public function modified()
342
+	{
343
+		return $this->get('EVT_modified');
344
+	}
345 345
 
346 346
 
347 347
 
348
-    /**
349
-     * @return bool
350
-     */
351
-    public function name()
352
-    {
353
-        return $this->get('EVT_name');
354
-    }
348
+	/**
349
+	 * @return bool
350
+	 */
351
+	public function name()
352
+	{
353
+		return $this->get('EVT_name');
354
+	}
355 355
 
356 356
 
357 357
 
358
-    /**
359
-     * @return bool
360
-     */
361
-    public function order()
362
-    {
363
-        return $this->get('EVT_order');
364
-    }
358
+	/**
359
+	 * @return bool
360
+	 */
361
+	public function order()
362
+	{
363
+		return $this->get('EVT_order');
364
+	}
365 365
 
366 366
 
367 367
 
368
-    /**
369
-     * @return bool|string
370
-     */
371
-    public function default_registration_status()
372
-    {
373
-        $event_default_registration_status = $this->get('EVT_default_registration_status');
374
-        return ! empty($event_default_registration_status) ? $event_default_registration_status
375
-            : EE_Registry::instance()->CFG->registration->default_STS_ID;
376
-    }
368
+	/**
369
+	 * @return bool|string
370
+	 */
371
+	public function default_registration_status()
372
+	{
373
+		$event_default_registration_status = $this->get('EVT_default_registration_status');
374
+		return ! empty($event_default_registration_status) ? $event_default_registration_status
375
+			: EE_Registry::instance()->CFG->registration->default_STS_ID;
376
+	}
377 377
 
378 378
 
379 379
 
380
-    /**
381
-     * @param int  $num_words
382
-     * @param null $more
383
-     * @param bool $not_full_desc
384
-     * @return bool|string
385
-     */
386
-    public function short_description($num_words = 55, $more = null, $not_full_desc = false)
387
-    {
388
-        $short_desc = $this->get('EVT_short_desc');
389
-        if ( ! empty($short_desc) || $not_full_desc) {
390
-            return $short_desc;
391
-        } else {
392
-            $full_desc = $this->get('EVT_desc');
393
-            return wp_trim_words($full_desc, $num_words, $more);
394
-        }
395
-    }
380
+	/**
381
+	 * @param int  $num_words
382
+	 * @param null $more
383
+	 * @param bool $not_full_desc
384
+	 * @return bool|string
385
+	 */
386
+	public function short_description($num_words = 55, $more = null, $not_full_desc = false)
387
+	{
388
+		$short_desc = $this->get('EVT_short_desc');
389
+		if ( ! empty($short_desc) || $not_full_desc) {
390
+			return $short_desc;
391
+		} else {
392
+			$full_desc = $this->get('EVT_desc');
393
+			return wp_trim_words($full_desc, $num_words, $more);
394
+		}
395
+	}
396 396
 
397 397
 
398 398
 
399
-    /**
400
-     * @return bool
401
-     */
402
-    public function slug()
403
-    {
404
-        return $this->get('EVT_slug');
405
-    }
399
+	/**
400
+	 * @return bool
401
+	 */
402
+	public function slug()
403
+	{
404
+		return $this->get('EVT_slug');
405
+	}
406 406
 
407 407
 
408 408
 
409
-    /**
410
-     * @return bool
411
-     */
412
-    public function timezone_string()
413
-    {
414
-        return $this->get('EVT_timezone_string');
415
-    }
409
+	/**
410
+	 * @return bool
411
+	 */
412
+	public function timezone_string()
413
+	{
414
+		return $this->get('EVT_timezone_string');
415
+	}
416 416
 
417 417
 
418 418
 
419
-    /**
420
-     * @return bool
421
-     */
422
-    public function visible_on()
423
-    {
424
-        return $this->get('EVT_visible_on');
425
-    }
419
+	/**
420
+	 * @return bool
421
+	 */
422
+	public function visible_on()
423
+	{
424
+		return $this->get('EVT_visible_on');
425
+	}
426 426
 
427 427
 
428 428
 
429
-    /**
430
-     * @return int
431
-     */
432
-    public function wp_user()
433
-    {
434
-        return $this->get('EVT_wp_user');
435
-    }
429
+	/**
430
+	 * @return int
431
+	 */
432
+	public function wp_user()
433
+	{
434
+		return $this->get('EVT_wp_user');
435
+	}
436 436
 
437 437
 
438 438
 
439
-    /**
440
-     * @return bool
441
-     */
442
-    public function donations()
443
-    {
444
-        return $this->get('EVT_donations');
445
-    }
439
+	/**
440
+	 * @return bool
441
+	 */
442
+	public function donations()
443
+	{
444
+		return $this->get('EVT_donations');
445
+	}
446 446
 
447 447
 
448 448
 
449
-    /**
450
-     * @param $limit
451
-     */
452
-    public function set_additional_limit($limit)
453
-    {
454
-        $this->set('EVT_additional_limit', $limit);
455
-    }
449
+	/**
450
+	 * @param $limit
451
+	 */
452
+	public function set_additional_limit($limit)
453
+	{
454
+		$this->set('EVT_additional_limit', $limit);
455
+	}
456 456
 
457 457
 
458 458
 
459
-    /**
460
-     * @param $created
461
-     */
462
-    public function set_created($created)
463
-    {
464
-        $this->set('EVT_created', $created);
465
-    }
459
+	/**
460
+	 * @param $created
461
+	 */
462
+	public function set_created($created)
463
+	{
464
+		$this->set('EVT_created', $created);
465
+	}
466 466
 
467 467
 
468 468
 
469
-    /**
470
-     * @param $desc
471
-     */
472
-    public function set_description($desc)
473
-    {
474
-        $this->set('EVT_desc', $desc);
475
-    }
469
+	/**
470
+	 * @param $desc
471
+	 */
472
+	public function set_description($desc)
473
+	{
474
+		$this->set('EVT_desc', $desc);
475
+	}
476 476
 
477 477
 
478 478
 
479
-    /**
480
-     * @param $display_desc
481
-     */
482
-    public function set_display_description($display_desc)
483
-    {
484
-        $this->set('EVT_display_desc', $display_desc);
485
-    }
479
+	/**
480
+	 * @param $display_desc
481
+	 */
482
+	public function set_display_description($display_desc)
483
+	{
484
+		$this->set('EVT_display_desc', $display_desc);
485
+	}
486 486
 
487 487
 
488 488
 
489
-    /**
490
-     * @param $display_ticket_selector
491
-     */
492
-    public function set_display_ticket_selector($display_ticket_selector)
493
-    {
494
-        $this->set('EVT_display_ticket_selector', $display_ticket_selector);
495
-    }
489
+	/**
490
+	 * @param $display_ticket_selector
491
+	 */
492
+	public function set_display_ticket_selector($display_ticket_selector)
493
+	{
494
+		$this->set('EVT_display_ticket_selector', $display_ticket_selector);
495
+	}
496 496
 
497 497
 
498 498
 
499
-    /**
500
-     * @param $external_url
501
-     */
502
-    public function set_external_url($external_url)
503
-    {
504
-        $this->set('EVT_external_URL', $external_url);
505
-    }
499
+	/**
500
+	 * @param $external_url
501
+	 */
502
+	public function set_external_url($external_url)
503
+	{
504
+		$this->set('EVT_external_URL', $external_url);
505
+	}
506 506
 
507 507
 
508 508
 
509
-    /**
510
-     * @param $member_only
511
-     */
512
-    public function set_member_only($member_only)
513
-    {
514
-        $this->set('EVT_member_only', $member_only);
515
-    }
509
+	/**
510
+	 * @param $member_only
511
+	 */
512
+	public function set_member_only($member_only)
513
+	{
514
+		$this->set('EVT_member_only', $member_only);
515
+	}
516 516
 
517 517
 
518 518
 
519
-    /**
520
-     * @param $event_phone
521
-     */
522
-    public function set_event_phone($event_phone)
523
-    {
524
-        $this->set('EVT_phone', $event_phone);
525
-    }
519
+	/**
520
+	 * @param $event_phone
521
+	 */
522
+	public function set_event_phone($event_phone)
523
+	{
524
+		$this->set('EVT_phone', $event_phone);
525
+	}
526 526
 
527 527
 
528 528
 
529
-    /**
530
-     * @param $modified
531
-     */
532
-    public function set_modified($modified)
533
-    {
534
-        $this->set('EVT_modified', $modified);
535
-    }
529
+	/**
530
+	 * @param $modified
531
+	 */
532
+	public function set_modified($modified)
533
+	{
534
+		$this->set('EVT_modified', $modified);
535
+	}
536 536
 
537 537
 
538 538
 
539
-    /**
540
-     * @param $name
541
-     */
542
-    public function set_name($name)
543
-    {
544
-        $this->set('EVT_name', $name);
545
-    }
539
+	/**
540
+	 * @param $name
541
+	 */
542
+	public function set_name($name)
543
+	{
544
+		$this->set('EVT_name', $name);
545
+	}
546 546
 
547 547
 
548 548
 
549
-    /**
550
-     * @param $order
551
-     */
552
-    public function set_order($order)
553
-    {
554
-        $this->set('EVT_order', $order);
555
-    }
549
+	/**
550
+	 * @param $order
551
+	 */
552
+	public function set_order($order)
553
+	{
554
+		$this->set('EVT_order', $order);
555
+	}
556 556
 
557 557
 
558 558
 
559
-    /**
560
-     * @param $short_desc
561
-     */
562
-    public function set_short_description($short_desc)
563
-    {
564
-        $this->set('EVT_short_desc', $short_desc);
565
-    }
559
+	/**
560
+	 * @param $short_desc
561
+	 */
562
+	public function set_short_description($short_desc)
563
+	{
564
+		$this->set('EVT_short_desc', $short_desc);
565
+	}
566 566
 
567 567
 
568 568
 
569
-    /**
570
-     * @param $slug
571
-     */
572
-    public function set_slug($slug)
573
-    {
574
-        $this->set('EVT_slug', $slug);
575
-    }
569
+	/**
570
+	 * @param $slug
571
+	 */
572
+	public function set_slug($slug)
573
+	{
574
+		$this->set('EVT_slug', $slug);
575
+	}
576 576
 
577 577
 
578 578
 
579
-    /**
580
-     * @param $timezone_string
581
-     */
582
-    public function set_timezone_string($timezone_string)
583
-    {
584
-        $this->set('EVT_timezone_string', $timezone_string);
585
-    }
579
+	/**
580
+	 * @param $timezone_string
581
+	 */
582
+	public function set_timezone_string($timezone_string)
583
+	{
584
+		$this->set('EVT_timezone_string', $timezone_string);
585
+	}
586 586
 
587 587
 
588 588
 
589
-    /**
590
-     * @param $visible_on
591
-     */
592
-    public function set_visible_on($visible_on)
593
-    {
594
-        $this->set('EVT_visible_on', $visible_on);
595
-    }
589
+	/**
590
+	 * @param $visible_on
591
+	 */
592
+	public function set_visible_on($visible_on)
593
+	{
594
+		$this->set('EVT_visible_on', $visible_on);
595
+	}
596 596
 
597 597
 
598 598
 
599
-    /**
600
-     * @param $wp_user
601
-     */
602
-    public function set_wp_user($wp_user)
603
-    {
604
-        $this->set('EVT_wp_user', $wp_user);
605
-    }
599
+	/**
600
+	 * @param $wp_user
601
+	 */
602
+	public function set_wp_user($wp_user)
603
+	{
604
+		$this->set('EVT_wp_user', $wp_user);
605
+	}
606 606
 
607 607
 
608
-
609
-    /**
610
-     * @param $default_registration_status
611
-     */
612
-    public function set_default_registration_status($default_registration_status)
613
-    {
614
-        $this->set('EVT_default_registration_status', $default_registration_status);
615
-    }
616
-
617
-
618
-
619
-    /**
620
-     * @param $donations
621
-     */
622
-    public function set_donations($donations)
623
-    {
624
-        $this->set('EVT_donations', $donations);
625
-    }
626
-
627
-
628
-
629
-    /**
630
-     * Adds a venue to this event
631
-     *
632
-     * @param EE_Venue /int $venue_id_or_obj
633
-     * @return EE_Venue
634
-     */
635
-    public function add_venue($venue_id_or_obj)
636
-    {
637
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
638
-    }
639
-
640
-
641
-
642
-    /**
643
-     * Removes a venue from the event
644
-     *
645
-     * @param EE_Venue /int $venue_id_or_obj
646
-     * @return EE_Venue
647
-     */
648
-    public function remove_venue($venue_id_or_obj)
649
-    {
650
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
651
-    }
652
-
653
-
654
-
655
-    /**
656
-     * Gets all the venues related ot the event. May provide additional $query_params if desired
657
-     *
658
-     * @param array $query_params like EEM_Base::get_all's $query_params
659
-     * @return EE_Venue[]
660
-     */
661
-    public function venues($query_params = array())
662
-    {
663
-        return $this->get_many_related('Venue', $query_params);
664
-    }
665
-
666
-
667
-
668
-    /**
669
-     * check if event id is present and if event is published
670
-     *
671
-     * @access public
672
-     * @return boolean true yes, false no
673
-     */
674
-    private function _has_ID_and_is_published()
675
-    {
676
-        // first check if event id is present and not NULL, then check if this event is published (or any of the equivalent "published" statuses)
677
-        return ($this->ID() && $this->ID() !== null
678
-                && ($this->status() == 'publish'
679
-                    || $this->status()
680
-                       == EEM_Event::sold_out
681
-                    || $this->status() == EEM_Event::postponed
682
-                    || $this->status() == EEM_Event::cancelled)) ? true : false;
683
-    }
684
-
685
-
686
-
687
-    /**
688
-     * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
689
-     *
690
-     * @access public
691
-     * @return boolean true yes, false no
692
-     */
693
-    public function is_upcoming()
694
-    {
695
-        // check if event id is present and if this event is published
696
-        if ($this->is_inactive()) {
697
-            return false;
698
-        }
699
-        // set initial value
700
-        $upcoming = false;
701
-        //next let's get all datetimes and loop through them
702
-        $datetimes = $this->datetimes_in_chronological_order();
703
-        foreach ($datetimes as $datetime) {
704
-            if ($datetime instanceof EE_Datetime) {
705
-                //if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
706
-                if ($datetime->is_expired()) {
707
-                    continue;
708
-                }
709
-                //if this dtt is active then we return false.
710
-                if ($datetime->is_active()) {
711
-                    return false;
712
-                }
713
-                //otherwise let's check upcoming status
714
-                $upcoming = $datetime->is_upcoming();
715
-            }
716
-        }
717
-        return $upcoming;
718
-    }
719
-
720
-
721
-
722
-    /**
723
-     * @return bool
724
-     */
725
-    public function is_active()
726
-    {
727
-        // check if event id is present and if this event is published
728
-        if ($this->is_inactive()) {
729
-            return false;
730
-        }
731
-        // set initial value
732
-        $active = false;
733
-        //next let's get all datetimes and loop through them
734
-        $datetimes = $this->datetimes_in_chronological_order();
735
-        foreach ($datetimes as $datetime) {
736
-            if ($datetime instanceof EE_Datetime) {
737
-                //if this dtt is expired then we continue cause one of the other datetimes might be active.
738
-                if ($datetime->is_expired()) {
739
-                    continue;
740
-                }
741
-                //if this dtt is upcoming then we return false.
742
-                if ($datetime->is_upcoming()) {
743
-                    return false;
744
-                }
745
-                //otherwise let's check active status
746
-                $active = $datetime->is_active();
747
-            }
748
-        }
749
-        return $active;
750
-    }
751
-
752
-
753
-
754
-    /**
755
-     * @return bool
756
-     */
757
-    public function is_expired()
758
-    {
759
-        // check if event id is present and if this event is published
760
-        if ($this->is_inactive()) {
761
-            return false;
762
-        }
763
-        // set initial value
764
-        $expired = false;
765
-        //first let's get all datetimes and loop through them
766
-        $datetimes = $this->datetimes_in_chronological_order();
767
-        foreach ($datetimes as $datetime) {
768
-            if ($datetime instanceof EE_Datetime) {
769
-                //if this dtt is upcoming or active then we return false.
770
-                if ($datetime->is_upcoming() || $datetime->is_active()) {
771
-                    return false;
772
-                }
773
-                //otherwise let's check active status
774
-                $expired = $datetime->is_expired();
775
-            }
776
-        }
777
-        return $expired;
778
-    }
779
-
780
-
781
-
782
-    /**
783
-     * @return bool
784
-     */
785
-    public function is_inactive()
786
-    {
787
-        // check if event id is present and if this event is published
788
-        if ($this->_has_ID_and_is_published()) {
789
-            return false;
790
-        }
791
-        return true;
792
-    }
793
-
794
-
795
-
796
-    /**
797
-     *    perform_sold_out_status_check
798
-     *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
799
-     *    available... if NOT, then the event status will get toggled to 'sold_out'
800
-     *
801
-     * @access public
802
-     * @return bool    return the ACTUAL sold out state.
803
-     */
804
-    public function perform_sold_out_status_check()
805
-    {
806
-        // get all unexpired untrashed tickets
807
-        $tickets = $this->tickets(array(
808
-            array(
809
-                'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
810
-                'TKT_deleted'  => false,
811
-            ),
812
-        ));
813
-        // if all the tickets are just expired, then don't update the event status to sold out
814
-        if (empty($tickets)) {
815
-            return true;
816
-        }
817
-        // set initial value
818
-        $spaces_remaining = 0;
819
-        foreach ($tickets as $ticket) {
820
-            if ($ticket instanceof EE_Ticket) {
821
-                $spaces_remaining += $ticket->qty('saleable');
822
-            }
823
-        }
824
-        if ($spaces_remaining === 0) {
825
-            $this->set_status(EEM_Event::sold_out);
826
-            if ( ! is_admin() || (is_admin() && defined('DOING_AJAX'))) {
827
-                $this->save();
828
-            }
829
-            $sold_out = true;
830
-        } else {
831
-            $sold_out = false;
832
-            // was event previously marked as sold out ?
833
-            if ($this->status() == EEM_Event::sold_out) {
834
-                // revert status to previous value, if it was set
835
-                $previous_event_status = $this->get_post_meta('_previous_event_status', true);
836
-                if ($previous_event_status) {
837
-                    $this->set_status($previous_event_status);
838
-                }
839
-            }
840
-        }
841
-        //note: I considered changing the EEM_Event status away from sold_out if this status check reveals that it's no longer sold out (yet the status is still set as sold out) but the problem is... what do we change the status BACK to?  We can't always assume that the previous event status was 'published' because this status check is always done in the admin and its entirely possible the event admin manually changes to sold_out status from some other status.  We also don't want a draft event to become a "publish event" because the sold out check reveals its NOT sold out.
842
-        // So I'll forgo the automatic switch away from sold out status for now and instead just return the $sold out status... so this check can be used to validate the TRUE sold out status regardless of what the Event status is set to.
843
-        return $sold_out;
844
-    }
845
-
846
-
847
-
848
-    /**
849
-     * This returns the total remaining spaces for sale on this event.
850
-     * ############################
851
-     * VERY IMPORTANT FOR DEVELOPERS:
852
-     * While included here, this method is still being tested internally, so its signature and behaviour COULD change.
853
-     * While this comment block is in place, usage is at your own risk and know that it may change in future builds.
854
-     * ############################
855
-     *
856
-     * @uses EE_Event::total_available_spaces()
857
-     * @return float|int  (EE_INF is returned as float)
858
-     */
859
-    public function spaces_remaining_for_sale()
860
-    {
861
-        //first get total available spaces including consideration for tickets that have already sold.
862
-        $spaces_available = $this->total_available_spaces(true);
863
-        //if total available = 0, then exit right away because that means everything is expired.
864
-        if ($spaces_available === 0) {
865
-            return 0;
866
-        }
867
-        //subtract total approved registrations from spaces available to get how many are remaining.
868
-        $spots_taken = EEM_Registration::instance()->count(array(
869
-            array(
870
-                'EVT_ID' => $this->ID(),
871
-                'STS_ID' => EEM_Registration::status_id_approved,
872
-            ),
873
-        ), 'REG_ID', true);
874
-        $spaces_remaining = $spaces_available - $spots_taken;
875
-        return $spaces_remaining > 0 ? $spaces_remaining : 0;
876
-    }
877
-
878
-
879
-
880
-    /**
881
-     * This returns the total spaces available for an event while considering all the qtys on the tickets and the reg
882
-     * limits on the datetimes attached to this event.
883
-     * ############################
884
-     * VERY IMPORTANT FOR DEVELOPERS:
885
-     * While included here, this method is still being tested internally, so its signature and behaviour COULD change.
886
-     * While this comment block is in place, usage is at your own risk and know that it may change in future builds.
887
-     * ############################
888
-     * Note: by "spaces available" we are not returning how many spaces remain.  That is a calculation involving using
889
-     * the value from this method and subtracting the approved registrations for the event.
890
-     *
891
-     * @param   bool $current_total_available       Whether to consider any tickets that have already sold in our
892
-     *                                              calculation. If this is false, then we return the most tickets that
893
-     *                                              could ever be sold for this event with the datetime and tickets
894
-     *                                              setup on the event under optimal selling conditions.  Otherwise we
895
-     *                                              return a live calculation of spaces available based on tickets
896
-     *                                              sold.  Depending on setup and stage of sales, this may appear to
897
-     *                                              equal remaining tickets.  However, the more tickets are sold out,
898
-     *                                              the more accurate the "live" total is.
899
-     * @return  int|float  (Note: if EE_INF is returned its considered a float by PHP)
900
-     */
901
-    public function total_available_spaces($current_total_available = false)
902
-    {
903
-        $spaces_available = 0;
904
-        //first get all tickets on the event and include expired tickets
905
-        $tickets = $this->tickets(array('default_where_conditions' => 'none'));
906
-        $ticket_sums = array();
907
-        $datetime_limits = array();
908
-        //loop through tickets and normalize them
909
-        foreach ($tickets as $ticket) {
910
-            $datetimes = $ticket->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
911
-            if (empty($datetimes)) {
912
-                continue;
913
-            }
914
-            //first datetime should be the lowest datetime
915
-            $least_datetime = reset($datetimes);
916
-            //lets reset the ticket quantity to be the lower of either the lowest datetime reg limit or the ticket quantity
917
-            //IF datetimes sold (and we're not doing current live total available, then use spaces remaining for datetime, not reg_limit.
918
-            if ($current_total_available) {
919
-                if ($ticket->is_remaining()) {
920
-                    $remaining = $ticket->remaining();
921
-                } else {
922
-                    $spaces_available += $ticket->sold();
923
-                    //and we don't cache this ticket to our list because its sold out.
924
-                    continue;
925
-                }
926
-            } else {
927
-                $remaining = min($ticket->qty(), $least_datetime->reg_limit());
928
-            }
929
-            //if $ticket_limit == infinity then let's drop out right away and just return that because any infinity amount trumps all other "available" amounts.
930
-            if ($remaining === EE_INF) {
931
-                return EE_INF;
932
-            }
933
-            //multiply normalized $tkt quantity by the number of datetimes on the ticket as the "sum"
934
-            //also include the sum of all the datetime reg limits on the ticket for breaking ties.
935
-            $ticket_sums[$ticket->ID()]['sum'] = $remaining * count($datetimes);
936
-            $ticket_sums[$ticket->ID()]['datetime_sums'] = 0;
937
-            foreach ($datetimes as $datetime) {
938
-                if ($datetime->reg_limit() === EE_INF) {
939
-                    $ticket_sums[$ticket->ID()]['datetime_sums'] = EE_INF;
940
-                } else {
941
-                    $ticket_sums[$ticket->ID()]['datetime_sums'] += $current_total_available
942
-                        ? $datetime->spaces_remaining() : $datetime->reg_limit();
943
-                }
944
-                $datetime_limits[$datetime->ID()] = $current_total_available ? $datetime->spaces_remaining()
945
-                    : $datetime->reg_limit();
946
-            }
947
-            $ticket_sums[$ticket->ID()]['ticket'] = $ticket;
948
-        }
949
-        //The order is sorted by lowest available first (which is calculated for each ticket by multiplying the normalized
950
-        //ticket quantity by the number of datetimes on the ticket).  For tie-breakers, then the next sort is based on the
951
-        //ticket with the greatest sum of all remaining datetime->spaces_remaining() ( or $datetime->reg_limit() if not
952
-        //$current_total_available ) for the datetimes on the ticket.
953
-        usort($ticket_sums, function ($a, $b) {
954
-            if ($a['sum'] == $b['sum']) {
955
-                if ($a['datetime_sums'] == $b['datetime_sums']) {
956
-                    return 0;
957
-                }
958
-                return $a['datetime_sums'] < $b['datetime_sums'] ? 1 : -1;
959
-            }
960
-            return ($a['sum'] < $b['sum']) ? -1 : 1;
961
-        });
962
-        //now let's loop through the sorted tickets and simulate sellouts
963
-        foreach ($ticket_sums as $ticket_info) {
964
-            if ($ticket_info['ticket'] instanceof EE_Ticket) {
965
-                $datetimes = $ticket_info['ticket']->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
966
-                //need to sort these $datetimes by remaining (only if $current_total_available)
967
-                //setup datetimes for simulation
968
-                $ticket_datetimes_remaining = array();
969
-                foreach ($datetimes as $datetime) {
970
-                    $ticket_datetimes_remaining[$datetime->ID()]['rem'] = $datetime_limits[$datetime->ID()];
971
-                    $ticket_datetimes_remaining[$datetime->ID()]['datetime'] = $datetime;
972
-                }
973
-                usort($ticket_datetimes_remaining, function ($a, $b) {
974
-                    if ($a['rem'] == $b['rem']) {
975
-                        return 0;
976
-                    }
977
-                    return ($a['rem'] < $b['rem']) ? -1 : 1;
978
-                });
979
-                //get the remaining on the first datetime (which should be the one with the least remaining) and that is
980
-                //what we add to the spaces_available running total.  Then we need to decrease the remaining on our datetime tracker.
981
-                $lowest_datetime = reset($ticket_datetimes_remaining);
982
-                //need to get the lower of; what the remaining is on the lowest datetime, and the remaining on the ticket.
983
-                // If this ends up being 0 (because of previous tickets in our simulation selling out), then it has already
984
-                // been tracked on $spaces available and this ticket is now sold out for the simulation, so we can continue
985
-                // to the next ticket.
986
-                if ($current_total_available) {
987
-                    $remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->remaining());
988
-                } else {
989
-                    $remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->qty());
990
-                }
991
-                //if $remaining is infinite that means that all datetimes on this ticket are infinite but we've made it here because all
992
-                //tickets have a quantity.  So we don't have to track datetimes, we can just use ticket quantities for total
993
-                //available.
994
-                if ($remaining === EE_INF) {
995
-                    $spaces_available += $ticket_info['ticket']->qty();
996
-                    continue;
997
-                }
998
-                //if ticket has sold amounts then we also need to add that (but only if doing live counts)
999
-                if ($current_total_available) {
1000
-                    $spaces_available += $ticket_info['ticket']->sold();
1001
-                }
1002
-                if ($remaining <= 0) {
1003
-                    continue;
1004
-                } else {
1005
-                    $spaces_available += $remaining;
1006
-                }
1007
-                //loop through the datetimes and sell them out!
1008
-                foreach ($ticket_datetimes_remaining as $datetime_info) {
1009
-                    if ($datetime_info['datetime'] instanceof EE_Datetime) {
1010
-                        $datetime_limits[$datetime_info['datetime']->ID()] += -$remaining;
1011
-                    }
1012
-                }
1013
-            }
1014
-        }
1015
-        return $spaces_available;
1016
-    }
1017
-
1018
-
1019
-
1020
-    /**
1021
-     * Checks if the event is set to sold out
1022
-     *
1023
-     * @param  bool $actual whether or not to perform calculations to not only figure the actual status but also to
1024
-     *                      flip the status if necessary to sold out If false, we just check the existing status of the
1025
-     *                      event
1026
-     * @return boolean
1027
-     */
1028
-    public function is_sold_out($actual = false)
1029
-    {
1030
-        if ( ! $actual) {
1031
-            return $this->status() == EEM_Event::sold_out;
1032
-        } else {
1033
-            return $this->perform_sold_out_status_check();
1034
-        }
1035
-    }
1036
-
1037
-
1038
-
1039
-    /**
1040
-     * Checks if the event is marked as postponed
1041
-     *
1042
-     * @return boolean
1043
-     */
1044
-    public function is_postponed()
1045
-    {
1046
-        return $this->status() == EEM_Event::postponed;
1047
-    }
1048
-
1049
-
1050
-
1051
-    /**
1052
-     * Checks if the event is marked as cancelled
1053
-     *
1054
-     * @return boolean
1055
-     */
1056
-    public function is_cancelled()
1057
-    {
1058
-        return $this->status() == EEM_Event::cancelled;
1059
-    }
1060
-
1061
-
1062
-
1063
-    /**
1064
-     * Get the logical active status in a hierarchical order for all the datetimes.  Note
1065
-     * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1066
-     * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1067
-     * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1068
-     * the event is considered expired.
1069
-     * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1070
-     * status set on the EVENT when it is not published and thus is done
1071
-     *
1072
-     * @param bool $reset
1073
-     * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1074
-     */
1075
-    public function get_active_status($reset = false)
1076
-    {
1077
-        // if the active status has already been set, then just use that value (unless we are resetting it)
1078
-        if ( ! empty($this->_active_status) && ! $reset) {
1079
-            return $this->_active_status;
1080
-        }
1081
-        //first check if event id is present on this object
1082
-        if ( ! $this->ID()) {
1083
-            return false;
1084
-        }
1085
-        $where_params_for_event = array(array('EVT_ID' => $this->ID()));
1086
-        //if event is published:
1087
-        if ($this->status() === 'publish') {
1088
-            //active?
1089
-            if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::active, $where_params_for_event)
1090
-                > 0
1091
-            ) {
1092
-                $this->_active_status = EE_Datetime::active;
1093
-            } else {
1094
-                //upcoming?
1095
-                if (EEM_Datetime::instance()
1096
-                                ->get_datetime_count_for_status(EE_Datetime::upcoming, $where_params_for_event) > 0
1097
-                ) {
1098
-                    $this->_active_status = EE_Datetime::upcoming;
1099
-                } else {
1100
-                    //expired?
1101
-                    if (EEM_Datetime::instance()
1102
-                                    ->get_datetime_count_for_status(EE_Datetime::expired, $where_params_for_event) > 0
1103
-                    ) {
1104
-                        $this->_active_status = EE_Datetime::expired;
1105
-                    } else {
1106
-                        //it would be odd if things make it this far because it basically means there are no datetime's
1107
-                        //attached to the event.  So in this case it will just be considered inactive.
1108
-                        $this->_active_status = EE_Datetime::inactive;
1109
-                    }
1110
-                }
1111
-            }
1112
-        } else {
1113
-            //the event is not published, so let's just set it's active status according to its' post status
1114
-            switch ($this->status()) {
1115
-                case EEM_Event::sold_out :
1116
-                    $this->_active_status = EE_Datetime::sold_out;
1117
-                    break;
1118
-                case EEM_Event::cancelled :
1119
-                    $this->_active_status = EE_Datetime::cancelled;
1120
-                    break;
1121
-                case EEM_Event::postponed :
1122
-                    $this->_active_status = EE_Datetime::postponed;
1123
-                    break;
1124
-                default :
1125
-                    $this->_active_status = EE_Datetime::inactive;
1126
-            }
1127
-        }
1128
-        return $this->_active_status;
1129
-    }
1130
-
1131
-
1132
-
1133
-    /**
1134
-     *    pretty_active_status
1135
-     *
1136
-     * @access public
1137
-     * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1138
-     * @return mixed void|string
1139
-     */
1140
-    public function pretty_active_status($echo = true)
1141
-    {
1142
-        $active_status = $this->get_active_status();
1143
-        $status = '<span class="ee-status event-active-status-'
1144
-                  . $active_status
1145
-                  . '">'
1146
-                  . EEH_Template::pretty_status($active_status, false, 'sentence')
1147
-                  . '</span>';
1148
-        if ($echo) {
1149
-            echo $status;
1150
-            return '';
1151
-        }
1152
-        return $status;
1153
-    }
1154
-
1155
-
1156
-
1157
-    /**
1158
-     * @return bool|int
1159
-     */
1160
-    public function get_number_of_tickets_sold()
1161
-    {
1162
-        $tkt_sold = 0;
1163
-        if ( ! $this->ID()) {
1164
-            return 0;
1165
-        }
1166
-        $datetimes = $this->datetimes();
1167
-        foreach ($datetimes as $datetime) {
1168
-            if ($datetime instanceof EE_Datetime) {
1169
-                $tkt_sold += $datetime->sold();
1170
-            }
1171
-        }
1172
-        return $tkt_sold;
1173
-    }
1174
-
1175
-
1176
-
1177
-    /**
1178
-     * This just returns a count of all the registrations for this event
1179
-     *
1180
-     * @access  public
1181
-     * @return int
1182
-     */
1183
-    public function get_count_of_all_registrations()
1184
-    {
1185
-        return EEM_Event::instance()->count_related($this, 'Registration');
1186
-    }
1187
-
1188
-
1189
-
1190
-    /**
1191
-     * This returns the ticket with the earliest start time that is available for this event (across all datetimes
1192
-     * attached to the event)
1193
-     *
1194
-     * @return EE_Ticket
1195
-     */
1196
-    public function get_ticket_with_earliest_start_time()
1197
-    {
1198
-        $where['Datetime.EVT_ID'] = $this->ID();
1199
-        $query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1200
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1201
-    }
1202
-
1203
-
1204
-
1205
-    /**
1206
-     * This returns the ticket with the latest end time that is available for this event (across all datetimes attached
1207
-     * to the event)
1208
-     *
1209
-     * @return EE_Ticket
1210
-     */
1211
-    public function get_ticket_with_latest_end_time()
1212
-    {
1213
-        $where['Datetime.EVT_ID'] = $this->ID();
1214
-        $query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1215
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1216
-    }
1217
-
1218
-
1219
-
1220
-    /**
1221
-     * This returns whether there are any tickets on sale for this event.
1222
-     *
1223
-     * @return bool true = YES tickets on sale.
1224
-     */
1225
-    public function tickets_on_sale()
1226
-    {
1227
-        $earliest_ticket = $this->get_ticket_with_earliest_start_time();
1228
-        $latest_ticket = $this->get_ticket_with_latest_end_time();
1229
-        if ( ! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1230
-            return false;
1231
-        }
1232
-        //check on sale for these two tickets.
1233
-        if ($latest_ticket->is_on_sale() || $earliest_ticket->is_on_sale()) {
1234
-            return true;
1235
-        }
1236
-        return false;
1237
-    }
1238
-
1239
-
1240
-
1241
-    /**
1242
-     * Gets the URL for viewing this event on the front-end. Overrides parent
1243
-     * to check for an external URL first
1244
-     *
1245
-     * @return string
1246
-     */
1247
-    public function get_permalink()
1248
-    {
1249
-        if ($this->external_url()) {
1250
-            return $this->external_url();
1251
-        } else {
1252
-            return parent::get_permalink();
1253
-        }
1254
-    }
1255
-
1256
-
1257
-
1258
-    /**
1259
-     * Gets the first term for 'espresso_event_categories' we can find
1260
-     *
1261
-     * @param array $query_params like EEM_Base::get_all
1262
-     * @return EE_Term
1263
-     */
1264
-    public function first_event_category($query_params = array())
1265
-    {
1266
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1267
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1268
-        return EEM_Term::instance()->get_one($query_params);
1269
-    }
1270
-
1271
-
1272
-
1273
-    /**
1274
-     * Gets all terms for 'espresso_event_categories' we can find
1275
-     *
1276
-     * @param array $query_params
1277
-     * @return EE_Term[]
1278
-     */
1279
-    public function get_all_event_categories($query_params = array())
1280
-    {
1281
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1282
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1283
-        return EEM_Term::instance()->get_all($query_params);
1284
-    }
1285
-
1286
-
1287
-
1288
-    /**
1289
-     * Gets all the question groups, ordering them by QSG_order ascending
1290
-     *
1291
-     * @param array $query_params @see EEM_Base::get_all
1292
-     * @return EE_Question_Group[]
1293
-     */
1294
-    public function question_groups($query_params = array())
1295
-    {
1296
-        $query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1297
-        return $this->get_many_related('Question_Group', $query_params);
1298
-    }
1299
-
1300
-
1301
-
1302
-    /**
1303
-     * Implementation for EEI_Has_Icon interface method.
1304
-     *
1305
-     * @see EEI_Visual_Representation for comments
1306
-     * @return string
1307
-     */
1308
-    public function get_icon()
1309
-    {
1310
-        return '<span class="dashicons dashicons-flag"></span>';
1311
-    }
1312
-
1313
-
1314
-
1315
-    /**
1316
-     * Implementation for EEI_Admin_Links interface method.
1317
-     *
1318
-     * @see EEI_Admin_Links for comments
1319
-     * @return string
1320
-     */
1321
-    public function get_admin_details_link()
1322
-    {
1323
-        return $this->get_admin_edit_link();
1324
-    }
1325
-
1326
-
1327
-
1328
-    /**
1329
-     * Implementation for EEI_Admin_Links interface method.
1330
-     *
1331
-     * @see EEI_Admin_Links for comments
1332
-     * @return string
1333
-     */
1334
-    public function get_admin_edit_link()
1335
-    {
1336
-        return EEH_URL::add_query_args_and_nonce(array(
1337
-            'page'   => 'espresso_events',
1338
-            'action' => 'edit',
1339
-            'post'   => $this->ID(),
1340
-        ),
1341
-            admin_url('admin.php')
1342
-        );
1343
-    }
1344
-
1345
-
1346
-
1347
-    /**
1348
-     * Implementation for EEI_Admin_Links interface method.
1349
-     *
1350
-     * @see EEI_Admin_Links for comments
1351
-     * @return string
1352
-     */
1353
-    public function get_admin_settings_link()
1354
-    {
1355
-        return EEH_URL::add_query_args_and_nonce(array(
1356
-            'page'   => 'espresso_events',
1357
-            'action' => 'default_event_settings',
1358
-        ),
1359
-            admin_url('admin.php')
1360
-        );
1361
-    }
1362
-
1363
-
1364
-
1365
-    /**
1366
-     * Implementation for EEI_Admin_Links interface method.
1367
-     *
1368
-     * @see EEI_Admin_Links for comments
1369
-     * @return string
1370
-     */
1371
-    public function get_admin_overview_link()
1372
-    {
1373
-        return EEH_URL::add_query_args_and_nonce(array(
1374
-            'page'   => 'espresso_events',
1375
-            'action' => 'default'
1376
-        ),
1377
-            admin_url('admin.php')
1378
-        );
1379
-    }
608
+
609
+	/**
610
+	 * @param $default_registration_status
611
+	 */
612
+	public function set_default_registration_status($default_registration_status)
613
+	{
614
+		$this->set('EVT_default_registration_status', $default_registration_status);
615
+	}
616
+
617
+
618
+
619
+	/**
620
+	 * @param $donations
621
+	 */
622
+	public function set_donations($donations)
623
+	{
624
+		$this->set('EVT_donations', $donations);
625
+	}
626
+
627
+
628
+
629
+	/**
630
+	 * Adds a venue to this event
631
+	 *
632
+	 * @param EE_Venue /int $venue_id_or_obj
633
+	 * @return EE_Venue
634
+	 */
635
+	public function add_venue($venue_id_or_obj)
636
+	{
637
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
638
+	}
639
+
640
+
641
+
642
+	/**
643
+	 * Removes a venue from the event
644
+	 *
645
+	 * @param EE_Venue /int $venue_id_or_obj
646
+	 * @return EE_Venue
647
+	 */
648
+	public function remove_venue($venue_id_or_obj)
649
+	{
650
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
651
+	}
652
+
653
+
654
+
655
+	/**
656
+	 * Gets all the venues related ot the event. May provide additional $query_params if desired
657
+	 *
658
+	 * @param array $query_params like EEM_Base::get_all's $query_params
659
+	 * @return EE_Venue[]
660
+	 */
661
+	public function venues($query_params = array())
662
+	{
663
+		return $this->get_many_related('Venue', $query_params);
664
+	}
665
+
666
+
667
+
668
+	/**
669
+	 * check if event id is present and if event is published
670
+	 *
671
+	 * @access public
672
+	 * @return boolean true yes, false no
673
+	 */
674
+	private function _has_ID_and_is_published()
675
+	{
676
+		// first check if event id is present and not NULL, then check if this event is published (or any of the equivalent "published" statuses)
677
+		return ($this->ID() && $this->ID() !== null
678
+				&& ($this->status() == 'publish'
679
+					|| $this->status()
680
+					   == EEM_Event::sold_out
681
+					|| $this->status() == EEM_Event::postponed
682
+					|| $this->status() == EEM_Event::cancelled)) ? true : false;
683
+	}
684
+
685
+
686
+
687
+	/**
688
+	 * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
689
+	 *
690
+	 * @access public
691
+	 * @return boolean true yes, false no
692
+	 */
693
+	public function is_upcoming()
694
+	{
695
+		// check if event id is present and if this event is published
696
+		if ($this->is_inactive()) {
697
+			return false;
698
+		}
699
+		// set initial value
700
+		$upcoming = false;
701
+		//next let's get all datetimes and loop through them
702
+		$datetimes = $this->datetimes_in_chronological_order();
703
+		foreach ($datetimes as $datetime) {
704
+			if ($datetime instanceof EE_Datetime) {
705
+				//if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
706
+				if ($datetime->is_expired()) {
707
+					continue;
708
+				}
709
+				//if this dtt is active then we return false.
710
+				if ($datetime->is_active()) {
711
+					return false;
712
+				}
713
+				//otherwise let's check upcoming status
714
+				$upcoming = $datetime->is_upcoming();
715
+			}
716
+		}
717
+		return $upcoming;
718
+	}
719
+
720
+
721
+
722
+	/**
723
+	 * @return bool
724
+	 */
725
+	public function is_active()
726
+	{
727
+		// check if event id is present and if this event is published
728
+		if ($this->is_inactive()) {
729
+			return false;
730
+		}
731
+		// set initial value
732
+		$active = false;
733
+		//next let's get all datetimes and loop through them
734
+		$datetimes = $this->datetimes_in_chronological_order();
735
+		foreach ($datetimes as $datetime) {
736
+			if ($datetime instanceof EE_Datetime) {
737
+				//if this dtt is expired then we continue cause one of the other datetimes might be active.
738
+				if ($datetime->is_expired()) {
739
+					continue;
740
+				}
741
+				//if this dtt is upcoming then we return false.
742
+				if ($datetime->is_upcoming()) {
743
+					return false;
744
+				}
745
+				//otherwise let's check active status
746
+				$active = $datetime->is_active();
747
+			}
748
+		}
749
+		return $active;
750
+	}
751
+
752
+
753
+
754
+	/**
755
+	 * @return bool
756
+	 */
757
+	public function is_expired()
758
+	{
759
+		// check if event id is present and if this event is published
760
+		if ($this->is_inactive()) {
761
+			return false;
762
+		}
763
+		// set initial value
764
+		$expired = false;
765
+		//first let's get all datetimes and loop through them
766
+		$datetimes = $this->datetimes_in_chronological_order();
767
+		foreach ($datetimes as $datetime) {
768
+			if ($datetime instanceof EE_Datetime) {
769
+				//if this dtt is upcoming or active then we return false.
770
+				if ($datetime->is_upcoming() || $datetime->is_active()) {
771
+					return false;
772
+				}
773
+				//otherwise let's check active status
774
+				$expired = $datetime->is_expired();
775
+			}
776
+		}
777
+		return $expired;
778
+	}
779
+
780
+
781
+
782
+	/**
783
+	 * @return bool
784
+	 */
785
+	public function is_inactive()
786
+	{
787
+		// check if event id is present and if this event is published
788
+		if ($this->_has_ID_and_is_published()) {
789
+			return false;
790
+		}
791
+		return true;
792
+	}
793
+
794
+
795
+
796
+	/**
797
+	 *    perform_sold_out_status_check
798
+	 *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
799
+	 *    available... if NOT, then the event status will get toggled to 'sold_out'
800
+	 *
801
+	 * @access public
802
+	 * @return bool    return the ACTUAL sold out state.
803
+	 */
804
+	public function perform_sold_out_status_check()
805
+	{
806
+		// get all unexpired untrashed tickets
807
+		$tickets = $this->tickets(array(
808
+			array(
809
+				'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
810
+				'TKT_deleted'  => false,
811
+			),
812
+		));
813
+		// if all the tickets are just expired, then don't update the event status to sold out
814
+		if (empty($tickets)) {
815
+			return true;
816
+		}
817
+		// set initial value
818
+		$spaces_remaining = 0;
819
+		foreach ($tickets as $ticket) {
820
+			if ($ticket instanceof EE_Ticket) {
821
+				$spaces_remaining += $ticket->qty('saleable');
822
+			}
823
+		}
824
+		if ($spaces_remaining === 0) {
825
+			$this->set_status(EEM_Event::sold_out);
826
+			if ( ! is_admin() || (is_admin() && defined('DOING_AJAX'))) {
827
+				$this->save();
828
+			}
829
+			$sold_out = true;
830
+		} else {
831
+			$sold_out = false;
832
+			// was event previously marked as sold out ?
833
+			if ($this->status() == EEM_Event::sold_out) {
834
+				// revert status to previous value, if it was set
835
+				$previous_event_status = $this->get_post_meta('_previous_event_status', true);
836
+				if ($previous_event_status) {
837
+					$this->set_status($previous_event_status);
838
+				}
839
+			}
840
+		}
841
+		//note: I considered changing the EEM_Event status away from sold_out if this status check reveals that it's no longer sold out (yet the status is still set as sold out) but the problem is... what do we change the status BACK to?  We can't always assume that the previous event status was 'published' because this status check is always done in the admin and its entirely possible the event admin manually changes to sold_out status from some other status.  We also don't want a draft event to become a "publish event" because the sold out check reveals its NOT sold out.
842
+		// So I'll forgo the automatic switch away from sold out status for now and instead just return the $sold out status... so this check can be used to validate the TRUE sold out status regardless of what the Event status is set to.
843
+		return $sold_out;
844
+	}
845
+
846
+
847
+
848
+	/**
849
+	 * This returns the total remaining spaces for sale on this event.
850
+	 * ############################
851
+	 * VERY IMPORTANT FOR DEVELOPERS:
852
+	 * While included here, this method is still being tested internally, so its signature and behaviour COULD change.
853
+	 * While this comment block is in place, usage is at your own risk and know that it may change in future builds.
854
+	 * ############################
855
+	 *
856
+	 * @uses EE_Event::total_available_spaces()
857
+	 * @return float|int  (EE_INF is returned as float)
858
+	 */
859
+	public function spaces_remaining_for_sale()
860
+	{
861
+		//first get total available spaces including consideration for tickets that have already sold.
862
+		$spaces_available = $this->total_available_spaces(true);
863
+		//if total available = 0, then exit right away because that means everything is expired.
864
+		if ($spaces_available === 0) {
865
+			return 0;
866
+		}
867
+		//subtract total approved registrations from spaces available to get how many are remaining.
868
+		$spots_taken = EEM_Registration::instance()->count(array(
869
+			array(
870
+				'EVT_ID' => $this->ID(),
871
+				'STS_ID' => EEM_Registration::status_id_approved,
872
+			),
873
+		), 'REG_ID', true);
874
+		$spaces_remaining = $spaces_available - $spots_taken;
875
+		return $spaces_remaining > 0 ? $spaces_remaining : 0;
876
+	}
877
+
878
+
879
+
880
+	/**
881
+	 * This returns the total spaces available for an event while considering all the qtys on the tickets and the reg
882
+	 * limits on the datetimes attached to this event.
883
+	 * ############################
884
+	 * VERY IMPORTANT FOR DEVELOPERS:
885
+	 * While included here, this method is still being tested internally, so its signature and behaviour COULD change.
886
+	 * While this comment block is in place, usage is at your own risk and know that it may change in future builds.
887
+	 * ############################
888
+	 * Note: by "spaces available" we are not returning how many spaces remain.  That is a calculation involving using
889
+	 * the value from this method and subtracting the approved registrations for the event.
890
+	 *
891
+	 * @param   bool $current_total_available       Whether to consider any tickets that have already sold in our
892
+	 *                                              calculation. If this is false, then we return the most tickets that
893
+	 *                                              could ever be sold for this event with the datetime and tickets
894
+	 *                                              setup on the event under optimal selling conditions.  Otherwise we
895
+	 *                                              return a live calculation of spaces available based on tickets
896
+	 *                                              sold.  Depending on setup and stage of sales, this may appear to
897
+	 *                                              equal remaining tickets.  However, the more tickets are sold out,
898
+	 *                                              the more accurate the "live" total is.
899
+	 * @return  int|float  (Note: if EE_INF is returned its considered a float by PHP)
900
+	 */
901
+	public function total_available_spaces($current_total_available = false)
902
+	{
903
+		$spaces_available = 0;
904
+		//first get all tickets on the event and include expired tickets
905
+		$tickets = $this->tickets(array('default_where_conditions' => 'none'));
906
+		$ticket_sums = array();
907
+		$datetime_limits = array();
908
+		//loop through tickets and normalize them
909
+		foreach ($tickets as $ticket) {
910
+			$datetimes = $ticket->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
911
+			if (empty($datetimes)) {
912
+				continue;
913
+			}
914
+			//first datetime should be the lowest datetime
915
+			$least_datetime = reset($datetimes);
916
+			//lets reset the ticket quantity to be the lower of either the lowest datetime reg limit or the ticket quantity
917
+			//IF datetimes sold (and we're not doing current live total available, then use spaces remaining for datetime, not reg_limit.
918
+			if ($current_total_available) {
919
+				if ($ticket->is_remaining()) {
920
+					$remaining = $ticket->remaining();
921
+				} else {
922
+					$spaces_available += $ticket->sold();
923
+					//and we don't cache this ticket to our list because its sold out.
924
+					continue;
925
+				}
926
+			} else {
927
+				$remaining = min($ticket->qty(), $least_datetime->reg_limit());
928
+			}
929
+			//if $ticket_limit == infinity then let's drop out right away and just return that because any infinity amount trumps all other "available" amounts.
930
+			if ($remaining === EE_INF) {
931
+				return EE_INF;
932
+			}
933
+			//multiply normalized $tkt quantity by the number of datetimes on the ticket as the "sum"
934
+			//also include the sum of all the datetime reg limits on the ticket for breaking ties.
935
+			$ticket_sums[$ticket->ID()]['sum'] = $remaining * count($datetimes);
936
+			$ticket_sums[$ticket->ID()]['datetime_sums'] = 0;
937
+			foreach ($datetimes as $datetime) {
938
+				if ($datetime->reg_limit() === EE_INF) {
939
+					$ticket_sums[$ticket->ID()]['datetime_sums'] = EE_INF;
940
+				} else {
941
+					$ticket_sums[$ticket->ID()]['datetime_sums'] += $current_total_available
942
+						? $datetime->spaces_remaining() : $datetime->reg_limit();
943
+				}
944
+				$datetime_limits[$datetime->ID()] = $current_total_available ? $datetime->spaces_remaining()
945
+					: $datetime->reg_limit();
946
+			}
947
+			$ticket_sums[$ticket->ID()]['ticket'] = $ticket;
948
+		}
949
+		//The order is sorted by lowest available first (which is calculated for each ticket by multiplying the normalized
950
+		//ticket quantity by the number of datetimes on the ticket).  For tie-breakers, then the next sort is based on the
951
+		//ticket with the greatest sum of all remaining datetime->spaces_remaining() ( or $datetime->reg_limit() if not
952
+		//$current_total_available ) for the datetimes on the ticket.
953
+		usort($ticket_sums, function ($a, $b) {
954
+			if ($a['sum'] == $b['sum']) {
955
+				if ($a['datetime_sums'] == $b['datetime_sums']) {
956
+					return 0;
957
+				}
958
+				return $a['datetime_sums'] < $b['datetime_sums'] ? 1 : -1;
959
+			}
960
+			return ($a['sum'] < $b['sum']) ? -1 : 1;
961
+		});
962
+		//now let's loop through the sorted tickets and simulate sellouts
963
+		foreach ($ticket_sums as $ticket_info) {
964
+			if ($ticket_info['ticket'] instanceof EE_Ticket) {
965
+				$datetimes = $ticket_info['ticket']->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
966
+				//need to sort these $datetimes by remaining (only if $current_total_available)
967
+				//setup datetimes for simulation
968
+				$ticket_datetimes_remaining = array();
969
+				foreach ($datetimes as $datetime) {
970
+					$ticket_datetimes_remaining[$datetime->ID()]['rem'] = $datetime_limits[$datetime->ID()];
971
+					$ticket_datetimes_remaining[$datetime->ID()]['datetime'] = $datetime;
972
+				}
973
+				usort($ticket_datetimes_remaining, function ($a, $b) {
974
+					if ($a['rem'] == $b['rem']) {
975
+						return 0;
976
+					}
977
+					return ($a['rem'] < $b['rem']) ? -1 : 1;
978
+				});
979
+				//get the remaining on the first datetime (which should be the one with the least remaining) and that is
980
+				//what we add to the spaces_available running total.  Then we need to decrease the remaining on our datetime tracker.
981
+				$lowest_datetime = reset($ticket_datetimes_remaining);
982
+				//need to get the lower of; what the remaining is on the lowest datetime, and the remaining on the ticket.
983
+				// If this ends up being 0 (because of previous tickets in our simulation selling out), then it has already
984
+				// been tracked on $spaces available and this ticket is now sold out for the simulation, so we can continue
985
+				// to the next ticket.
986
+				if ($current_total_available) {
987
+					$remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->remaining());
988
+				} else {
989
+					$remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->qty());
990
+				}
991
+				//if $remaining is infinite that means that all datetimes on this ticket are infinite but we've made it here because all
992
+				//tickets have a quantity.  So we don't have to track datetimes, we can just use ticket quantities for total
993
+				//available.
994
+				if ($remaining === EE_INF) {
995
+					$spaces_available += $ticket_info['ticket']->qty();
996
+					continue;
997
+				}
998
+				//if ticket has sold amounts then we also need to add that (but only if doing live counts)
999
+				if ($current_total_available) {
1000
+					$spaces_available += $ticket_info['ticket']->sold();
1001
+				}
1002
+				if ($remaining <= 0) {
1003
+					continue;
1004
+				} else {
1005
+					$spaces_available += $remaining;
1006
+				}
1007
+				//loop through the datetimes and sell them out!
1008
+				foreach ($ticket_datetimes_remaining as $datetime_info) {
1009
+					if ($datetime_info['datetime'] instanceof EE_Datetime) {
1010
+						$datetime_limits[$datetime_info['datetime']->ID()] += -$remaining;
1011
+					}
1012
+				}
1013
+			}
1014
+		}
1015
+		return $spaces_available;
1016
+	}
1017
+
1018
+
1019
+
1020
+	/**
1021
+	 * Checks if the event is set to sold out
1022
+	 *
1023
+	 * @param  bool $actual whether or not to perform calculations to not only figure the actual status but also to
1024
+	 *                      flip the status if necessary to sold out If false, we just check the existing status of the
1025
+	 *                      event
1026
+	 * @return boolean
1027
+	 */
1028
+	public function is_sold_out($actual = false)
1029
+	{
1030
+		if ( ! $actual) {
1031
+			return $this->status() == EEM_Event::sold_out;
1032
+		} else {
1033
+			return $this->perform_sold_out_status_check();
1034
+		}
1035
+	}
1036
+
1037
+
1038
+
1039
+	/**
1040
+	 * Checks if the event is marked as postponed
1041
+	 *
1042
+	 * @return boolean
1043
+	 */
1044
+	public function is_postponed()
1045
+	{
1046
+		return $this->status() == EEM_Event::postponed;
1047
+	}
1048
+
1049
+
1050
+
1051
+	/**
1052
+	 * Checks if the event is marked as cancelled
1053
+	 *
1054
+	 * @return boolean
1055
+	 */
1056
+	public function is_cancelled()
1057
+	{
1058
+		return $this->status() == EEM_Event::cancelled;
1059
+	}
1060
+
1061
+
1062
+
1063
+	/**
1064
+	 * Get the logical active status in a hierarchical order for all the datetimes.  Note
1065
+	 * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1066
+	 * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1067
+	 * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1068
+	 * the event is considered expired.
1069
+	 * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1070
+	 * status set on the EVENT when it is not published and thus is done
1071
+	 *
1072
+	 * @param bool $reset
1073
+	 * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1074
+	 */
1075
+	public function get_active_status($reset = false)
1076
+	{
1077
+		// if the active status has already been set, then just use that value (unless we are resetting it)
1078
+		if ( ! empty($this->_active_status) && ! $reset) {
1079
+			return $this->_active_status;
1080
+		}
1081
+		//first check if event id is present on this object
1082
+		if ( ! $this->ID()) {
1083
+			return false;
1084
+		}
1085
+		$where_params_for_event = array(array('EVT_ID' => $this->ID()));
1086
+		//if event is published:
1087
+		if ($this->status() === 'publish') {
1088
+			//active?
1089
+			if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::active, $where_params_for_event)
1090
+				> 0
1091
+			) {
1092
+				$this->_active_status = EE_Datetime::active;
1093
+			} else {
1094
+				//upcoming?
1095
+				if (EEM_Datetime::instance()
1096
+								->get_datetime_count_for_status(EE_Datetime::upcoming, $where_params_for_event) > 0
1097
+				) {
1098
+					$this->_active_status = EE_Datetime::upcoming;
1099
+				} else {
1100
+					//expired?
1101
+					if (EEM_Datetime::instance()
1102
+									->get_datetime_count_for_status(EE_Datetime::expired, $where_params_for_event) > 0
1103
+					) {
1104
+						$this->_active_status = EE_Datetime::expired;
1105
+					} else {
1106
+						//it would be odd if things make it this far because it basically means there are no datetime's
1107
+						//attached to the event.  So in this case it will just be considered inactive.
1108
+						$this->_active_status = EE_Datetime::inactive;
1109
+					}
1110
+				}
1111
+			}
1112
+		} else {
1113
+			//the event is not published, so let's just set it's active status according to its' post status
1114
+			switch ($this->status()) {
1115
+				case EEM_Event::sold_out :
1116
+					$this->_active_status = EE_Datetime::sold_out;
1117
+					break;
1118
+				case EEM_Event::cancelled :
1119
+					$this->_active_status = EE_Datetime::cancelled;
1120
+					break;
1121
+				case EEM_Event::postponed :
1122
+					$this->_active_status = EE_Datetime::postponed;
1123
+					break;
1124
+				default :
1125
+					$this->_active_status = EE_Datetime::inactive;
1126
+			}
1127
+		}
1128
+		return $this->_active_status;
1129
+	}
1130
+
1131
+
1132
+
1133
+	/**
1134
+	 *    pretty_active_status
1135
+	 *
1136
+	 * @access public
1137
+	 * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1138
+	 * @return mixed void|string
1139
+	 */
1140
+	public function pretty_active_status($echo = true)
1141
+	{
1142
+		$active_status = $this->get_active_status();
1143
+		$status = '<span class="ee-status event-active-status-'
1144
+				  . $active_status
1145
+				  . '">'
1146
+				  . EEH_Template::pretty_status($active_status, false, 'sentence')
1147
+				  . '</span>';
1148
+		if ($echo) {
1149
+			echo $status;
1150
+			return '';
1151
+		}
1152
+		return $status;
1153
+	}
1154
+
1155
+
1156
+
1157
+	/**
1158
+	 * @return bool|int
1159
+	 */
1160
+	public function get_number_of_tickets_sold()
1161
+	{
1162
+		$tkt_sold = 0;
1163
+		if ( ! $this->ID()) {
1164
+			return 0;
1165
+		}
1166
+		$datetimes = $this->datetimes();
1167
+		foreach ($datetimes as $datetime) {
1168
+			if ($datetime instanceof EE_Datetime) {
1169
+				$tkt_sold += $datetime->sold();
1170
+			}
1171
+		}
1172
+		return $tkt_sold;
1173
+	}
1174
+
1175
+
1176
+
1177
+	/**
1178
+	 * This just returns a count of all the registrations for this event
1179
+	 *
1180
+	 * @access  public
1181
+	 * @return int
1182
+	 */
1183
+	public function get_count_of_all_registrations()
1184
+	{
1185
+		return EEM_Event::instance()->count_related($this, 'Registration');
1186
+	}
1187
+
1188
+
1189
+
1190
+	/**
1191
+	 * This returns the ticket with the earliest start time that is available for this event (across all datetimes
1192
+	 * attached to the event)
1193
+	 *
1194
+	 * @return EE_Ticket
1195
+	 */
1196
+	public function get_ticket_with_earliest_start_time()
1197
+	{
1198
+		$where['Datetime.EVT_ID'] = $this->ID();
1199
+		$query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1200
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1201
+	}
1202
+
1203
+
1204
+
1205
+	/**
1206
+	 * This returns the ticket with the latest end time that is available for this event (across all datetimes attached
1207
+	 * to the event)
1208
+	 *
1209
+	 * @return EE_Ticket
1210
+	 */
1211
+	public function get_ticket_with_latest_end_time()
1212
+	{
1213
+		$where['Datetime.EVT_ID'] = $this->ID();
1214
+		$query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1215
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1216
+	}
1217
+
1218
+
1219
+
1220
+	/**
1221
+	 * This returns whether there are any tickets on sale for this event.
1222
+	 *
1223
+	 * @return bool true = YES tickets on sale.
1224
+	 */
1225
+	public function tickets_on_sale()
1226
+	{
1227
+		$earliest_ticket = $this->get_ticket_with_earliest_start_time();
1228
+		$latest_ticket = $this->get_ticket_with_latest_end_time();
1229
+		if ( ! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1230
+			return false;
1231
+		}
1232
+		//check on sale for these two tickets.
1233
+		if ($latest_ticket->is_on_sale() || $earliest_ticket->is_on_sale()) {
1234
+			return true;
1235
+		}
1236
+		return false;
1237
+	}
1238
+
1239
+
1240
+
1241
+	/**
1242
+	 * Gets the URL for viewing this event on the front-end. Overrides parent
1243
+	 * to check for an external URL first
1244
+	 *
1245
+	 * @return string
1246
+	 */
1247
+	public function get_permalink()
1248
+	{
1249
+		if ($this->external_url()) {
1250
+			return $this->external_url();
1251
+		} else {
1252
+			return parent::get_permalink();
1253
+		}
1254
+	}
1255
+
1256
+
1257
+
1258
+	/**
1259
+	 * Gets the first term for 'espresso_event_categories' we can find
1260
+	 *
1261
+	 * @param array $query_params like EEM_Base::get_all
1262
+	 * @return EE_Term
1263
+	 */
1264
+	public function first_event_category($query_params = array())
1265
+	{
1266
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1267
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1268
+		return EEM_Term::instance()->get_one($query_params);
1269
+	}
1270
+
1271
+
1272
+
1273
+	/**
1274
+	 * Gets all terms for 'espresso_event_categories' we can find
1275
+	 *
1276
+	 * @param array $query_params
1277
+	 * @return EE_Term[]
1278
+	 */
1279
+	public function get_all_event_categories($query_params = array())
1280
+	{
1281
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1282
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1283
+		return EEM_Term::instance()->get_all($query_params);
1284
+	}
1285
+
1286
+
1287
+
1288
+	/**
1289
+	 * Gets all the question groups, ordering them by QSG_order ascending
1290
+	 *
1291
+	 * @param array $query_params @see EEM_Base::get_all
1292
+	 * @return EE_Question_Group[]
1293
+	 */
1294
+	public function question_groups($query_params = array())
1295
+	{
1296
+		$query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1297
+		return $this->get_many_related('Question_Group', $query_params);
1298
+	}
1299
+
1300
+
1301
+
1302
+	/**
1303
+	 * Implementation for EEI_Has_Icon interface method.
1304
+	 *
1305
+	 * @see EEI_Visual_Representation for comments
1306
+	 * @return string
1307
+	 */
1308
+	public function get_icon()
1309
+	{
1310
+		return '<span class="dashicons dashicons-flag"></span>';
1311
+	}
1312
+
1313
+
1314
+
1315
+	/**
1316
+	 * Implementation for EEI_Admin_Links interface method.
1317
+	 *
1318
+	 * @see EEI_Admin_Links for comments
1319
+	 * @return string
1320
+	 */
1321
+	public function get_admin_details_link()
1322
+	{
1323
+		return $this->get_admin_edit_link();
1324
+	}
1325
+
1326
+
1327
+
1328
+	/**
1329
+	 * Implementation for EEI_Admin_Links interface method.
1330
+	 *
1331
+	 * @see EEI_Admin_Links for comments
1332
+	 * @return string
1333
+	 */
1334
+	public function get_admin_edit_link()
1335
+	{
1336
+		return EEH_URL::add_query_args_and_nonce(array(
1337
+			'page'   => 'espresso_events',
1338
+			'action' => 'edit',
1339
+			'post'   => $this->ID(),
1340
+		),
1341
+			admin_url('admin.php')
1342
+		);
1343
+	}
1344
+
1345
+
1346
+
1347
+	/**
1348
+	 * Implementation for EEI_Admin_Links interface method.
1349
+	 *
1350
+	 * @see EEI_Admin_Links for comments
1351
+	 * @return string
1352
+	 */
1353
+	public function get_admin_settings_link()
1354
+	{
1355
+		return EEH_URL::add_query_args_and_nonce(array(
1356
+			'page'   => 'espresso_events',
1357
+			'action' => 'default_event_settings',
1358
+		),
1359
+			admin_url('admin.php')
1360
+		);
1361
+	}
1362
+
1363
+
1364
+
1365
+	/**
1366
+	 * Implementation for EEI_Admin_Links interface method.
1367
+	 *
1368
+	 * @see EEI_Admin_Links for comments
1369
+	 * @return string
1370
+	 */
1371
+	public function get_admin_overview_link()
1372
+	{
1373
+		return EEH_URL::add_query_args_and_nonce(array(
1374
+			'page'   => 'espresso_events',
1375
+			'action' => 'default'
1376
+		),
1377
+			admin_url('admin.php')
1378
+		);
1379
+	}
1380 1380
 
1381 1381
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
      */
301 301
     public function display_ticket_selector()
302 302
     {
303
-        return (bool)$this->get('EVT_display_ticket_selector');
303
+        return (bool) $this->get('EVT_display_ticket_selector');
304 304
     }
305 305
 
306 306
 
@@ -950,7 +950,7 @@  discard block
 block discarded – undo
950 950
         //ticket quantity by the number of datetimes on the ticket).  For tie-breakers, then the next sort is based on the
951 951
         //ticket with the greatest sum of all remaining datetime->spaces_remaining() ( or $datetime->reg_limit() if not
952 952
         //$current_total_available ) for the datetimes on the ticket.
953
-        usort($ticket_sums, function ($a, $b) {
953
+        usort($ticket_sums, function($a, $b) {
954 954
             if ($a['sum'] == $b['sum']) {
955 955
                 if ($a['datetime_sums'] == $b['datetime_sums']) {
956 956
                     return 0;
@@ -970,7 +970,7 @@  discard block
 block discarded – undo
970 970
                     $ticket_datetimes_remaining[$datetime->ID()]['rem'] = $datetime_limits[$datetime->ID()];
971 971
                     $ticket_datetimes_remaining[$datetime->ID()]['datetime'] = $datetime;
972 972
                 }
973
-                usort($ticket_datetimes_remaining, function ($a, $b) {
973
+                usort($ticket_datetimes_remaining, function($a, $b) {
974 974
                     if ($a['rem'] == $b['rem']) {
975 975
                         return 0;
976 976
                     }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page_CPT.core.php 2 patches
Indentation   +1352 added lines, -1352 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -26,423 +26,423 @@  discard block
 block discarded – undo
26 26
 {
27 27
 
28 28
 
29
-    /**
30
-     * This gets set in _setup_cpt
31
-     * It will contain the object for the custom post type.
32
-     *
33
-     * @var object
34
-     */
35
-    protected $_cpt_object;
29
+	/**
30
+	 * This gets set in _setup_cpt
31
+	 * It will contain the object for the custom post type.
32
+	 *
33
+	 * @var object
34
+	 */
35
+	protected $_cpt_object;
36 36
 
37 37
 
38
-
39
-    /**
40
-     * a boolean flag to set whether the current route is a cpt route or not.
41
-     *
42
-     * @var bool
43
-     */
44
-    protected $_cpt_route = false;
38
+
39
+	/**
40
+	 * a boolean flag to set whether the current route is a cpt route or not.
41
+	 *
42
+	 * @var bool
43
+	 */
44
+	protected $_cpt_route = false;
45 45
 
46 46
 
47 47
 
48
-    /**
49
-     * This property allows cpt classes to define multiple routes as cpt routes.
50
-     * //in this array we define what the custom post type for this route is.
51
-     * array(
52
-     * 'route_name' => 'custom_post_type_slug'
53
-     * )
54
-     *
55
-     * @var array
56
-     */
57
-    protected $_cpt_routes = array();
48
+	/**
49
+	 * This property allows cpt classes to define multiple routes as cpt routes.
50
+	 * //in this array we define what the custom post type for this route is.
51
+	 * array(
52
+	 * 'route_name' => 'custom_post_type_slug'
53
+	 * )
54
+	 *
55
+	 * @var array
56
+	 */
57
+	protected $_cpt_routes = array();
58 58
 
59 59
 
60 60
 
61
-    /**
62
-     * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
63
-     * in this format:
64
-     * array(
65
-     * 'post_type_slug' => 'edit_route'
66
-     * )
67
-     *
68
-     * @var array
69
-     */
70
-    protected $_cpt_edit_routes = array();
61
+	/**
62
+	 * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
63
+	 * in this format:
64
+	 * array(
65
+	 * 'post_type_slug' => 'edit_route'
66
+	 * )
67
+	 *
68
+	 * @var array
69
+	 */
70
+	protected $_cpt_edit_routes = array();
71 71
 
72 72
 
73 73
 
74
-    /**
75
-     * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
76
-     * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
77
-     * _cpt_model_names property should be in the following format: array(
78
-     * 'route_defined_by_action_param' => 'Model_Name')
79
-     *
80
-     * @var array $_cpt_model_names
81
-     */
82
-    protected $_cpt_model_names = array();
83
-
84
-
85
-    /**
86
-     * @var EE_CPT_Base
87
-     */
88
-    protected $_cpt_model_obj = false;
89
-
90
-
91
-
92
-    /**
93
-     * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
94
-     * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
95
-     * the _register_autosave_containers() method so that we don't override any other containers already registered.
96
-     * Registration of containers should be done before load_page_dependencies() is run.
97
-     *
98
-     * @var array()
99
-     */
100
-    protected $_autosave_containers = array();
101
-
102
-    protected $_autosave_fields     = array();
103
-
104
-    /**
105
-     * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
106
-     * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
107
-     *
108
-     * @var array
109
-     */
110
-    protected $_pagenow_map = null;
111
-
112
-
113
-
114
-    /**
115
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
116
-     * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
117
-     * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
118
-     * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
119
-     * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
120
-     *
121
-     * @access protected
122
-     * @abstract
123
-     * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
124
-     * @param  object $post    The post object of the cpt that was saved.
125
-     * @return void
126
-     */
127
-    abstract protected function _insert_update_cpt_item($post_id, $post);
128
-
129
-
130
-
131
-    /**
132
-     * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
133
-     *
134
-     * @abstract
135
-     * @access public
136
-     * @param  string $post_id The ID of the cpt that was trashed
137
-     * @return void
138
-     */
139
-    abstract public function trash_cpt_item($post_id);
140
-
141
-
142
-
143
-    /**
144
-     * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
145
-     *
146
-     * @param  string $post_id theID of the cpt that was untrashed
147
-     * @return void
148
-     */
149
-    abstract public function restore_cpt_item($post_id);
150
-
151
-
152
-
153
-    /**
154
-     * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
155
-     * from the db
156
-     *
157
-     * @param  string $post_id the ID of the cpt that was deleted
158
-     * @return void
159
-     */
160
-    abstract public function delete_cpt_item($post_id);
161
-
162
-
163
-
164
-    /**
165
-     * Just utilizing the method EE_Admin exposes for doing things before page setup.
166
-     *
167
-     * @access protected
168
-     * @return void
169
-     */
170
-    protected function _before_page_setup()
171
-    {
172
-        $page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
173
-        $this->_cpt_routes = array_merge(array(
174
-            'create_new' => $this->page_slug,
175
-            'edit'       => $this->page_slug,
176
-            'trash'      => $this->page_slug,
177
-        ), $this->_cpt_routes);
178
-        //let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
179
-        $this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[$this->_req_data['action']])
180
-            ? get_post_type_object($this->_cpt_routes[$this->_req_data['action']]) : get_post_type_object($page);
181
-        //tweak pagenow for page loading.
182
-        if ( ! $this->_pagenow_map) {
183
-            $this->_pagenow_map = array(
184
-                'create_new' => 'post-new.php',
185
-                'edit'       => 'post.php',
186
-                'trash'      => 'post.php',
187
-            );
188
-        }
189
-        add_action('current_screen', array($this, 'modify_pagenow'));
190
-        //TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
191
-        //get current page from autosave
192
-        $current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
193
-            ? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'] : null;
194
-        $this->_current_page = isset($this->_req_data['current_page']) ? $this->_req_data['current_page']
195
-            : $current_page;
196
-        //autosave... make sure its only for the correct page
197
-        if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
198
-            //setup autosave ajax hook
199
-            //add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
200
-        }
201
-    }
202
-
203
-
204
-
205
-    /**
206
-     * Simply ensure that we simulate the correct post route for cpt screens
207
-     *
208
-     * @param WP_Screen $current_screen
209
-     * @return void
210
-     */
211
-    public function modify_pagenow($current_screen)
212
-    {
213
-        global $pagenow, $hook_suffix;
214
-        //possibly reset pagenow.
215
-        if ( ! empty($this->_req_data['page'])
216
-             && $this->_req_data['page'] == $this->page_slug
217
-             && ! empty($this->_req_data['action'])
218
-             && isset($this->_pagenow_map[$this->_req_data['action']])
219
-        ) {
220
-            $pagenow = $this->_pagenow_map[$this->_req_data['action']];
221
-            $hook_suffix = $pagenow;
222
-        }
223
-    }
224
-
225
-
226
-
227
-    /**
228
-     * This method is used to register additional autosave containers to the _autosave_containers property.
229
-     *
230
-     * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
231
-     *       automatically register the id for the post metabox as a container.
232
-     * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
233
-     *                    you would send along the id of a metabox container.
234
-     * @return void
235
-     */
236
-    protected function _register_autosave_containers($ids)
237
-    {
238
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
239
-    }
240
-
241
-
242
-
243
-    /**
244
-     * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
245
-     * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
246
-     */
247
-    protected function _set_autosave_containers()
248
-    {
249
-        global $wp_meta_boxes;
250
-        $containers = array();
251
-        if (empty($wp_meta_boxes)) {
252
-            return;
253
-        }
254
-        $current_metaboxes = isset($wp_meta_boxes[$this->page_slug]) ? $wp_meta_boxes[$this->page_slug] : array();
255
-        foreach ($current_metaboxes as $box_context) {
256
-            foreach ($box_context as $box_details) {
257
-                foreach ($box_details as $box) {
258
-                    if (is_array($box['callback'])
259
-                        && ($box['callback'][0] instanceof EE_Admin_Page
260
-                            || $box['callback'][0] instanceof EE_Admin_Hooks)
261
-                    ) {
262
-                        $containers[] = $box['id'];
263
-                    }
264
-                }
265
-            }
266
-        }
267
-        $this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
268
-        //add hidden inputs container
269
-        $this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
270
-    }
271
-
272
-
273
-
274
-    protected function _load_autosave_scripts_styles()
275
-    {
276
-        /*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
74
+	/**
75
+	 * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
76
+	 * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
77
+	 * _cpt_model_names property should be in the following format: array(
78
+	 * 'route_defined_by_action_param' => 'Model_Name')
79
+	 *
80
+	 * @var array $_cpt_model_names
81
+	 */
82
+	protected $_cpt_model_names = array();
83
+
84
+
85
+	/**
86
+	 * @var EE_CPT_Base
87
+	 */
88
+	protected $_cpt_model_obj = false;
89
+
90
+
91
+
92
+	/**
93
+	 * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
94
+	 * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
95
+	 * the _register_autosave_containers() method so that we don't override any other containers already registered.
96
+	 * Registration of containers should be done before load_page_dependencies() is run.
97
+	 *
98
+	 * @var array()
99
+	 */
100
+	protected $_autosave_containers = array();
101
+
102
+	protected $_autosave_fields     = array();
103
+
104
+	/**
105
+	 * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
106
+	 * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
107
+	 *
108
+	 * @var array
109
+	 */
110
+	protected $_pagenow_map = null;
111
+
112
+
113
+
114
+	/**
115
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
116
+	 * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
117
+	 * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
118
+	 * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
119
+	 * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
120
+	 *
121
+	 * @access protected
122
+	 * @abstract
123
+	 * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
124
+	 * @param  object $post    The post object of the cpt that was saved.
125
+	 * @return void
126
+	 */
127
+	abstract protected function _insert_update_cpt_item($post_id, $post);
128
+
129
+
130
+
131
+	/**
132
+	 * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
133
+	 *
134
+	 * @abstract
135
+	 * @access public
136
+	 * @param  string $post_id The ID of the cpt that was trashed
137
+	 * @return void
138
+	 */
139
+	abstract public function trash_cpt_item($post_id);
140
+
141
+
142
+
143
+	/**
144
+	 * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
145
+	 *
146
+	 * @param  string $post_id theID of the cpt that was untrashed
147
+	 * @return void
148
+	 */
149
+	abstract public function restore_cpt_item($post_id);
150
+
151
+
152
+
153
+	/**
154
+	 * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
155
+	 * from the db
156
+	 *
157
+	 * @param  string $post_id the ID of the cpt that was deleted
158
+	 * @return void
159
+	 */
160
+	abstract public function delete_cpt_item($post_id);
161
+
162
+
163
+
164
+	/**
165
+	 * Just utilizing the method EE_Admin exposes for doing things before page setup.
166
+	 *
167
+	 * @access protected
168
+	 * @return void
169
+	 */
170
+	protected function _before_page_setup()
171
+	{
172
+		$page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
173
+		$this->_cpt_routes = array_merge(array(
174
+			'create_new' => $this->page_slug,
175
+			'edit'       => $this->page_slug,
176
+			'trash'      => $this->page_slug,
177
+		), $this->_cpt_routes);
178
+		//let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
179
+		$this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[$this->_req_data['action']])
180
+			? get_post_type_object($this->_cpt_routes[$this->_req_data['action']]) : get_post_type_object($page);
181
+		//tweak pagenow for page loading.
182
+		if ( ! $this->_pagenow_map) {
183
+			$this->_pagenow_map = array(
184
+				'create_new' => 'post-new.php',
185
+				'edit'       => 'post.php',
186
+				'trash'      => 'post.php',
187
+			);
188
+		}
189
+		add_action('current_screen', array($this, 'modify_pagenow'));
190
+		//TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
191
+		//get current page from autosave
192
+		$current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
193
+			? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'] : null;
194
+		$this->_current_page = isset($this->_req_data['current_page']) ? $this->_req_data['current_page']
195
+			: $current_page;
196
+		//autosave... make sure its only for the correct page
197
+		if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
198
+			//setup autosave ajax hook
199
+			//add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
200
+		}
201
+	}
202
+
203
+
204
+
205
+	/**
206
+	 * Simply ensure that we simulate the correct post route for cpt screens
207
+	 *
208
+	 * @param WP_Screen $current_screen
209
+	 * @return void
210
+	 */
211
+	public function modify_pagenow($current_screen)
212
+	{
213
+		global $pagenow, $hook_suffix;
214
+		//possibly reset pagenow.
215
+		if ( ! empty($this->_req_data['page'])
216
+			 && $this->_req_data['page'] == $this->page_slug
217
+			 && ! empty($this->_req_data['action'])
218
+			 && isset($this->_pagenow_map[$this->_req_data['action']])
219
+		) {
220
+			$pagenow = $this->_pagenow_map[$this->_req_data['action']];
221
+			$hook_suffix = $pagenow;
222
+		}
223
+	}
224
+
225
+
226
+
227
+	/**
228
+	 * This method is used to register additional autosave containers to the _autosave_containers property.
229
+	 *
230
+	 * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
231
+	 *       automatically register the id for the post metabox as a container.
232
+	 * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
233
+	 *                    you would send along the id of a metabox container.
234
+	 * @return void
235
+	 */
236
+	protected function _register_autosave_containers($ids)
237
+	{
238
+		$this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
239
+	}
240
+
241
+
242
+
243
+	/**
244
+	 * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
245
+	 * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
246
+	 */
247
+	protected function _set_autosave_containers()
248
+	{
249
+		global $wp_meta_boxes;
250
+		$containers = array();
251
+		if (empty($wp_meta_boxes)) {
252
+			return;
253
+		}
254
+		$current_metaboxes = isset($wp_meta_boxes[$this->page_slug]) ? $wp_meta_boxes[$this->page_slug] : array();
255
+		foreach ($current_metaboxes as $box_context) {
256
+			foreach ($box_context as $box_details) {
257
+				foreach ($box_details as $box) {
258
+					if (is_array($box['callback'])
259
+						&& ($box['callback'][0] instanceof EE_Admin_Page
260
+							|| $box['callback'][0] instanceof EE_Admin_Hooks)
261
+					) {
262
+						$containers[] = $box['id'];
263
+					}
264
+				}
265
+			}
266
+		}
267
+		$this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
268
+		//add hidden inputs container
269
+		$this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
270
+	}
271
+
272
+
273
+
274
+	protected function _load_autosave_scripts_styles()
275
+	{
276
+		/*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
277 277
         wp_enqueue_script('cpt-autosave');/**/ //todo re-enable when we start doing autosave again in 4.2
278
-        //filter _autosave_containers
279
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
280
-            $this->_autosave_containers, $this);
281
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__'
282
-                                    . get_class($this)
283
-                                    . '___load_autosave_scripts_styles__containers', $containers, $this);
284
-        wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
285
-            $containers); //todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
286
-        $unsaved_data_msg = array(
287
-            'eventmsg'     => sprintf(__("The changes you made to this %s will be lost if you navigate away from this page.",
288
-                'event_espresso'), $this->_cpt_object->labels->singular_name),
289
-            'inputChanged' => 0,
290
-        );
291
-        wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
292
-    }
293
-
294
-
295
-
296
-    public function load_page_dependencies()
297
-    {
298
-        try {
299
-            $this->_load_page_dependencies();
300
-        } catch (EE_Error $e) {
301
-            $e->get_error();
302
-        }
303
-    }
304
-
305
-
306
-
307
-    /**
308
-     * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
309
-     *
310
-     * @access protected
311
-     * @return void
312
-     */
313
-    protected function _load_page_dependencies()
314
-    {
315
-        //we only add stuff if this is a cpt_route!
316
-        if ( ! $this->_cpt_route) {
317
-            parent::_load_page_dependencies();
318
-            return;
319
-        }
320
-        //now let's do some automatic filters into the wp_system and we'll check to make sure the CHILD class automatically has the required methods in place.
321
-        //the following filters are for setting all the redirects on DEFAULT WP custom post type actions
322
-        //let's add a hidden input to the post-edit form so we know when we have to trigger our custom redirects!  Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
323
-        add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
324
-        //inject our Admin page nav tabs...
325
-        //let's make sure the nav tabs are set if they aren't already
326
-        //if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
327
-        add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
328
-        //modify the post_updated messages array
329
-        add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
330
-        //add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE, cpts use the same format for shortlinks as posts!
331
-        add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
332
-        //This basically allows us to change the title of the "publish" metabox area on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
333
-        if ( ! empty($this->_labels['publishbox'])) {
334
-            $box_label = is_array($this->_labels['publishbox'])
335
-                         && isset($this->_labels['publishbox'][$this->_req_action])
336
-                ? $this->_labels['publishbox'][$this->_req_action] : $this->_labels['publishbox'];
337
-            remove_meta_box('submitdiv', __('Publish'), 'post_submit_meta_box', $this->_cpt_routes[$this->_req_action],
338
-                'side', 'core');
339
-            add_meta_box('submitdiv', $box_label, 'post_submit_meta_box', $this->_cpt_routes[$this->_req_action],
340
-                'side', 'core');
341
-        }
342
-        //let's add page_templates metabox if this cpt added support for it.
343
-        if ($this->_supports_page_templates($this->_cpt_object->name)) {
344
-            add_meta_box('page_templates', __('Page Template', 'event_espresso'),
345
-                array($this, 'page_template_meta_box'), $this->_cpt_routes[$this->_req_action], 'side', 'default');
346
-        }/**/
347
-        //this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
348
-        if (method_exists($this, 'extra_permalink_field_buttons')) {
349
-            add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
350
-        }
351
-        //add preview button
352
-        add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
353
-        //insert our own post_stati dropdown
354
-        add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
355
-        //This allows adding additional information to the publish post submitbox on the wp post edit form
356
-        if (method_exists($this, 'extra_misc_actions_publish_box')) {
357
-            add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
358
-        }
359
-        //This allows for adding additional stuff after the title field on the wp post edit form.  This is also before the wp_editor for post description field.
360
-        if (method_exists($this, 'edit_form_after_title')) {
361
-            add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
362
-        }
363
-        /**
364
-         * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
365
-         */
366
-        add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
367
-        parent::_load_page_dependencies();
368
-        //notice we are ALSO going to load the pagenow hook set for this route (see _before_page_setup for the reset of the pagenow global ). This is for any plugins that are doing things properly and hooking into the load page hook for core wp cpt routes.
369
-        global $pagenow;
370
-        do_action('load-' . $pagenow);
371
-        $this->modify_current_screen();
372
-        add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
373
-        //we route REALLY early.
374
-        try {
375
-            $this->_route_admin_request();
376
-        } catch (EE_Error $e) {
377
-            $e->get_error();
378
-        }
379
-    }
380
-
381
-
382
-
383
-    /**
384
-     * Since we don't want users going to default core wp routes, this will check any wp urls run through the
385
-     * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
386
-     * route instead.
387
-     *
388
-     * @param string $good_protocol_url The escaped url.
389
-     * @param string $original_url      The original url.
390
-     * @param string $_context          The context sendt to the esc_url method.
391
-     * @return string possibly a new url for our route.
392
-     */
393
-    public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
394
-    {
395
-        $routes_to_match = array(
396
-            0 => array(
397
-                'edit.php?post_type=espresso_attendees',
398
-                'admin.php?page=espresso_registrations&action=contact_list',
399
-            ),
400
-            1 => array(
401
-                'edit.php?post_type=' . $this->_cpt_object->name,
402
-                'admin.php?page=' . $this->_cpt_object->name,
403
-            ),
404
-        );
405
-        foreach ($routes_to_match as $route_matches) {
406
-            if (strpos($good_protocol_url, $route_matches[0]) !== false) {
407
-                return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
408
-            }
409
-        }
410
-        return $good_protocol_url;
411
-    }
412
-
413
-
414
-
415
-    /**
416
-     * Determine whether the current cpt supports page templates or not.
417
-     *
418
-     * @since %VER%
419
-     * @param string $cpt_name The cpt slug we're checking on.
420
-     * @return bool True supported, false not.
421
-     */
422
-    private function _supports_page_templates($cpt_name)
423
-    {
424
-        $cpt_args = EE_Register_CPTs::get_CPTs();
425
-        $cpt_args = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : array();
426
-        return ! empty($cpt_args['page_templates']) ? true : false;
427
-    }
428
-
429
-
430
-
431
-    /**
432
-     * Callback for the page_templates metabox selector.
433
-     *
434
-     * @since %VER%
435
-     * @return string html
436
-     */
437
-    public function page_template_meta_box()
438
-    {
439
-        global $post;
440
-        $template = '';
441
-        if (count(get_page_templates($post)) != 0) {
442
-            $page_template = get_post_meta($post->ID, '_wp_page_template', true);
443
-            $template = ! empty($page_template) ? $page_template : '';
444
-        }
445
-        ?>
278
+		//filter _autosave_containers
279
+		$containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
280
+			$this->_autosave_containers, $this);
281
+		$containers = apply_filters('FHEE__EE_Admin_Page_CPT__'
282
+									. get_class($this)
283
+									. '___load_autosave_scripts_styles__containers', $containers, $this);
284
+		wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
285
+			$containers); //todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
286
+		$unsaved_data_msg = array(
287
+			'eventmsg'     => sprintf(__("The changes you made to this %s will be lost if you navigate away from this page.",
288
+				'event_espresso'), $this->_cpt_object->labels->singular_name),
289
+			'inputChanged' => 0,
290
+		);
291
+		wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
292
+	}
293
+
294
+
295
+
296
+	public function load_page_dependencies()
297
+	{
298
+		try {
299
+			$this->_load_page_dependencies();
300
+		} catch (EE_Error $e) {
301
+			$e->get_error();
302
+		}
303
+	}
304
+
305
+
306
+
307
+	/**
308
+	 * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
309
+	 *
310
+	 * @access protected
311
+	 * @return void
312
+	 */
313
+	protected function _load_page_dependencies()
314
+	{
315
+		//we only add stuff if this is a cpt_route!
316
+		if ( ! $this->_cpt_route) {
317
+			parent::_load_page_dependencies();
318
+			return;
319
+		}
320
+		//now let's do some automatic filters into the wp_system and we'll check to make sure the CHILD class automatically has the required methods in place.
321
+		//the following filters are for setting all the redirects on DEFAULT WP custom post type actions
322
+		//let's add a hidden input to the post-edit form so we know when we have to trigger our custom redirects!  Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
323
+		add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
324
+		//inject our Admin page nav tabs...
325
+		//let's make sure the nav tabs are set if they aren't already
326
+		//if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
327
+		add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
328
+		//modify the post_updated messages array
329
+		add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
330
+		//add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE, cpts use the same format for shortlinks as posts!
331
+		add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
332
+		//This basically allows us to change the title of the "publish" metabox area on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
333
+		if ( ! empty($this->_labels['publishbox'])) {
334
+			$box_label = is_array($this->_labels['publishbox'])
335
+						 && isset($this->_labels['publishbox'][$this->_req_action])
336
+				? $this->_labels['publishbox'][$this->_req_action] : $this->_labels['publishbox'];
337
+			remove_meta_box('submitdiv', __('Publish'), 'post_submit_meta_box', $this->_cpt_routes[$this->_req_action],
338
+				'side', 'core');
339
+			add_meta_box('submitdiv', $box_label, 'post_submit_meta_box', $this->_cpt_routes[$this->_req_action],
340
+				'side', 'core');
341
+		}
342
+		//let's add page_templates metabox if this cpt added support for it.
343
+		if ($this->_supports_page_templates($this->_cpt_object->name)) {
344
+			add_meta_box('page_templates', __('Page Template', 'event_espresso'),
345
+				array($this, 'page_template_meta_box'), $this->_cpt_routes[$this->_req_action], 'side', 'default');
346
+		}/**/
347
+		//this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
348
+		if (method_exists($this, 'extra_permalink_field_buttons')) {
349
+			add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
350
+		}
351
+		//add preview button
352
+		add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
353
+		//insert our own post_stati dropdown
354
+		add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
355
+		//This allows adding additional information to the publish post submitbox on the wp post edit form
356
+		if (method_exists($this, 'extra_misc_actions_publish_box')) {
357
+			add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
358
+		}
359
+		//This allows for adding additional stuff after the title field on the wp post edit form.  This is also before the wp_editor for post description field.
360
+		if (method_exists($this, 'edit_form_after_title')) {
361
+			add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
362
+		}
363
+		/**
364
+		 * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
365
+		 */
366
+		add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
367
+		parent::_load_page_dependencies();
368
+		//notice we are ALSO going to load the pagenow hook set for this route (see _before_page_setup for the reset of the pagenow global ). This is for any plugins that are doing things properly and hooking into the load page hook for core wp cpt routes.
369
+		global $pagenow;
370
+		do_action('load-' . $pagenow);
371
+		$this->modify_current_screen();
372
+		add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
373
+		//we route REALLY early.
374
+		try {
375
+			$this->_route_admin_request();
376
+		} catch (EE_Error $e) {
377
+			$e->get_error();
378
+		}
379
+	}
380
+
381
+
382
+
383
+	/**
384
+	 * Since we don't want users going to default core wp routes, this will check any wp urls run through the
385
+	 * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
386
+	 * route instead.
387
+	 *
388
+	 * @param string $good_protocol_url The escaped url.
389
+	 * @param string $original_url      The original url.
390
+	 * @param string $_context          The context sendt to the esc_url method.
391
+	 * @return string possibly a new url for our route.
392
+	 */
393
+	public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
394
+	{
395
+		$routes_to_match = array(
396
+			0 => array(
397
+				'edit.php?post_type=espresso_attendees',
398
+				'admin.php?page=espresso_registrations&action=contact_list',
399
+			),
400
+			1 => array(
401
+				'edit.php?post_type=' . $this->_cpt_object->name,
402
+				'admin.php?page=' . $this->_cpt_object->name,
403
+			),
404
+		);
405
+		foreach ($routes_to_match as $route_matches) {
406
+			if (strpos($good_protocol_url, $route_matches[0]) !== false) {
407
+				return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
408
+			}
409
+		}
410
+		return $good_protocol_url;
411
+	}
412
+
413
+
414
+
415
+	/**
416
+	 * Determine whether the current cpt supports page templates or not.
417
+	 *
418
+	 * @since %VER%
419
+	 * @param string $cpt_name The cpt slug we're checking on.
420
+	 * @return bool True supported, false not.
421
+	 */
422
+	private function _supports_page_templates($cpt_name)
423
+	{
424
+		$cpt_args = EE_Register_CPTs::get_CPTs();
425
+		$cpt_args = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : array();
426
+		return ! empty($cpt_args['page_templates']) ? true : false;
427
+	}
428
+
429
+
430
+
431
+	/**
432
+	 * Callback for the page_templates metabox selector.
433
+	 *
434
+	 * @since %VER%
435
+	 * @return string html
436
+	 */
437
+	public function page_template_meta_box()
438
+	{
439
+		global $post;
440
+		$template = '';
441
+		if (count(get_page_templates($post)) != 0) {
442
+			$page_template = get_post_meta($post->ID, '_wp_page_template', true);
443
+			$template = ! empty($page_template) ? $page_template : '';
444
+		}
445
+		?>
446 446
         <p><strong><?php _e('Template') ?></strong></p>
447 447
         <label class="screen-reader-text" for="page_template"><?php _e('Page Template') ?></label><select
448 448
             name="page_template" id="page_template">
@@ -450,435 +450,435 @@  discard block
 block discarded – undo
450 450
         <?php page_template_dropdown($template); ?>
451 451
     </select>
452 452
         <?php
453
-    }
454
-
455
-
456
-
457
-    /**
458
-     * if this post is a draft or scheduled post then we provide a preview button for user to click
459
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
460
-     *
461
-     * @param  string $return    the current html
462
-     * @param  int    $id        the post id for the page
463
-     * @param  string $new_title What the title is
464
-     * @param  string $new_slug  what the slug is
465
-     * @return string            The new html string for the permalink area
466
-     */
467
-    public function preview_button_html($return, $id, $new_title, $new_slug)
468
-    {
469
-        $post = get_post($id);
470
-        if ('publish' != get_post_status($post)) {
471
-            $return .= '<span_id="view-post-btn"><a href="'
472
-                       . wp_get_shortlink($id, 'post')
473
-                       . '" class="button button-small">'
474
-                       . __('Preview', 'event_espresso')
475
-                       . '</a></span>'
476
-                       . "\n";
477
-        }
478
-        return $return;
479
-    }
480
-
481
-
482
-
483
-    /**
484
-     * add our custom post stati dropdown on the wp post page for this cpt
485
-     *
486
-     * @return string html for dropdown
487
-     */
488
-    public function custom_post_stati_dropdown()
489
-    {
490
-        $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
491
-        $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
492
-            ? $statuses[$this->_cpt_model_obj->status()] : '';
493
-        $template_args = array(
494
-            'cur_status'            => $this->_cpt_model_obj->status(),
495
-            'statuses'              => $statuses,
496
-            'cur_status_label'      => $cur_status_label,
497
-            'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
498
-        );
499
-        //we'll add a trash post status (WP doesn't add one for some reason)
500
-        if ($this->_cpt_model_obj->status() == 'trash') {
501
-            $template_args['cur_status_label'] = __('Trashed', 'event_espresso');
502
-            $statuses['trash'] = __('Trashed', 'event_espresso');
503
-            $template_args['statuses'] = $statuses;
504
-        }
505
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
506
-        EEH_Template::display_template($template, $template_args);
507
-    }
508
-
509
-
510
-
511
-    public function setup_autosave_hooks()
512
-    {
513
-        $this->_set_autosave_containers();
514
-        $this->_load_autosave_scripts_styles();
515
-    }
516
-
517
-
518
-
519
-    /**
520
-     * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
521
-     * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
522
-     * for the nonce in here, but then this method looks for two things:
523
-     * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
524
-     * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
525
-     * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
526
-     * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
527
-     * template args.
528
-     *    1. $template_args['error'] = IF there is an error you can add the message in here.
529
-     *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
530
-     *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
531
-     *    $this->_template_args['data']['items'] = array(
532
-     *        'event-datetime-ids' => '1,2,3';
533
-     *    );
534
-     *    Keep in mind the following things:
535
-     *    - "where" index is for the input with the id as that string.
536
-     *    - "what" index is what will be used for the value of that input.
537
-     *
538
-     * @return void
539
-     */
540
-    public function do_extra_autosave_stuff()
541
-    {
542
-        //next let's check for the autosave nonce (we'll use _verify_nonce )
543
-        $nonce = isset($this->_req_data['autosavenonce']) ? $this->_req_data['autosavenonce'] : null;
544
-        $this->_verify_nonce($nonce, 'autosave');
545
-        //make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
546
-        if ( ! defined('DOING_AUTOSAVE')) {
547
-            define('DOING_AUTOSAVE', true);
548
-        }
549
-        //if we made it here then the nonce checked out.  Let's run our methods and actions
550
-        if (method_exists($this, '_ee_autosave_' . $this->_current_view)) {
551
-            call_user_func(array($this, '_ee_autosave_' . $this->_current_view));
552
-        } else {
553
-            $this->_template_args['success'] = true;
554
-        }
555
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
556
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
557
-        //now let's return json
558
-        $this->_return_json();
559
-    }
560
-
561
-
562
-
563
-    /**
564
-     * This takes care of setting up default routes and pages that utilize the core WP admin pages.
565
-     * Child classes can override the defaults (in cases for adding metaboxes etc.)
566
-     * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
567
-     *
568
-     * @access protected
569
-     * @throws EE_Error
570
-     * @return void
571
-     */
572
-    protected function _extend_page_config_for_cpt()
573
-    {
574
-        //before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
575
-        if ((isset($this->_req_data['page']) && $this->_req_data['page'] != $this->page_slug)) {
576
-            return;
577
-        }
578
-        //set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
579
-        if ( ! empty($this->_cpt_object)) {
580
-            $this->_page_routes = array_merge(array(
581
-                'create_new' => '_create_new_cpt_item',
582
-                'edit'       => '_edit_cpt_item',
583
-            ), $this->_page_routes);
584
-            $this->_page_config = array_merge(array(
585
-                'create_new' => array(
586
-                    'nav'           => array(
587
-                        'label' => $this->_cpt_object->labels->add_new_item,
588
-                        'order' => 5,
589
-                    ),
590
-                    'require_nonce' => false,
591
-                ),
592
-                'edit'       => array(
593
-                    'nav'           => array(
594
-                        'label'      => $this->_cpt_object->labels->edit_item,
595
-                        'order'      => 5,
596
-                        'persistent' => false,
597
-                        'url'        => '',
598
-                    ),
599
-                    'require_nonce' => false,
600
-                ),
601
-            ),
602
-                $this->_page_config
603
-            );
604
-        }
605
-        //load the next section only if this is a matching cpt route as set in the cpt routes array.
606
-        if ( ! isset($this->_cpt_routes[$this->_req_action])) {
607
-            return;
608
-        }
609
-        $this->_cpt_route = isset($this->_cpt_routes[$this->_req_action]) ? true : false;
610
-        //add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
611
-        if (empty($this->_cpt_object)) {
612
-            $msg = sprintf(__('This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).'),
613
-                $this->page_slug, $this->_req_action, get_class($this));
614
-            throw new EE_Error($msg);
615
-        }
616
-        if ($this->_cpt_route) {
617
-            $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
618
-            $this->_set_model_object($id);
619
-        }
620
-    }
621
-
622
-
623
-
624
-    /**
625
-     * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
626
-     *
627
-     * @access protected
628
-     * @param int  $id The id to retrieve the model object for. If empty we set a default object.
629
-     * @param bool $ignore_route_check
630
-     */
631
-    protected function _set_model_object($id = null, $ignore_route_check = false)
632
-    {
633
-        $model = null;
634
-        if (
635
-            empty($this->_cpt_model_names)
636
-            || (
637
-                ! $ignore_route_check
638
-                && ! isset($this->_cpt_routes[$this->_req_action])
639
-            )
640
-            || (
641
-                $this->_cpt_model_obj instanceof EE_CPT_Base
642
-                && $this->_cpt_model_obj->ID() === $id
643
-            )
644
-        ) {
645
-            //get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
646
-            return;
647
-        }
648
-        //if ignore_route_check is true, then get the model name via EE_Register_CPTs
649
-        if ($ignore_route_check) {
650
-            $model_names = EE_Register_CPTs::get_cpt_model_names();
651
-            $post_type = get_post_type($id);
652
-            if (isset($model_names[$post_type])) {
653
-                $model = EE_Registry::instance()->load_model($model_names[$post_type]);
654
-            }
655
-        } else {
656
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
657
-        }
658
-        if ($model instanceof EEM_Base) {
659
-            $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
660
-        }
661
-        do_action('AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object');
662
-    }
663
-
664
-
665
-
666
-    /**
667
-     * admin_init_global
668
-     * This runs all the code that we want executed within the WP admin_init hook.
669
-     * This method executes for ALL EE Admin pages.
670
-     *
671
-     * @access public
672
-     * @return void
673
-     */
674
-    public function admin_init_global()
675
-    {
676
-        $post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
677
-        //its possible this is a new save so let's catch that instead
678
-        $post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
679
-        $post_type = $post ? $post->post_type : false;
680
-        $current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route']
681
-            : 'shouldneverwork';
682
-        $route_to_check = $post_type && isset($this->_cpt_routes[$current_route]) ? $this->_cpt_routes[$current_route]
683
-            : '';
684
-        add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
685
-        add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
686
-        if ($post_type === $route_to_check) {
687
-            add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
688
-            //catch trashed wp redirect
689
-            add_filter('wp_redirect', array($this, 'cpt_trash_post_location_redirect'), 10, 2);
690
-        }
691
-        //now let's filter redirect if we're on a revision page and the revision is for an event CPT.
692
-        $revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
693
-        /**var_dump($this->_req_data);
453
+	}
454
+
455
+
456
+
457
+	/**
458
+	 * if this post is a draft or scheduled post then we provide a preview button for user to click
459
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
460
+	 *
461
+	 * @param  string $return    the current html
462
+	 * @param  int    $id        the post id for the page
463
+	 * @param  string $new_title What the title is
464
+	 * @param  string $new_slug  what the slug is
465
+	 * @return string            The new html string for the permalink area
466
+	 */
467
+	public function preview_button_html($return, $id, $new_title, $new_slug)
468
+	{
469
+		$post = get_post($id);
470
+		if ('publish' != get_post_status($post)) {
471
+			$return .= '<span_id="view-post-btn"><a href="'
472
+					   . wp_get_shortlink($id, 'post')
473
+					   . '" class="button button-small">'
474
+					   . __('Preview', 'event_espresso')
475
+					   . '</a></span>'
476
+					   . "\n";
477
+		}
478
+		return $return;
479
+	}
480
+
481
+
482
+
483
+	/**
484
+	 * add our custom post stati dropdown on the wp post page for this cpt
485
+	 *
486
+	 * @return string html for dropdown
487
+	 */
488
+	public function custom_post_stati_dropdown()
489
+	{
490
+		$statuses = $this->_cpt_model_obj->get_custom_post_statuses();
491
+		$cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
492
+			? $statuses[$this->_cpt_model_obj->status()] : '';
493
+		$template_args = array(
494
+			'cur_status'            => $this->_cpt_model_obj->status(),
495
+			'statuses'              => $statuses,
496
+			'cur_status_label'      => $cur_status_label,
497
+			'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
498
+		);
499
+		//we'll add a trash post status (WP doesn't add one for some reason)
500
+		if ($this->_cpt_model_obj->status() == 'trash') {
501
+			$template_args['cur_status_label'] = __('Trashed', 'event_espresso');
502
+			$statuses['trash'] = __('Trashed', 'event_espresso');
503
+			$template_args['statuses'] = $statuses;
504
+		}
505
+		$template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
506
+		EEH_Template::display_template($template, $template_args);
507
+	}
508
+
509
+
510
+
511
+	public function setup_autosave_hooks()
512
+	{
513
+		$this->_set_autosave_containers();
514
+		$this->_load_autosave_scripts_styles();
515
+	}
516
+
517
+
518
+
519
+	/**
520
+	 * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
521
+	 * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
522
+	 * for the nonce in here, but then this method looks for two things:
523
+	 * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
524
+	 * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
525
+	 * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
526
+	 * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
527
+	 * template args.
528
+	 *    1. $template_args['error'] = IF there is an error you can add the message in here.
529
+	 *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
530
+	 *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
531
+	 *    $this->_template_args['data']['items'] = array(
532
+	 *        'event-datetime-ids' => '1,2,3';
533
+	 *    );
534
+	 *    Keep in mind the following things:
535
+	 *    - "where" index is for the input with the id as that string.
536
+	 *    - "what" index is what will be used for the value of that input.
537
+	 *
538
+	 * @return void
539
+	 */
540
+	public function do_extra_autosave_stuff()
541
+	{
542
+		//next let's check for the autosave nonce (we'll use _verify_nonce )
543
+		$nonce = isset($this->_req_data['autosavenonce']) ? $this->_req_data['autosavenonce'] : null;
544
+		$this->_verify_nonce($nonce, 'autosave');
545
+		//make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
546
+		if ( ! defined('DOING_AUTOSAVE')) {
547
+			define('DOING_AUTOSAVE', true);
548
+		}
549
+		//if we made it here then the nonce checked out.  Let's run our methods and actions
550
+		if (method_exists($this, '_ee_autosave_' . $this->_current_view)) {
551
+			call_user_func(array($this, '_ee_autosave_' . $this->_current_view));
552
+		} else {
553
+			$this->_template_args['success'] = true;
554
+		}
555
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
556
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
557
+		//now let's return json
558
+		$this->_return_json();
559
+	}
560
+
561
+
562
+
563
+	/**
564
+	 * This takes care of setting up default routes and pages that utilize the core WP admin pages.
565
+	 * Child classes can override the defaults (in cases for adding metaboxes etc.)
566
+	 * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
567
+	 *
568
+	 * @access protected
569
+	 * @throws EE_Error
570
+	 * @return void
571
+	 */
572
+	protected function _extend_page_config_for_cpt()
573
+	{
574
+		//before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
575
+		if ((isset($this->_req_data['page']) && $this->_req_data['page'] != $this->page_slug)) {
576
+			return;
577
+		}
578
+		//set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
579
+		if ( ! empty($this->_cpt_object)) {
580
+			$this->_page_routes = array_merge(array(
581
+				'create_new' => '_create_new_cpt_item',
582
+				'edit'       => '_edit_cpt_item',
583
+			), $this->_page_routes);
584
+			$this->_page_config = array_merge(array(
585
+				'create_new' => array(
586
+					'nav'           => array(
587
+						'label' => $this->_cpt_object->labels->add_new_item,
588
+						'order' => 5,
589
+					),
590
+					'require_nonce' => false,
591
+				),
592
+				'edit'       => array(
593
+					'nav'           => array(
594
+						'label'      => $this->_cpt_object->labels->edit_item,
595
+						'order'      => 5,
596
+						'persistent' => false,
597
+						'url'        => '',
598
+					),
599
+					'require_nonce' => false,
600
+				),
601
+			),
602
+				$this->_page_config
603
+			);
604
+		}
605
+		//load the next section only if this is a matching cpt route as set in the cpt routes array.
606
+		if ( ! isset($this->_cpt_routes[$this->_req_action])) {
607
+			return;
608
+		}
609
+		$this->_cpt_route = isset($this->_cpt_routes[$this->_req_action]) ? true : false;
610
+		//add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
611
+		if (empty($this->_cpt_object)) {
612
+			$msg = sprintf(__('This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).'),
613
+				$this->page_slug, $this->_req_action, get_class($this));
614
+			throw new EE_Error($msg);
615
+		}
616
+		if ($this->_cpt_route) {
617
+			$id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
618
+			$this->_set_model_object($id);
619
+		}
620
+	}
621
+
622
+
623
+
624
+	/**
625
+	 * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
626
+	 *
627
+	 * @access protected
628
+	 * @param int  $id The id to retrieve the model object for. If empty we set a default object.
629
+	 * @param bool $ignore_route_check
630
+	 */
631
+	protected function _set_model_object($id = null, $ignore_route_check = false)
632
+	{
633
+		$model = null;
634
+		if (
635
+			empty($this->_cpt_model_names)
636
+			|| (
637
+				! $ignore_route_check
638
+				&& ! isset($this->_cpt_routes[$this->_req_action])
639
+			)
640
+			|| (
641
+				$this->_cpt_model_obj instanceof EE_CPT_Base
642
+				&& $this->_cpt_model_obj->ID() === $id
643
+			)
644
+		) {
645
+			//get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
646
+			return;
647
+		}
648
+		//if ignore_route_check is true, then get the model name via EE_Register_CPTs
649
+		if ($ignore_route_check) {
650
+			$model_names = EE_Register_CPTs::get_cpt_model_names();
651
+			$post_type = get_post_type($id);
652
+			if (isset($model_names[$post_type])) {
653
+				$model = EE_Registry::instance()->load_model($model_names[$post_type]);
654
+			}
655
+		} else {
656
+			$model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
657
+		}
658
+		if ($model instanceof EEM_Base) {
659
+			$this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
660
+		}
661
+		do_action('AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object');
662
+	}
663
+
664
+
665
+
666
+	/**
667
+	 * admin_init_global
668
+	 * This runs all the code that we want executed within the WP admin_init hook.
669
+	 * This method executes for ALL EE Admin pages.
670
+	 *
671
+	 * @access public
672
+	 * @return void
673
+	 */
674
+	public function admin_init_global()
675
+	{
676
+		$post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
677
+		//its possible this is a new save so let's catch that instead
678
+		$post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
679
+		$post_type = $post ? $post->post_type : false;
680
+		$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route']
681
+			: 'shouldneverwork';
682
+		$route_to_check = $post_type && isset($this->_cpt_routes[$current_route]) ? $this->_cpt_routes[$current_route]
683
+			: '';
684
+		add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
685
+		add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
686
+		if ($post_type === $route_to_check) {
687
+			add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
688
+			//catch trashed wp redirect
689
+			add_filter('wp_redirect', array($this, 'cpt_trash_post_location_redirect'), 10, 2);
690
+		}
691
+		//now let's filter redirect if we're on a revision page and the revision is for an event CPT.
692
+		$revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
693
+		/**var_dump($this->_req_data);
694 694
          * exit();/**/
695
-        if ( ! empty($revision)) {
696
-            $action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
697
-            //doing a restore?
698
-            if ( ! empty($action) && $action == 'restore') {
699
-                //get post for revision
700
-                $rev_post = get_post($revision);
701
-                $rev_parent = get_post($rev_post->post_parent);
702
-                //only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
703
-                if ($rev_parent && $rev_parent->post_type == $this->page_slug) {
704
-                    add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
705
-                    //restores of revisions
706
-                    add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
707
-                }
708
-            }
709
-        }
710
-        //NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
711
-        if ($post_type && $post_type === $route_to_check) {
712
-            //$post_id, $post
713
-            add_action('save_post', array($this, 'insert_update'), 10, 3);
714
-            //$post_id
715
-            add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
716
-            add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
717
-            add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
718
-            add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
719
-        }
720
-    }
721
-
722
-
723
-
724
-    /**
725
-     * Callback for the WordPress trashed_post hook.
726
-     * Execute some basic checks before calling the trash_cpt_item declared in the child class.
727
-     *
728
-     * @param int $post_id
729
-     */
730
-    public function before_trash_cpt_item($post_id)
731
-    {
732
-        $this->_set_model_object($post_id, true);
733
-        //if our cpt object isn't existent then get out immediately.
734
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
735
-            return;
736
-        }
737
-        $this->trash_cpt_item($post_id);
738
-    }
739
-
740
-
741
-
742
-    /**
743
-     * Callback for the WordPress untrashed_post hook.
744
-     * Execute some basic checks before calling the restore_cpt_method in the child class.
745
-     *
746
-     * @param $post_id
747
-     */
748
-    public function before_restore_cpt_item($post_id)
749
-    {
750
-        $this->_set_model_object($post_id, true);
751
-        //if our cpt object isn't existent then get out immediately.
752
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
753
-            return;
754
-        }
755
-        $this->restore_cpt_item($post_id);
756
-    }
757
-
758
-
759
-
760
-    /**
761
-     * Callback for the WordPress after_delete_post hook.
762
-     * Execute some basic checks before calling the delete_cpt_item method in the child class.
763
-     *
764
-     * @param $post_id
765
-     */
766
-    public function before_delete_cpt_item($post_id)
767
-    {
768
-        $this->_set_model_object($post_id, true);
769
-        //if our cpt object isn't existent then get out immediately.
770
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
771
-            return;
772
-        }
773
-        $this->delete_cpt_item($post_id);
774
-    }
775
-
776
-
777
-
778
-    /**
779
-     * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
780
-     * accordingly.
781
-     *
782
-     * @access public
783
-     * @throws EE_Error
784
-     * @return void
785
-     */
786
-    public function verify_cpt_object()
787
-    {
788
-        $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
789
-        // verify event object
790
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
791
-            throw new EE_Error(sprintf(__('Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
792
-                'event_espresso'), $label));
793
-        }
794
-        //if auto-draft then throw an error
795
-        if ($this->_cpt_model_obj->get('status') == 'auto-draft') {
796
-            EE_Error::overwrite_errors();
797
-            EE_Error::add_error(sprintf(__('This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.'),
798
-                $label), __FILE__, __FUNCTION__, __LINE__);
799
-        }
800
-    }
801
-
802
-
803
-
804
-    /**
805
-     * admin_footer_scripts_global
806
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
807
-     * will apply on ALL EE_Admin pages.
808
-     *
809
-     * @access public
810
-     * @return void
811
-     */
812
-    public function admin_footer_scripts_global()
813
-    {
814
-        $this->_add_admin_page_ajax_loading_img();
815
-        $this->_add_admin_page_overlay();
816
-    }
817
-
818
-
819
-
820
-    /**
821
-     * add in any global scripts for cpt routes
822
-     *
823
-     * @return void
824
-     */
825
-    public function load_global_scripts_styles()
826
-    {
827
-        parent::load_global_scripts_styles();
828
-        if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
829
-            //setup custom post status object for localize script but only if we've got a cpt object
830
-            $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
831
-            if ( ! empty($statuses)) {
832
-                //get ALL statuses!
833
-                $statuses = $this->_cpt_model_obj->get_all_post_statuses();
834
-                //setup object
835
-                $ee_cpt_statuses = array();
836
-                foreach ($statuses as $status => $label) {
837
-                    $ee_cpt_statuses[$status] = array(
838
-                        'label'      => $label,
839
-                        'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
840
-                    );
841
-                }
842
-                wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
843
-            }
844
-        }
845
-    }
846
-
847
-
848
-
849
-    /**
850
-     * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
851
-     * insert/updates
852
-     *
853
-     * @param  int     $post_id ID of post being updated
854
-     * @param  WP_Post $post    Post object from WP
855
-     * @param  bool    $update  Whether this is an update or a new save.
856
-     * @return void
857
-     */
858
-    public function insert_update($post_id, $post, $update)
859
-    {
860
-        //make sure that if this is a revision OR trash action that we don't do any updates!
861
-        if (
862
-            isset($this->_req_data['action'])
863
-            && (
864
-                $this->_req_data['action'] == 'restore'
865
-                || $this->_req_data['action'] == 'trash'
866
-            )
867
-        ) {
868
-            return;
869
-        }
870
-        $this->_set_model_object($post_id, true);
871
-        //if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
872
-        if ($update
873
-            && (
874
-                ! $this->_cpt_model_obj instanceof EE_CPT_Base
875
-                || $this->_cpt_model_obj->ID() !== $post_id
876
-            )
877
-        ) {
878
-            return;
879
-        }
880
-        //check for autosave and update our req_data property accordingly.
881
-        /*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
695
+		if ( ! empty($revision)) {
696
+			$action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
697
+			//doing a restore?
698
+			if ( ! empty($action) && $action == 'restore') {
699
+				//get post for revision
700
+				$rev_post = get_post($revision);
701
+				$rev_parent = get_post($rev_post->post_parent);
702
+				//only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
703
+				if ($rev_parent && $rev_parent->post_type == $this->page_slug) {
704
+					add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
705
+					//restores of revisions
706
+					add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
707
+				}
708
+			}
709
+		}
710
+		//NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
711
+		if ($post_type && $post_type === $route_to_check) {
712
+			//$post_id, $post
713
+			add_action('save_post', array($this, 'insert_update'), 10, 3);
714
+			//$post_id
715
+			add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
716
+			add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
717
+			add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
718
+			add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
719
+		}
720
+	}
721
+
722
+
723
+
724
+	/**
725
+	 * Callback for the WordPress trashed_post hook.
726
+	 * Execute some basic checks before calling the trash_cpt_item declared in the child class.
727
+	 *
728
+	 * @param int $post_id
729
+	 */
730
+	public function before_trash_cpt_item($post_id)
731
+	{
732
+		$this->_set_model_object($post_id, true);
733
+		//if our cpt object isn't existent then get out immediately.
734
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
735
+			return;
736
+		}
737
+		$this->trash_cpt_item($post_id);
738
+	}
739
+
740
+
741
+
742
+	/**
743
+	 * Callback for the WordPress untrashed_post hook.
744
+	 * Execute some basic checks before calling the restore_cpt_method in the child class.
745
+	 *
746
+	 * @param $post_id
747
+	 */
748
+	public function before_restore_cpt_item($post_id)
749
+	{
750
+		$this->_set_model_object($post_id, true);
751
+		//if our cpt object isn't existent then get out immediately.
752
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
753
+			return;
754
+		}
755
+		$this->restore_cpt_item($post_id);
756
+	}
757
+
758
+
759
+
760
+	/**
761
+	 * Callback for the WordPress after_delete_post hook.
762
+	 * Execute some basic checks before calling the delete_cpt_item method in the child class.
763
+	 *
764
+	 * @param $post_id
765
+	 */
766
+	public function before_delete_cpt_item($post_id)
767
+	{
768
+		$this->_set_model_object($post_id, true);
769
+		//if our cpt object isn't existent then get out immediately.
770
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
771
+			return;
772
+		}
773
+		$this->delete_cpt_item($post_id);
774
+	}
775
+
776
+
777
+
778
+	/**
779
+	 * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
780
+	 * accordingly.
781
+	 *
782
+	 * @access public
783
+	 * @throws EE_Error
784
+	 * @return void
785
+	 */
786
+	public function verify_cpt_object()
787
+	{
788
+		$label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
789
+		// verify event object
790
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
791
+			throw new EE_Error(sprintf(__('Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
792
+				'event_espresso'), $label));
793
+		}
794
+		//if auto-draft then throw an error
795
+		if ($this->_cpt_model_obj->get('status') == 'auto-draft') {
796
+			EE_Error::overwrite_errors();
797
+			EE_Error::add_error(sprintf(__('This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.'),
798
+				$label), __FILE__, __FUNCTION__, __LINE__);
799
+		}
800
+	}
801
+
802
+
803
+
804
+	/**
805
+	 * admin_footer_scripts_global
806
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
807
+	 * will apply on ALL EE_Admin pages.
808
+	 *
809
+	 * @access public
810
+	 * @return void
811
+	 */
812
+	public function admin_footer_scripts_global()
813
+	{
814
+		$this->_add_admin_page_ajax_loading_img();
815
+		$this->_add_admin_page_overlay();
816
+	}
817
+
818
+
819
+
820
+	/**
821
+	 * add in any global scripts for cpt routes
822
+	 *
823
+	 * @return void
824
+	 */
825
+	public function load_global_scripts_styles()
826
+	{
827
+		parent::load_global_scripts_styles();
828
+		if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
829
+			//setup custom post status object for localize script but only if we've got a cpt object
830
+			$statuses = $this->_cpt_model_obj->get_custom_post_statuses();
831
+			if ( ! empty($statuses)) {
832
+				//get ALL statuses!
833
+				$statuses = $this->_cpt_model_obj->get_all_post_statuses();
834
+				//setup object
835
+				$ee_cpt_statuses = array();
836
+				foreach ($statuses as $status => $label) {
837
+					$ee_cpt_statuses[$status] = array(
838
+						'label'      => $label,
839
+						'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
840
+					);
841
+				}
842
+				wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
843
+			}
844
+		}
845
+	}
846
+
847
+
848
+
849
+	/**
850
+	 * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
851
+	 * insert/updates
852
+	 *
853
+	 * @param  int     $post_id ID of post being updated
854
+	 * @param  WP_Post $post    Post object from WP
855
+	 * @param  bool    $update  Whether this is an update or a new save.
856
+	 * @return void
857
+	 */
858
+	public function insert_update($post_id, $post, $update)
859
+	{
860
+		//make sure that if this is a revision OR trash action that we don't do any updates!
861
+		if (
862
+			isset($this->_req_data['action'])
863
+			&& (
864
+				$this->_req_data['action'] == 'restore'
865
+				|| $this->_req_data['action'] == 'trash'
866
+			)
867
+		) {
868
+			return;
869
+		}
870
+		$this->_set_model_object($post_id, true);
871
+		//if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
872
+		if ($update
873
+			&& (
874
+				! $this->_cpt_model_obj instanceof EE_CPT_Base
875
+				|| $this->_cpt_model_obj->ID() !== $post_id
876
+			)
877
+		) {
878
+			return;
879
+		}
880
+		//check for autosave and update our req_data property accordingly.
881
+		/*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
882 882
             foreach( (array) $this->_req_data['ee_autosave_data'] as $id => $values ) {
883 883
 
884 884
                 foreach ( (array) $values as $key => $value ) {
@@ -887,533 +887,533 @@  discard block
 block discarded – undo
887 887
             }
888 888
 
889 889
         }/**/ //TODO reactivate after autosave is implemented in 4.2
890
-        //take care of updating any selected page_template IF this cpt supports it.
891
-        if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
892
-            $post->page_template = $this->_req_data['page_template'];
893
-            $page_templates = wp_get_theme()->get_page_templates($post);
894
-            if ('default' != $this->_req_data['page_template']
895
-                && ! isset($page_templates[$this->_req_data['page_template']])
896
-            ) {
897
-                EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
898
-            } else {
899
-                update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
900
-            }
901
-        }
902
-        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
903
-            return;
904
-        } //TODO we'll remove this after reimplementing autosave in 4.2
905
-        $this->_insert_update_cpt_item($post_id, $post);
906
-    }
907
-
908
-
909
-
910
-    /**
911
-     * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
912
-     * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
913
-     * so we don't have to check for our CPT.
914
-     *
915
-     * @param  int $post_id ID of the post
916
-     * @return void
917
-     */
918
-    public function dont_permanently_delete_ee_cpts($post_id)
919
-    {
920
-        //only do this if we're actually processing one of our CPTs
921
-        //if our cpt object isn't existent then get out immediately.
922
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
923
-            return;
924
-        }
925
-        delete_post_meta($post_id, '_wp_trash_meta_status');
926
-        delete_post_meta($post_id, '_wp_trash_meta_time');
927
-        //our cpts may have comments so let's take care of that too
928
-        delete_post_meta($post_id, '_wp_trash_meta_comments_status');
929
-    }
930
-
931
-
932
-
933
-    /**
934
-     * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
935
-     * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
936
-     * in them.  We also have our OWN action in here so addons can hook into the restore process easily.
937
-     *
938
-     * @param  int $post_id     ID of cpt item
939
-     * @param  int $revision_id ID of revision being restored
940
-     * @return void
941
-     */
942
-    public function restore_revision($post_id, $revision_id)
943
-    {
944
-        $this->_restore_cpt_item($post_id, $revision_id);
945
-        //global action
946
-        do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
947
-        //class specific action so you can limit hooking into a specific page.
948
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
949
-    }
950
-
951
-
952
-
953
-    /**
954
-     * @see restore_revision() for details
955
-     * @param  int $post_id     ID of cpt item
956
-     * @param  int $revision_id ID of revision for item
957
-     * @return void
958
-     */
959
-    abstract protected function _restore_cpt_item($post_id, $revision_id);
960
-
961
-
962
-
963
-    /**
964
-     * Execution of this method is added to the end of the load_page_dependencies method in the parent
965
-     * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
966
-     * To fix we have to reset the current_screen using the page_slug
967
-     * (which is identical - or should be - to our registered_post_type id.)
968
-     * Also, since the core WP file loads the admin_header.php for WP
969
-     * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
970
-     * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
971
-     *
972
-     * @return void
973
-     */
974
-    public function modify_current_screen()
975
-    {
976
-        //ONLY do this if the current page_route IS a cpt route
977
-        if ( ! $this->_cpt_route) {
978
-            return;
979
-        }
980
-        //routing things REALLY early b/c this is a cpt admin page
981
-        set_current_screen($this->_cpt_routes[$this->_req_action]);
982
-        $this->_current_screen = get_current_screen();
983
-        $this->_current_screen->base = 'event-espresso';
984
-        $this->_add_help_tabs(); //we make sure we add any help tabs back in!
985
-        /*try {
890
+		//take care of updating any selected page_template IF this cpt supports it.
891
+		if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
892
+			$post->page_template = $this->_req_data['page_template'];
893
+			$page_templates = wp_get_theme()->get_page_templates($post);
894
+			if ('default' != $this->_req_data['page_template']
895
+				&& ! isset($page_templates[$this->_req_data['page_template']])
896
+			) {
897
+				EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
898
+			} else {
899
+				update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
900
+			}
901
+		}
902
+		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
903
+			return;
904
+		} //TODO we'll remove this after reimplementing autosave in 4.2
905
+		$this->_insert_update_cpt_item($post_id, $post);
906
+	}
907
+
908
+
909
+
910
+	/**
911
+	 * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
912
+	 * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
913
+	 * so we don't have to check for our CPT.
914
+	 *
915
+	 * @param  int $post_id ID of the post
916
+	 * @return void
917
+	 */
918
+	public function dont_permanently_delete_ee_cpts($post_id)
919
+	{
920
+		//only do this if we're actually processing one of our CPTs
921
+		//if our cpt object isn't existent then get out immediately.
922
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
923
+			return;
924
+		}
925
+		delete_post_meta($post_id, '_wp_trash_meta_status');
926
+		delete_post_meta($post_id, '_wp_trash_meta_time');
927
+		//our cpts may have comments so let's take care of that too
928
+		delete_post_meta($post_id, '_wp_trash_meta_comments_status');
929
+	}
930
+
931
+
932
+
933
+	/**
934
+	 * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
935
+	 * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
936
+	 * in them.  We also have our OWN action in here so addons can hook into the restore process easily.
937
+	 *
938
+	 * @param  int $post_id     ID of cpt item
939
+	 * @param  int $revision_id ID of revision being restored
940
+	 * @return void
941
+	 */
942
+	public function restore_revision($post_id, $revision_id)
943
+	{
944
+		$this->_restore_cpt_item($post_id, $revision_id);
945
+		//global action
946
+		do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
947
+		//class specific action so you can limit hooking into a specific page.
948
+		do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
949
+	}
950
+
951
+
952
+
953
+	/**
954
+	 * @see restore_revision() for details
955
+	 * @param  int $post_id     ID of cpt item
956
+	 * @param  int $revision_id ID of revision for item
957
+	 * @return void
958
+	 */
959
+	abstract protected function _restore_cpt_item($post_id, $revision_id);
960
+
961
+
962
+
963
+	/**
964
+	 * Execution of this method is added to the end of the load_page_dependencies method in the parent
965
+	 * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
966
+	 * To fix we have to reset the current_screen using the page_slug
967
+	 * (which is identical - or should be - to our registered_post_type id.)
968
+	 * Also, since the core WP file loads the admin_header.php for WP
969
+	 * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
970
+	 * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
971
+	 *
972
+	 * @return void
973
+	 */
974
+	public function modify_current_screen()
975
+	{
976
+		//ONLY do this if the current page_route IS a cpt route
977
+		if ( ! $this->_cpt_route) {
978
+			return;
979
+		}
980
+		//routing things REALLY early b/c this is a cpt admin page
981
+		set_current_screen($this->_cpt_routes[$this->_req_action]);
982
+		$this->_current_screen = get_current_screen();
983
+		$this->_current_screen->base = 'event-espresso';
984
+		$this->_add_help_tabs(); //we make sure we add any help tabs back in!
985
+		/*try {
986 986
             $this->_route_admin_request();
987 987
         } catch ( EE_Error $e ) {
988 988
             $e->get_error();
989 989
         }/**/
990
-    }
991
-
992
-
993
-
994
-    /**
995
-     * This allows child classes to modify the default editor title that appears when people add a new or edit an
996
-     * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
997
-     * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
998
-     * default to be.
999
-     *
1000
-     * @param string $title The new title (or existing if there is no editor_title defined)
1001
-     * @return string
1002
-     */
1003
-    public function add_custom_editor_default_title($title)
1004
-    {
1005
-        return isset($this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]])
1006
-            ? $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]] : $title;
1007
-    }
1008
-
1009
-
1010
-
1011
-    /**
1012
-     * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1013
-     *
1014
-     * @param string $shortlink   The already generated shortlink
1015
-     * @param int    $id          Post ID for this item
1016
-     * @param string $context     The context for the link
1017
-     * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1018
-     * @return string
1019
-     */
1020
-    public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1021
-    {
1022
-        if ( ! empty($id) && '' != get_option('permalink_structure')) {
1023
-            $post = get_post($id);
1024
-            if (isset($post->post_type) && $this->page_slug == $post->post_type) {
1025
-                $shortlink = home_url('?p=' . $post->ID);
1026
-            }
1027
-        }
1028
-        return $shortlink;
1029
-    }
1030
-
1031
-
1032
-
1033
-    /**
1034
-     * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1035
-     * already run in modify_current_screen())
1036
-     *
1037
-     * @return void
1038
-     */
1039
-    public function route_admin_request()
1040
-    {
1041
-        if ($this->_cpt_route) {
1042
-            return;
1043
-        }
1044
-        try {
1045
-            $this->_route_admin_request();
1046
-        } catch (EE_Error $e) {
1047
-            $e->get_error();
1048
-        }
1049
-    }
1050
-
1051
-
1052
-
1053
-    /**
1054
-     * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1055
-     *
1056
-     * @return string html
1057
-     */
1058
-    public function cpt_post_form_hidden_input()
1059
-    {
1060
-        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1061
-        //we're also going to add the route value and the current page so we can direct autosave parsing correctly
1062
-        echo '<div id="ee-cpt-hidden-inputs">';
1063
-        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1064
-        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1065
-        echo '</div>';
1066
-    }
1067
-
1068
-
1069
-
1070
-    /**
1071
-     * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1072
-     *
1073
-     * @param  string $location Original location url
1074
-     * @param  int    $status   Status for http header
1075
-     * @return string           new (or original) url to redirect to.
1076
-     */
1077
-    public function revision_redirect($location, $status)
1078
-    {
1079
-        //get revision
1080
-        $rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1081
-        //can't do anything without revision so let's get out if not present
1082
-        if (empty($rev_id)) {
1083
-            return $location;
1084
-        }
1085
-        //get rev_post_data
1086
-        $rev = get_post($rev_id);
1087
-        $admin_url = $this->_admin_base_url;
1088
-        $query_args = array(
1089
-            'action'   => 'edit',
1090
-            'post'     => $rev->post_parent,
1091
-            'revision' => $rev_id,
1092
-            'message'  => 5,
1093
-        );
1094
-        $this->_process_notices($query_args, true);
1095
-        return self::add_query_args_and_nonce($query_args, $admin_url);
1096
-    }
1097
-
1098
-
1099
-
1100
-    /**
1101
-     * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1102
-     *
1103
-     * @param  string $link    the original generated link
1104
-     * @param  int    $id      post id
1105
-     * @param  string $context optional, defaults to display.  How to write the '&'
1106
-     * @return string          the link
1107
-     */
1108
-    public function modify_edit_post_link($link, $id, $context)
1109
-    {
1110
-        $post = get_post($id);
1111
-        if ( ! isset($this->_req_data['action'])
1112
-             || ! isset($this->_cpt_routes[$this->_req_data['action']])
1113
-             || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1114
-        ) {
1115
-            return $link;
1116
-        }
1117
-        $query_args = array(
1118
-            'action' => isset($this->_cpt_edit_routes[$post->post_type]) ? $this->_cpt_edit_routes[$post->post_type]
1119
-                : 'edit',
1120
-            'post'   => $id,
1121
-        );
1122
-        return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1123
-    }
1124
-
1125
-
1126
-
1127
-    /**
1128
-     * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1129
-     * our routes.
1130
-     *
1131
-     * @param  string $delete_link  original delete link
1132
-     * @param  int    $post_id      id of cpt object
1133
-     * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1134
-     * @return string               new delete link
1135
-     */
1136
-    public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1137
-    {
1138
-        $post = get_post($post_id);
1139
-        if ( ! isset($this->_req_data['action'])
1140
-             || (isset($this->_cpt_routes[$this->_req_data['action']])
1141
-                 && $post->post_type
1142
-                    !== $this->_cpt_routes[$this->_req_data['action']])
1143
-        ) {
1144
-            return $delete_link;
1145
-        }
1146
-        return add_query_arg(array('current_route' => 'trash'), $delete_link);
1147
-    }
1148
-
1149
-
1150
-
1151
-    /**
1152
-     * This hooks into the wp_redirect filter and if trashed is detected, then we'll redirect to the appropriate EE
1153
-     * route
1154
-     *
1155
-     * @param  string $location url
1156
-     * @param  string $status   status
1157
-     * @return string           url to redirect to
1158
-     */
1159
-    public function cpt_trash_post_location_redirect($location, $status)
1160
-    {
1161
-        if (isset($this->_req_data['action'])
1162
-            && $this->_req_data['action'] !== 'trash'
1163
-            && empty($this->_req_data['post'])
1164
-        ) {
1165
-            return $location;
1166
-        }
1167
-        $post = get_post($this->_req_data['post']);
1168
-        $query_args = array('action' => 'default');
1169
-        $this->_cpt_object = get_post_type_object($post->post_type);
1170
-        EE_Error::add_success(sprintf(__('%s trashed.', 'event_espresso'), $this->_cpt_object->labels->singular_name));
1171
-        $this->_process_notices($query_args, true);
1172
-        return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1173
-    }
1174
-
1175
-
1176
-
1177
-    /**
1178
-     * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1179
-     * so that we can hijack the default redirect locations for wp custom post types
1180
-     * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1181
-     *
1182
-     * @param  string $location This is the incoming currently set redirect location
1183
-     * @param  string $post_id  This is the 'ID' value of the wp_posts table
1184
-     * @return string           the new location to redirect to
1185
-     */
1186
-    public function cpt_post_location_redirect($location, $post_id)
1187
-    {
1188
-        //we DO have a match so let's setup the url
1189
-        //we have to get the post to determine our route
1190
-        $post = get_post($post_id);
1191
-        $edit_route = $this->_cpt_edit_routes[$post->post_type];
1192
-        //shared query_args
1193
-        $query_args = array('action' => $edit_route, 'post' => $post_id);
1194
-        $admin_url = $this->_admin_base_url;
1195
-        //		$append = '';
1196
-        if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1197
-            $status = get_post_status($post_id);
1198
-            if (isset($this->_req_data['publish'])) {
1199
-                switch ($status) {
1200
-                    case 'pending':
1201
-                        $message = 8;
1202
-                        break;
1203
-                    case 'future':
1204
-                        $message = 9;
1205
-                        break;
1206
-                    default:
1207
-                        $message = 6;
1208
-                }
1209
-            } else {
1210
-                $message = 'draft' == $status ? 10 : 1;
1211
-            }
1212
-        } else if (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1213
-            $message = 2;
1214
-            //			$append = '#postcustom';
1215
-        } else if (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1216
-            $message = 3;
1217
-            //			$append = '#postcustom';
1218
-        } elseif ($this->_req_data['action'] == 'post-quickpress-save-cont') {
1219
-            $message = 7;
1220
-        } else {
1221
-            $message = 4;
1222
-        }
1223
-        //change the message if the post type is not viewable on the frontend
1224
-        $this->_cpt_object = get_post_type_object($post->post_type);
1225
-        $message = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1226
-        $query_args = array_merge(array('message' => $message), $query_args);
1227
-        $this->_process_notices($query_args, true);
1228
-        return self::add_query_args_and_nonce($query_args, $admin_url);
1229
-    }
1230
-
1231
-
1232
-
1233
-    /**
1234
-     * This method is called to inject nav tabs on core WP cpt pages
1235
-     *
1236
-     * @access public
1237
-     * @return string html
1238
-     */
1239
-    public function inject_nav_tabs()
1240
-    {
1241
-        //can we hijack and insert the nav_tabs?
1242
-        $nav_tabs = $this->_get_main_nav_tabs();
1243
-        //first close off existing form tag
1244
-        $html = '>';
1245
-        $html .= $nav_tabs;
1246
-        //now let's handle the remaining tag ( missing ">" is CORRECT )
1247
-        $html .= '<span></span';
1248
-        echo $html;
1249
-    }
1250
-
1251
-
1252
-
1253
-    /**
1254
-     * This just sets up the post update messages when an update form is loaded
1255
-     *
1256
-     * @access public
1257
-     * @param  array $messages the original messages array
1258
-     * @return array           the new messages array
1259
-     */
1260
-    public function post_update_messages($messages)
1261
-    {
1262
-        global $post;
1263
-        $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1264
-        $id = empty($id) && is_object($post) ? $post->ID : null;
1265
-        //		$post_type = $post ? $post->post_type : false;
1266
-        /*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
990
+	}
991
+
992
+
993
+
994
+	/**
995
+	 * This allows child classes to modify the default editor title that appears when people add a new or edit an
996
+	 * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
997
+	 * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
998
+	 * default to be.
999
+	 *
1000
+	 * @param string $title The new title (or existing if there is no editor_title defined)
1001
+	 * @return string
1002
+	 */
1003
+	public function add_custom_editor_default_title($title)
1004
+	{
1005
+		return isset($this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]])
1006
+			? $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]] : $title;
1007
+	}
1008
+
1009
+
1010
+
1011
+	/**
1012
+	 * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1013
+	 *
1014
+	 * @param string $shortlink   The already generated shortlink
1015
+	 * @param int    $id          Post ID for this item
1016
+	 * @param string $context     The context for the link
1017
+	 * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1018
+	 * @return string
1019
+	 */
1020
+	public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1021
+	{
1022
+		if ( ! empty($id) && '' != get_option('permalink_structure')) {
1023
+			$post = get_post($id);
1024
+			if (isset($post->post_type) && $this->page_slug == $post->post_type) {
1025
+				$shortlink = home_url('?p=' . $post->ID);
1026
+			}
1027
+		}
1028
+		return $shortlink;
1029
+	}
1030
+
1031
+
1032
+
1033
+	/**
1034
+	 * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1035
+	 * already run in modify_current_screen())
1036
+	 *
1037
+	 * @return void
1038
+	 */
1039
+	public function route_admin_request()
1040
+	{
1041
+		if ($this->_cpt_route) {
1042
+			return;
1043
+		}
1044
+		try {
1045
+			$this->_route_admin_request();
1046
+		} catch (EE_Error $e) {
1047
+			$e->get_error();
1048
+		}
1049
+	}
1050
+
1051
+
1052
+
1053
+	/**
1054
+	 * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1055
+	 *
1056
+	 * @return string html
1057
+	 */
1058
+	public function cpt_post_form_hidden_input()
1059
+	{
1060
+		echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1061
+		//we're also going to add the route value and the current page so we can direct autosave parsing correctly
1062
+		echo '<div id="ee-cpt-hidden-inputs">';
1063
+		echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1064
+		echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1065
+		echo '</div>';
1066
+	}
1067
+
1068
+
1069
+
1070
+	/**
1071
+	 * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1072
+	 *
1073
+	 * @param  string $location Original location url
1074
+	 * @param  int    $status   Status for http header
1075
+	 * @return string           new (or original) url to redirect to.
1076
+	 */
1077
+	public function revision_redirect($location, $status)
1078
+	{
1079
+		//get revision
1080
+		$rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1081
+		//can't do anything without revision so let's get out if not present
1082
+		if (empty($rev_id)) {
1083
+			return $location;
1084
+		}
1085
+		//get rev_post_data
1086
+		$rev = get_post($rev_id);
1087
+		$admin_url = $this->_admin_base_url;
1088
+		$query_args = array(
1089
+			'action'   => 'edit',
1090
+			'post'     => $rev->post_parent,
1091
+			'revision' => $rev_id,
1092
+			'message'  => 5,
1093
+		);
1094
+		$this->_process_notices($query_args, true);
1095
+		return self::add_query_args_and_nonce($query_args, $admin_url);
1096
+	}
1097
+
1098
+
1099
+
1100
+	/**
1101
+	 * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1102
+	 *
1103
+	 * @param  string $link    the original generated link
1104
+	 * @param  int    $id      post id
1105
+	 * @param  string $context optional, defaults to display.  How to write the '&'
1106
+	 * @return string          the link
1107
+	 */
1108
+	public function modify_edit_post_link($link, $id, $context)
1109
+	{
1110
+		$post = get_post($id);
1111
+		if ( ! isset($this->_req_data['action'])
1112
+			 || ! isset($this->_cpt_routes[$this->_req_data['action']])
1113
+			 || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1114
+		) {
1115
+			return $link;
1116
+		}
1117
+		$query_args = array(
1118
+			'action' => isset($this->_cpt_edit_routes[$post->post_type]) ? $this->_cpt_edit_routes[$post->post_type]
1119
+				: 'edit',
1120
+			'post'   => $id,
1121
+		);
1122
+		return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1123
+	}
1124
+
1125
+
1126
+
1127
+	/**
1128
+	 * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1129
+	 * our routes.
1130
+	 *
1131
+	 * @param  string $delete_link  original delete link
1132
+	 * @param  int    $post_id      id of cpt object
1133
+	 * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1134
+	 * @return string               new delete link
1135
+	 */
1136
+	public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1137
+	{
1138
+		$post = get_post($post_id);
1139
+		if ( ! isset($this->_req_data['action'])
1140
+			 || (isset($this->_cpt_routes[$this->_req_data['action']])
1141
+				 && $post->post_type
1142
+					!== $this->_cpt_routes[$this->_req_data['action']])
1143
+		) {
1144
+			return $delete_link;
1145
+		}
1146
+		return add_query_arg(array('current_route' => 'trash'), $delete_link);
1147
+	}
1148
+
1149
+
1150
+
1151
+	/**
1152
+	 * This hooks into the wp_redirect filter and if trashed is detected, then we'll redirect to the appropriate EE
1153
+	 * route
1154
+	 *
1155
+	 * @param  string $location url
1156
+	 * @param  string $status   status
1157
+	 * @return string           url to redirect to
1158
+	 */
1159
+	public function cpt_trash_post_location_redirect($location, $status)
1160
+	{
1161
+		if (isset($this->_req_data['action'])
1162
+			&& $this->_req_data['action'] !== 'trash'
1163
+			&& empty($this->_req_data['post'])
1164
+		) {
1165
+			return $location;
1166
+		}
1167
+		$post = get_post($this->_req_data['post']);
1168
+		$query_args = array('action' => 'default');
1169
+		$this->_cpt_object = get_post_type_object($post->post_type);
1170
+		EE_Error::add_success(sprintf(__('%s trashed.', 'event_espresso'), $this->_cpt_object->labels->singular_name));
1171
+		$this->_process_notices($query_args, true);
1172
+		return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1173
+	}
1174
+
1175
+
1176
+
1177
+	/**
1178
+	 * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1179
+	 * so that we can hijack the default redirect locations for wp custom post types
1180
+	 * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1181
+	 *
1182
+	 * @param  string $location This is the incoming currently set redirect location
1183
+	 * @param  string $post_id  This is the 'ID' value of the wp_posts table
1184
+	 * @return string           the new location to redirect to
1185
+	 */
1186
+	public function cpt_post_location_redirect($location, $post_id)
1187
+	{
1188
+		//we DO have a match so let's setup the url
1189
+		//we have to get the post to determine our route
1190
+		$post = get_post($post_id);
1191
+		$edit_route = $this->_cpt_edit_routes[$post->post_type];
1192
+		//shared query_args
1193
+		$query_args = array('action' => $edit_route, 'post' => $post_id);
1194
+		$admin_url = $this->_admin_base_url;
1195
+		//		$append = '';
1196
+		if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1197
+			$status = get_post_status($post_id);
1198
+			if (isset($this->_req_data['publish'])) {
1199
+				switch ($status) {
1200
+					case 'pending':
1201
+						$message = 8;
1202
+						break;
1203
+					case 'future':
1204
+						$message = 9;
1205
+						break;
1206
+					default:
1207
+						$message = 6;
1208
+				}
1209
+			} else {
1210
+				$message = 'draft' == $status ? 10 : 1;
1211
+			}
1212
+		} else if (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1213
+			$message = 2;
1214
+			//			$append = '#postcustom';
1215
+		} else if (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1216
+			$message = 3;
1217
+			//			$append = '#postcustom';
1218
+		} elseif ($this->_req_data['action'] == 'post-quickpress-save-cont') {
1219
+			$message = 7;
1220
+		} else {
1221
+			$message = 4;
1222
+		}
1223
+		//change the message if the post type is not viewable on the frontend
1224
+		$this->_cpt_object = get_post_type_object($post->post_type);
1225
+		$message = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1226
+		$query_args = array_merge(array('message' => $message), $query_args);
1227
+		$this->_process_notices($query_args, true);
1228
+		return self::add_query_args_and_nonce($query_args, $admin_url);
1229
+	}
1230
+
1231
+
1232
+
1233
+	/**
1234
+	 * This method is called to inject nav tabs on core WP cpt pages
1235
+	 *
1236
+	 * @access public
1237
+	 * @return string html
1238
+	 */
1239
+	public function inject_nav_tabs()
1240
+	{
1241
+		//can we hijack and insert the nav_tabs?
1242
+		$nav_tabs = $this->_get_main_nav_tabs();
1243
+		//first close off existing form tag
1244
+		$html = '>';
1245
+		$html .= $nav_tabs;
1246
+		//now let's handle the remaining tag ( missing ">" is CORRECT )
1247
+		$html .= '<span></span';
1248
+		echo $html;
1249
+	}
1250
+
1251
+
1252
+
1253
+	/**
1254
+	 * This just sets up the post update messages when an update form is loaded
1255
+	 *
1256
+	 * @access public
1257
+	 * @param  array $messages the original messages array
1258
+	 * @return array           the new messages array
1259
+	 */
1260
+	public function post_update_messages($messages)
1261
+	{
1262
+		global $post;
1263
+		$id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1264
+		$id = empty($id) && is_object($post) ? $post->ID : null;
1265
+		//		$post_type = $post ? $post->post_type : false;
1266
+		/*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
1267 1267
 
1268 1268
         $route_to_check = $post_type && isset( $this->_cpt_routes[$current_route]) ? $this->_cpt_routes[$current_route] : '';/**/
1269
-        $messages[$post->post_type] = array(
1270
-            0 => '', //Unused. Messages start at index 1.
1271
-            1 => sprintf(
1272
-                __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1273
-                $this->_cpt_object->labels->singular_name,
1274
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1275
-                '</a>'
1276
-            ),
1277
-            2 => __('Custom field updated'),
1278
-            3 => __('Custom field deleted.'),
1279
-            4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1280
-            5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1281
-                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1282
-                : false,
1283
-            6 => sprintf(
1284
-                __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1285
-                $this->_cpt_object->labels->singular_name,
1286
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1287
-                '</a>'
1288
-            ),
1289
-            7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1290
-            8 => sprintf(
1291
-                __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1292
-                $this->_cpt_object->labels->singular_name,
1293
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1294
-                '</a>'
1295
-            ),
1296
-            9 => sprintf(
1297
-                __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1298
-                $this->_cpt_object->labels->singular_name,
1299
-                '<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1300
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1301
-                '</a>'
1302
-            ),
1303
-            10 => sprintf(
1304
-                __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1305
-                $this->_cpt_object->labels->singular_name,
1306
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1307
-                '</a>'
1308
-            ),
1309
-        );
1310
-        return $messages;
1311
-    }
1312
-
1313
-
1314
-
1315
-    /**
1316
-     * default method for the 'create_new' route for cpt admin pages.
1317
-     * For reference what to include in here, see wp-admin/post-new.php
1318
-     *
1319
-     * @access  protected
1320
-     * @return string template for add new cpt form
1321
-     */
1322
-    protected function _create_new_cpt_item()
1323
-    {
1324
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1325
-        $post_type = $this->_cpt_routes[$this->_req_action];
1326
-        $post_type_object = $this->_cpt_object;
1327
-        $title = $post_type_object->labels->add_new_item;
1328
-        $editing = true;
1329
-        wp_enqueue_script('autosave');
1330
-        $post = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1331
-        $post_ID = $post->ID;
1332
-        $is_IE = $is_IE;
1333
-        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1334
-        //modify the default editor title field with default title.
1335
-        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1336
-        include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1337
-    }
1338
-
1339
-
1340
-
1341
-    public function add_new_admin_page_global()
1342
-    {
1343
-        $admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1344
-        ?>
1269
+		$messages[$post->post_type] = array(
1270
+			0 => '', //Unused. Messages start at index 1.
1271
+			1 => sprintf(
1272
+				__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1273
+				$this->_cpt_object->labels->singular_name,
1274
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1275
+				'</a>'
1276
+			),
1277
+			2 => __('Custom field updated'),
1278
+			3 => __('Custom field deleted.'),
1279
+			4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1280
+			5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1281
+				$this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1282
+				: false,
1283
+			6 => sprintf(
1284
+				__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1285
+				$this->_cpt_object->labels->singular_name,
1286
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1287
+				'</a>'
1288
+			),
1289
+			7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1290
+			8 => sprintf(
1291
+				__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1292
+				$this->_cpt_object->labels->singular_name,
1293
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1294
+				'</a>'
1295
+			),
1296
+			9 => sprintf(
1297
+				__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1298
+				$this->_cpt_object->labels->singular_name,
1299
+				'<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1300
+				'<a target="_blank" href="' . esc_url(get_permalink($id)),
1301
+				'</a>'
1302
+			),
1303
+			10 => sprintf(
1304
+				__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1305
+				$this->_cpt_object->labels->singular_name,
1306
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1307
+				'</a>'
1308
+			),
1309
+		);
1310
+		return $messages;
1311
+	}
1312
+
1313
+
1314
+
1315
+	/**
1316
+	 * default method for the 'create_new' route for cpt admin pages.
1317
+	 * For reference what to include in here, see wp-admin/post-new.php
1318
+	 *
1319
+	 * @access  protected
1320
+	 * @return string template for add new cpt form
1321
+	 */
1322
+	protected function _create_new_cpt_item()
1323
+	{
1324
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1325
+		$post_type = $this->_cpt_routes[$this->_req_action];
1326
+		$post_type_object = $this->_cpt_object;
1327
+		$title = $post_type_object->labels->add_new_item;
1328
+		$editing = true;
1329
+		wp_enqueue_script('autosave');
1330
+		$post = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1331
+		$post_ID = $post->ID;
1332
+		$is_IE = $is_IE;
1333
+		add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1334
+		//modify the default editor title field with default title.
1335
+		add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1336
+		include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1337
+	}
1338
+
1339
+
1340
+
1341
+	public function add_new_admin_page_global()
1342
+	{
1343
+		$admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1344
+		?>
1345 1345
         <script type="text/javascript">
1346 1346
             adminpage = '<?php echo $admin_page; ?>';
1347 1347
         </script>
1348 1348
         <?php
1349
-    }
1350
-
1351
-
1352
-
1353
-    /**
1354
-     * default method for the 'edit' route for cpt admin pages
1355
-     * For reference on what to put in here, refer to wp-admin/post.php
1356
-     *
1357
-     * @access protected
1358
-     * @return string   template for edit cpt form
1359
-     */
1360
-    protected function _edit_cpt_item()
1361
-    {
1362
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1363
-        $post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1364
-        $post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1365
-        if (empty ($post)) {
1366
-            wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?'));
1367
-        }
1368
-        if ( ! empty($_GET['get-post-lock'])) {
1369
-            wp_set_post_lock($post_id);
1370
-            wp_redirect(get_edit_post_link($post_id, 'url'));
1371
-            exit();
1372
-        }
1373
-        // template vars
1374
-        $editing = true;
1375
-        $post_ID = $post_id;
1376
-        $post_type = $this->_cpt_routes[$this->_req_action];
1377
-        $post_type_object = $this->_cpt_object;
1378
-        if ( ! wp_check_post_lock($post->ID)) {
1379
-            $active_post_lock = wp_set_post_lock($post->ID);
1380
-            //wp_enqueue_script('autosave');
1381
-        }
1382
-        $title = $this->_cpt_object->labels->edit_item;
1383
-        add_action('admin_footer', '_admin_notice_post_locked');
1384
-        if (isset($this->_cpt_routes[$this->_req_data['action']])
1385
-            && ! isset($this->_labels['hide_add_button_on_cpt_route'][$this->_req_data['action']])
1386
-        ) {
1387
-            $create_new_action = apply_filters('FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1388
-                'create_new', $this);
1389
-            $post_new_file = EE_Admin_Page::add_query_args_and_nonce(array(
1390
-                'action' => $create_new_action,
1391
-                'page'   => $this->page_slug,
1392
-            ), 'admin.php');
1393
-        }
1394
-        if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1395
-            wp_enqueue_script('admin-comments');
1396
-            enqueue_comment_hotkeys_js();
1397
-        }
1398
-        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1399
-        //modify the default editor title field with default title.
1400
-        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1401
-        include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1402
-    }
1403
-
1404
-
1405
-
1406
-    /**
1407
-     * some getters
1408
-     */
1409
-    /**
1410
-     * This returns the protected _cpt_model_obj property
1411
-     *
1412
-     * @return EE_CPT_Base
1413
-     */
1414
-    public function get_cpt_model_obj()
1415
-    {
1416
-        return $this->_cpt_model_obj;
1417
-    }
1349
+	}
1350
+
1351
+
1352
+
1353
+	/**
1354
+	 * default method for the 'edit' route for cpt admin pages
1355
+	 * For reference on what to put in here, refer to wp-admin/post.php
1356
+	 *
1357
+	 * @access protected
1358
+	 * @return string   template for edit cpt form
1359
+	 */
1360
+	protected function _edit_cpt_item()
1361
+	{
1362
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1363
+		$post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1364
+		$post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1365
+		if (empty ($post)) {
1366
+			wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?'));
1367
+		}
1368
+		if ( ! empty($_GET['get-post-lock'])) {
1369
+			wp_set_post_lock($post_id);
1370
+			wp_redirect(get_edit_post_link($post_id, 'url'));
1371
+			exit();
1372
+		}
1373
+		// template vars
1374
+		$editing = true;
1375
+		$post_ID = $post_id;
1376
+		$post_type = $this->_cpt_routes[$this->_req_action];
1377
+		$post_type_object = $this->_cpt_object;
1378
+		if ( ! wp_check_post_lock($post->ID)) {
1379
+			$active_post_lock = wp_set_post_lock($post->ID);
1380
+			//wp_enqueue_script('autosave');
1381
+		}
1382
+		$title = $this->_cpt_object->labels->edit_item;
1383
+		add_action('admin_footer', '_admin_notice_post_locked');
1384
+		if (isset($this->_cpt_routes[$this->_req_data['action']])
1385
+			&& ! isset($this->_labels['hide_add_button_on_cpt_route'][$this->_req_data['action']])
1386
+		) {
1387
+			$create_new_action = apply_filters('FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1388
+				'create_new', $this);
1389
+			$post_new_file = EE_Admin_Page::add_query_args_and_nonce(array(
1390
+				'action' => $create_new_action,
1391
+				'page'   => $this->page_slug,
1392
+			), 'admin.php');
1393
+		}
1394
+		if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1395
+			wp_enqueue_script('admin-comments');
1396
+			enqueue_comment_hotkeys_js();
1397
+		}
1398
+		add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1399
+		//modify the default editor title field with default title.
1400
+		add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1401
+		include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1402
+	}
1403
+
1404
+
1405
+
1406
+	/**
1407
+	 * some getters
1408
+	 */
1409
+	/**
1410
+	 * This returns the protected _cpt_model_obj property
1411
+	 *
1412
+	 * @return EE_CPT_Base
1413
+	 */
1414
+	public function get_cpt_model_obj()
1415
+	{
1416
+		return $this->_cpt_model_obj;
1417
+	}
1418 1418
 
1419 1419
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -235,7 +235,7 @@  discard block
 block discarded – undo
235 235
      */
236 236
     protected function _register_autosave_containers($ids)
237 237
     {
238
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
238
+        $this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
239 239
     }
240 240
 
241 241
 
@@ -367,7 +367,7 @@  discard block
 block discarded – undo
367 367
         parent::_load_page_dependencies();
368 368
         //notice we are ALSO going to load the pagenow hook set for this route (see _before_page_setup for the reset of the pagenow global ). This is for any plugins that are doing things properly and hooking into the load page hook for core wp cpt routes.
369 369
         global $pagenow;
370
-        do_action('load-' . $pagenow);
370
+        do_action('load-'.$pagenow);
371 371
         $this->modify_current_screen();
372 372
         add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
373 373
         //we route REALLY early.
@@ -398,8 +398,8 @@  discard block
 block discarded – undo
398 398
                 'admin.php?page=espresso_registrations&action=contact_list',
399 399
             ),
400 400
             1 => array(
401
-                'edit.php?post_type=' . $this->_cpt_object->name,
402
-                'admin.php?page=' . $this->_cpt_object->name,
401
+                'edit.php?post_type='.$this->_cpt_object->name,
402
+                'admin.php?page='.$this->_cpt_object->name,
403 403
             ),
404 404
         );
405 405
         foreach ($routes_to_match as $route_matches) {
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
             $statuses['trash'] = __('Trashed', 'event_espresso');
503 503
             $template_args['statuses'] = $statuses;
504 504
         }
505
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
505
+        $template = EE_ADMIN_TEMPLATE.'status_dropdown.template.php';
506 506
         EEH_Template::display_template($template, $template_args);
507 507
     }
508 508
 
@@ -547,13 +547,13 @@  discard block
 block discarded – undo
547 547
             define('DOING_AUTOSAVE', true);
548 548
         }
549 549
         //if we made it here then the nonce checked out.  Let's run our methods and actions
550
-        if (method_exists($this, '_ee_autosave_' . $this->_current_view)) {
551
-            call_user_func(array($this, '_ee_autosave_' . $this->_current_view));
550
+        if (method_exists($this, '_ee_autosave_'.$this->_current_view)) {
551
+            call_user_func(array($this, '_ee_autosave_'.$this->_current_view));
552 552
         } else {
553 553
             $this->_template_args['success'] = true;
554 554
         }
555 555
         do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
556
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
556
+        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_'.get_class($this), $this);
557 557
         //now let's return json
558 558
         $this->_return_json();
559 559
     }
@@ -945,7 +945,7 @@  discard block
 block discarded – undo
945 945
         //global action
946 946
         do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
947 947
         //class specific action so you can limit hooking into a specific page.
948
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
948
+        do_action('AHEE_EE_Admin_Page_CPT_'.get_class($this).'__restore_revision', $post_id, $revision_id);
949 949
     }
950 950
 
951 951
 
@@ -1022,7 +1022,7 @@  discard block
 block discarded – undo
1022 1022
         if ( ! empty($id) && '' != get_option('permalink_structure')) {
1023 1023
             $post = get_post($id);
1024 1024
             if (isset($post->post_type) && $this->page_slug == $post->post_type) {
1025
-                $shortlink = home_url('?p=' . $post->ID);
1025
+                $shortlink = home_url('?p='.$post->ID);
1026 1026
             }
1027 1027
         }
1028 1028
         return $shortlink;
@@ -1057,11 +1057,11 @@  discard block
 block discarded – undo
1057 1057
      */
1058 1058
     public function cpt_post_form_hidden_input()
1059 1059
     {
1060
-        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1060
+        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="'.$this->_admin_base_url.'" />';
1061 1061
         //we're also going to add the route value and the current page so we can direct autosave parsing correctly
1062 1062
         echo '<div id="ee-cpt-hidden-inputs">';
1063
-        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1064
-        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1063
+        echo '<input type="hidden" id="current_route" name="current_route" value="'.$this->_current_view.'" />';
1064
+        echo '<input type="hidden" id="current_page" name="current_page" value="'.$this->page_slug.'" />';
1065 1065
         echo '</div>';
1066 1066
     }
1067 1067
 
@@ -1271,39 +1271,39 @@  discard block
 block discarded – undo
1271 1271
             1 => sprintf(
1272 1272
                 __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1273 1273
                 $this->_cpt_object->labels->singular_name,
1274
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1274
+                '<a href="'.esc_url(get_permalink($id)).'">',
1275 1275
                 '</a>'
1276 1276
             ),
1277 1277
             2 => __('Custom field updated'),
1278 1278
             3 => __('Custom field deleted.'),
1279 1279
             4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1280 1280
             5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1281
-                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1281
+                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int) $_GET['revision'], false))
1282 1282
                 : false,
1283 1283
             6 => sprintf(
1284 1284
                 __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1285 1285
                 $this->_cpt_object->labels->singular_name,
1286
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1286
+                '<a href="'.esc_url(get_permalink($id)).'">',
1287 1287
                 '</a>'
1288 1288
             ),
1289 1289
             7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1290 1290
             8 => sprintf(
1291 1291
                 __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1292 1292
                 $this->_cpt_object->labels->singular_name,
1293
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1293
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))).'">',
1294 1294
                 '</a>'
1295 1295
             ),
1296 1296
             9 => sprintf(
1297 1297
                 __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1298 1298
                 $this->_cpt_object->labels->singular_name,
1299
-                '<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1300
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1299
+                '<strong>'.date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)).'</strong>',
1300
+                '<a target="_blank" href="'.esc_url(get_permalink($id)),
1301 1301
                 '</a>'
1302 1302
             ),
1303 1303
             10 => sprintf(
1304 1304
                 __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1305 1305
                 $this->_cpt_object->labels->singular_name,
1306
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1306
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1307 1307
                 '</a>'
1308 1308
             ),
1309 1309
         );
@@ -1333,7 +1333,7 @@  discard block
 block discarded – undo
1333 1333
         add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1334 1334
         //modify the default editor title field with default title.
1335 1335
         add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1336
-        include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1336
+        include_once WP_ADMIN_PATH.'edit-form-advanced.php';
1337 1337
     }
1338 1338
 
1339 1339
 
@@ -1398,7 +1398,7 @@  discard block
 block discarded – undo
1398 1398
         add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1399 1399
         //modify the default editor title field with default title.
1400 1400
         add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1401
-        include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1401
+        include_once WP_ADMIN_PATH.'edit-form-advanced.php';
1402 1402
     }
1403 1403
 
1404 1404
 
Please login to merge, or discard this patch.
core/db_classes/EE_Base_Class.class.php 2 patches
Indentation   +2649 added lines, -2649 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 do_action('AHEE_log', __FILE__, ' FILE LOADED', '');
5 5
 
@@ -25,2654 +25,2654 @@  discard block
 block discarded – undo
25 25
 abstract class EE_Base_Class
26 26
 {
27 27
 
28
-    /**
29
-     * This is an array of the original properties and values provided during construction
30
-     * of this model object. (keys are model field names, values are their values).
31
-     * This list is important to remember so that when we are merging data from the db, we know
32
-     * which values to override and which to not override.
33
-     *
34
-     * @var array
35
-     */
36
-    protected $_props_n_values_provided_in_constructor;
37
-
38
-    /**
39
-     * Timezone
40
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
41
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
42
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
43
-     * access to it.
44
-     *
45
-     * @var string
46
-     */
47
-    protected $_timezone;
48
-
49
-
50
-
51
-    /**
52
-     * date format
53
-     * pattern or format for displaying dates
54
-     *
55
-     * @var string $_dt_frmt
56
-     */
57
-    protected $_dt_frmt;
58
-
59
-
60
-
61
-    /**
62
-     * time format
63
-     * pattern or format for displaying time
64
-     *
65
-     * @var string $_tm_frmt
66
-     */
67
-    protected $_tm_frmt;
68
-
69
-
70
-
71
-    /**
72
-     * This property is for holding a cached array of object properties indexed by property name as the key.
73
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
74
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
75
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
76
-     *
77
-     * @var array
78
-     */
79
-    protected $_cached_properties = array();
80
-
81
-    /**
82
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
83
-     * single
84
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
85
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
86
-     * all others have an array)
87
-     *
88
-     * @var array
89
-     */
90
-    protected $_model_relations = array();
91
-
92
-    /**
93
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
94
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
95
-     *
96
-     * @var array
97
-     */
98
-    protected $_fields = array();
99
-
100
-    /**
101
-     * @var boolean indicating whether or not this model object is intended to ever be saved
102
-     * For example, we might create model objects intended to only be used for the duration
103
-     * of this request and to be thrown away, and if they were accidentally saved
104
-     * it would be a bug.
105
-     */
106
-    protected $_allow_persist = true;
107
-
108
-
109
-
110
-    /**
111
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
112
-     * play nice
113
-     *
114
-     * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
115
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
116
-     *                                                         TXN_amount, QST_name, etc) and values are their values
117
-     * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
118
-     *                                                         corresponding db model or not.
119
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
120
-     *                                                         be in when instantiating a EE_Base_Class object.
121
-     * @param array   $date_formats                            An array of date formats to set on construct where first
122
-     *                                                         value is the date_format and second value is the time
123
-     *                                                         format.
124
-     * @throws EE_Error
125
-     */
126
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
127
-    {
128
-        $className = get_class($this);
129
-        do_action("AHEE__{$className}__construct", $this, $fieldValues);
130
-        $model = $this->get_model();
131
-        $model_fields = $model->field_settings(false);
132
-        // ensure $fieldValues is an array
133
-        $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
134
-        // EEH_Debug_Tools::printr( $fieldValues, '$fieldValues  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>', 'auto' );
135
-        // verify client code has not passed any invalid field names
136
-        foreach ($fieldValues as $field_name => $field_value) {
137
-            if ( ! isset($model_fields[$field_name])) {
138
-                throw new EE_Error(sprintf(__("Invalid field (%s) passed to constructor of %s. Allowed fields are :%s",
139
-                    "event_espresso"), $field_name, get_class($this), implode(", ", array_keys($model_fields))));
140
-            }
141
-        }
142
-        // EEH_Debug_Tools::printr( $model_fields, '$model_fields  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>', 'auto' );
143
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
144
-        if ( ! empty($date_formats) && is_array($date_formats)) {
145
-            list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
146
-        } else {
147
-            //set default formats for date and time
148
-            $this->_dt_frmt = (string)get_option('date_format', 'Y-m-d');
149
-            $this->_tm_frmt = (string)get_option('time_format', 'g:i a');
150
-        }
151
-        //if db model is instantiating
152
-        if ($bydb) {
153
-            //client code has indicated these field values are from the database
154
-            foreach ($model_fields as $fieldName => $field) {
155
-                $this->set_from_db($fieldName, isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null);
156
-            }
157
-        } else {
158
-            //we're constructing a brand
159
-            //new instance of the model object. Generally, this means we'll need to do more field validation
160
-            foreach ($model_fields as $fieldName => $field) {
161
-                $this->set($fieldName, isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null, true);
162
-            }
163
-        }
164
-        //remember what values were passed to this constructor
165
-        $this->_props_n_values_provided_in_constructor = $fieldValues;
166
-        //remember in entity mapper
167
-        if ( ! $bydb && $model->has_primary_key_field() && $this->ID()) {
168
-            $model->add_to_entity_map($this);
169
-        }
170
-        //setup all the relations
171
-        foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
172
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
173
-                $this->_model_relations[$relation_name] = null;
174
-            } else {
175
-                $this->_model_relations[$relation_name] = array();
176
-            }
177
-        }
178
-        /**
179
-         * Action done at the end of each model object construction
180
-         *
181
-         * @param EE_Base_Class $this the model object just created
182
-         */
183
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
184
-    }
185
-
186
-
187
-
188
-    /**
189
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
190
-     *
191
-     * @return boolean
192
-     */
193
-    public function allow_persist()
194
-    {
195
-        return $this->_allow_persist;
196
-    }
197
-
198
-
199
-
200
-    /**
201
-     * Sets whether or not this model object should be allowed to be saved to the DB.
202
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
203
-     * you got new information that somehow made you change your mind.
204
-     *
205
-     * @param boolean $allow_persist
206
-     * @return boolean
207
-     */
208
-    public function set_allow_persist($allow_persist)
209
-    {
210
-        return $this->_allow_persist = $allow_persist;
211
-    }
212
-
213
-
214
-
215
-    /**
216
-     * Gets the field's original value when this object was constructed during this request.
217
-     * This can be helpful when determining if a model object has changed or not
218
-     *
219
-     * @param string $field_name
220
-     * @return mixed|null
221
-     * @throws \EE_Error
222
-     */
223
-    public function get_original($field_name)
224
-    {
225
-        if (isset($this->_props_n_values_provided_in_constructor[$field_name])
226
-            && $field_settings = $this->get_model()->field_settings_for($field_name)
227
-        ) {
228
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
229
-        } else {
230
-            return null;
231
-        }
232
-    }
233
-
234
-
235
-
236
-    /**
237
-     * @param EE_Base_Class $obj
238
-     * @return string
239
-     */
240
-    public function get_class($obj)
241
-    {
242
-        return get_class($obj);
243
-    }
244
-
245
-
246
-
247
-    /**
248
-     * Overrides parent because parent expects old models.
249
-     * This also doesn't do any validation, and won't work for serialized arrays
250
-     *
251
-     * @param    string $field_name
252
-     * @param    mixed  $field_value
253
-     * @param bool      $use_default
254
-     * @throws \EE_Error
255
-     */
256
-    public function set($field_name, $field_value, $use_default = false)
257
-    {
258
-        $field_obj = $this->get_model()->field_settings_for($field_name);
259
-        if ($field_obj instanceof EE_Model_Field_Base) {
260
-            //			if ( method_exists( $field_obj, 'set_timezone' )) {
261
-            if ($field_obj instanceof EE_Datetime_Field) {
262
-                $field_obj->set_timezone($this->_timezone);
263
-                $field_obj->set_date_format($this->_dt_frmt);
264
-                $field_obj->set_time_format($this->_tm_frmt);
265
-            }
266
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
267
-            //should the value be null?
268
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
269
-                $this->_fields[$field_name] = $field_obj->get_default_value();
270
-                /**
271
-                 * To save having to refactor all the models, if a default value is used for a
272
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
273
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
274
-                 * object.
275
-                 *
276
-                 * @since 4.6.10+
277
-                 */
278
-                if (
279
-                    $field_obj instanceof EE_Datetime_Field
280
-                    && $this->_fields[$field_name] !== null
281
-                    && ! $this->_fields[$field_name] instanceof DateTime
282
-                ) {
283
-                    empty($this->_fields[$field_name])
284
-                        ? $this->set($field_name, time())
285
-                        : $this->set($field_name, $this->_fields[$field_name]);
286
-                }
287
-            } else {
288
-                $this->_fields[$field_name] = $holder_of_value;
289
-            }
290
-            //if we're not in the constructor...
291
-            //now check if what we set was a primary key
292
-            if (
293
-                //note: props_n_values_provided_in_constructor is only set at the END of the constructor
294
-                $this->_props_n_values_provided_in_constructor
295
-                && $field_value
296
-                && $field_name === self::_get_primary_key_name(get_class($this))
297
-            ) {
298
-                //if so, we want all this object's fields to be filled either with
299
-                //what we've explicitly set on this model
300
-                //or what we have in the db
301
-                // echo "setting primary key!";
302
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
303
-                $obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
304
-                foreach ($fields_on_model as $field_obj) {
305
-                    if ( ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
306
-                         && $field_obj->get_name() !== $field_name
307
-                    ) {
308
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
309
-                    }
310
-                }
311
-                //oh this model object has an ID? well make sure its in the entity mapper
312
-                $this->get_model()->add_to_entity_map($this);
313
-            }
314
-            //let's unset any cache for this field_name from the $_cached_properties property.
315
-            $this->_clear_cached_property($field_name);
316
-        } else {
317
-            throw new EE_Error(sprintf(__("A valid EE_Model_Field_Base could not be found for the given field name: %s",
318
-                "event_espresso"), $field_name));
319
-        }
320
-    }
321
-
322
-
323
-
324
-    /**
325
-     * This sets the field value on the db column if it exists for the given $column_name or
326
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
327
-     *
328
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
329
-     * @param string $field_name  Must be the exact column name.
330
-     * @param mixed  $field_value The value to set.
331
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
332
-     * @throws \EE_Error
333
-     */
334
-    public function set_field_or_extra_meta($field_name, $field_value)
335
-    {
336
-        if ($this->get_model()->has_field($field_name)) {
337
-            $this->set($field_name, $field_value);
338
-            return true;
339
-        } else {
340
-            //ensure this object is saved first so that extra meta can be properly related.
341
-            $this->save();
342
-            return $this->update_extra_meta($field_name, $field_value);
343
-        }
344
-    }
345
-
346
-
347
-
348
-    /**
349
-     * This retrieves the value of the db column set on this class or if that's not present
350
-     * it will attempt to retrieve from extra_meta if found.
351
-     * Example Usage:
352
-     * Via EE_Message child class:
353
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
354
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
355
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
356
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
357
-     * value for those extra fields dynamically via the EE_message object.
358
-     *
359
-     * @param  string $field_name expecting the fully qualified field name.
360
-     * @return mixed|null  value for the field if found.  null if not found.
361
-     * @throws \EE_Error
362
-     */
363
-    public function get_field_or_extra_meta($field_name)
364
-    {
365
-        if ($this->get_model()->has_field($field_name)) {
366
-            $column_value = $this->get($field_name);
367
-        } else {
368
-            //This isn't a column in the main table, let's see if it is in the extra meta.
369
-            $column_value = $this->get_extra_meta($field_name, true, null);
370
-        }
371
-        return $column_value;
372
-    }
373
-
374
-
375
-
376
-    /**
377
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
378
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
379
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
380
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
381
-     *
382
-     * @access public
383
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
384
-     * @return void
385
-     * @throws \EE_Error
386
-     */
387
-    public function set_timezone($timezone = '')
388
-    {
389
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
390
-        //make sure we clear all cached properties because they won't be relevant now
391
-        $this->_clear_cached_properties();
392
-        //make sure we update field settings and the date for all EE_Datetime_Fields
393
-        $model_fields = $this->get_model()->field_settings(false);
394
-        foreach ($model_fields as $field_name => $field_obj) {
395
-            if ($field_obj instanceof EE_Datetime_Field) {
396
-                $field_obj->set_timezone($this->_timezone);
397
-                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
398
-                    $this->_fields[$field_name]->setTimezone(new DateTimeZone($this->_timezone));
399
-                }
400
-            }
401
-        }
402
-    }
403
-
404
-
405
-
406
-    /**
407
-     * This just returns whatever is set for the current timezone.
408
-     *
409
-     * @access public
410
-     * @return string timezone string
411
-     */
412
-    public function get_timezone()
413
-    {
414
-        return $this->_timezone;
415
-    }
416
-
417
-
418
-
419
-    /**
420
-     * This sets the internal date format to what is sent in to be used as the new default for the class
421
-     * internally instead of wp set date format options
422
-     *
423
-     * @since 4.6
424
-     * @param string $format should be a format recognizable by PHP date() functions.
425
-     */
426
-    public function set_date_format($format)
427
-    {
428
-        $this->_dt_frmt = $format;
429
-        //clear cached_properties because they won't be relevant now.
430
-        $this->_clear_cached_properties();
431
-    }
432
-
433
-
434
-
435
-    /**
436
-     * This sets the internal time format string to what is sent in to be used as the new default for the
437
-     * class internally instead of wp set time format options.
438
-     *
439
-     * @since 4.6
440
-     * @param string $format should be a format recognizable by PHP date() functions.
441
-     */
442
-    public function set_time_format($format)
443
-    {
444
-        $this->_tm_frmt = $format;
445
-        //clear cached_properties because they won't be relevant now.
446
-        $this->_clear_cached_properties();
447
-    }
448
-
449
-
450
-
451
-    /**
452
-     * This returns the current internal set format for the date and time formats.
453
-     *
454
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
455
-     *                             where the first value is the date format and the second value is the time format.
456
-     * @return mixed string|array
457
-     */
458
-    public function get_format($full = true)
459
-    {
460
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
461
-    }
462
-
463
-
464
-
465
-    /**
466
-     * cache
467
-     * stores the passed model object on the current model object.
468
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
469
-     *
470
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
471
-     *                                       'Registration' associated with this model object
472
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
473
-     *                                       that could be a payment or a registration)
474
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
475
-     *                                       items which will be stored in an array on this object
476
-     * @throws EE_Error
477
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
478
-     *                  related thing, no array)
479
-     */
480
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
481
-    {
482
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
483
-        if ( ! $object_to_cache instanceof EE_Base_Class) {
484
-            return false;
485
-        }
486
-        // also get "how" the object is related, or throw an error
487
-        if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
488
-            throw new EE_Error(sprintf(__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
489
-                $relationName, get_class($this)));
490
-        }
491
-        // how many things are related ?
492
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
493
-            // if it's a "belongs to" relationship, then there's only one related model object  eg, if this is a registration, there's only 1 attendee for it
494
-            // so for these model objects just set it to be cached
495
-            $this->_model_relations[$relationName] = $object_to_cache;
496
-            $return = true;
497
-        } else {
498
-            // otherwise, this is the "many" side of a one to many relationship, so we'll add the object to the array of related objects for that type.
499
-            // eg: if this is an event, there are many registrations for that event, so we cache the registrations in an array
500
-            if ( ! is_array($this->_model_relations[$relationName])) {
501
-                // if for some reason, the cached item is a model object, then stick that in the array, otherwise start with an empty array
502
-                $this->_model_relations[$relationName] = $this->_model_relations[$relationName] instanceof EE_Base_Class
503
-                    ? array($this->_model_relations[$relationName]) : array();
504
-            }
505
-            // first check for a cache_id which is normally empty
506
-            if ( ! empty($cache_id)) {
507
-                // if the cache_id exists, then it means we are purposely trying to cache this with a known key that can then be used to retrieve the object later on
508
-                $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
509
-                $return = $cache_id;
510
-            } elseif ($object_to_cache->ID()) {
511
-                // OR the cached object originally came from the db, so let's just use it's PK for an ID
512
-                $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
513
-                $return = $object_to_cache->ID();
514
-            } else {
515
-                // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
516
-                $this->_model_relations[$relationName][] = $object_to_cache;
517
-                // move the internal pointer to the end of the array
518
-                end($this->_model_relations[$relationName]);
519
-                // and grab the key so that we can return it
520
-                $return = key($this->_model_relations[$relationName]);
521
-            }
522
-        }
523
-        return $return;
524
-    }
525
-
526
-
527
-
528
-    /**
529
-     * For adding an item to the cached_properties property.
530
-     *
531
-     * @access protected
532
-     * @param string      $fieldname the property item the corresponding value is for.
533
-     * @param mixed       $value     The value we are caching.
534
-     * @param string|null $cache_type
535
-     * @return void
536
-     * @throws \EE_Error
537
-     */
538
-    protected function _set_cached_property($fieldname, $value, $cache_type = null)
539
-    {
540
-        //first make sure this property exists
541
-        $this->get_model()->field_settings_for($fieldname);
542
-        $cache_type = empty($cache_type) ? 'standard' : $cache_type;
543
-        $this->_cached_properties[$fieldname][$cache_type] = $value;
544
-    }
545
-
546
-
547
-
548
-    /**
549
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
550
-     * This also SETS the cache if we return the actual property!
551
-     *
552
-     * @param string $fieldname        the name of the property we're trying to retrieve
553
-     * @param bool   $pretty
554
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
555
-     *                                 (in cases where the same property may be used for different outputs
556
-     *                                 - i.e. datetime, money etc.)
557
-     *                                 It can also accept certain pre-defined "schema" strings
558
-     *                                 to define how to output the property.
559
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
560
-     * @return mixed                   whatever the value for the property is we're retrieving
561
-     * @throws \EE_Error
562
-     */
563
-    protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
564
-    {
565
-        //verify the field exists
566
-        $this->get_model()->field_settings_for($fieldname);
567
-        $cache_type = $pretty ? 'pretty' : 'standard';
568
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
569
-        if (isset($this->_cached_properties[$fieldname][$cache_type])) {
570
-            return $this->_cached_properties[$fieldname][$cache_type];
571
-        }
572
-        $field_obj = $this->get_model()->field_settings_for($fieldname);
573
-        if ($field_obj instanceof EE_Model_Field_Base) {
574
-            /**
575
-             * maybe this is EE_Datetime_Field.  If so we need to make sure timezone and
576
-             * formats are correct.
577
-             */
578
-            if ($field_obj instanceof EE_Datetime_Field) {
579
-                $field_obj->set_timezone($this->_timezone);
580
-                $field_obj->set_date_format($this->_dt_frmt, $pretty);
581
-                $field_obj->set_time_format($this->_tm_frmt, $pretty);
582
-            }
583
-            if ( ! isset($this->_fields[$fieldname])) {
584
-                $this->_fields[$fieldname] = null;
585
-            }
586
-            $value = $pretty
587
-                ? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
588
-                : $field_obj->prepare_for_get($this->_fields[$fieldname]);
589
-            $this->_set_cached_property($fieldname, $value, $cache_type);
590
-            return $value;
591
-        }
592
-        return null;
593
-    }
594
-
595
-
596
-
597
-    /**
598
-     * This just takes care of clearing out the cached_properties
599
-     *
600
-     * @return void
601
-     */
602
-    protected function _clear_cached_properties()
603
-    {
604
-        $this->_cached_properties = array();
605
-    }
606
-
607
-
608
-
609
-    /**
610
-     * This just clears out ONE property if it exists in the cache
611
-     *
612
-     * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
613
-     * @return void
614
-     */
615
-    protected function _clear_cached_property($property_name)
616
-    {
617
-        if (isset($this->_cached_properties[$property_name])) {
618
-            unset($this->_cached_properties[$property_name]);
619
-        }
620
-    }
621
-
622
-
623
-
624
-    /**
625
-     * Ensures that this related thing is a model object.
626
-     *
627
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
628
-     * @param string $model_name   name of the related thing, eg 'Attendee',
629
-     * @return EE_Base_Class
630
-     * @throws \EE_Error
631
-     */
632
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
633
-    {
634
-        $other_model_instance = self::_get_model_instance_with_name(
635
-            self::_get_model_classname($model_name),
636
-            $this->_timezone
637
-        );
638
-        return $other_model_instance->ensure_is_obj($object_or_id);
639
-    }
640
-
641
-
642
-
643
-    /**
644
-     * Forgets the cached model of the given relation Name. So the next time we request it,
645
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
646
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
647
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
648
-     *
649
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
650
-     *                                                     Eg 'Registration'
651
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
652
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
653
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
654
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
655
-     *                                                     this is HasMany or HABTM.
656
-     * @throws EE_Error
657
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
658
-     *                       relation from all
659
-     */
660
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
661
-    {
662
-        $relationship_to_model = $this->get_model()->related_settings_for($relationName);
663
-        $index_in_cache = '';
664
-        if ( ! $relationship_to_model) {
665
-            throw new EE_Error(
666
-                sprintf(
667
-                    __("There is no relationship to %s on a %s. Cannot clear that cache", 'event_espresso'),
668
-                    $relationName,
669
-                    get_class($this)
670
-                )
671
-            );
672
-        }
673
-        if ($clear_all) {
674
-            $obj_removed = true;
675
-            $this->_model_relations[$relationName] = null;
676
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
677
-            $obj_removed = $this->_model_relations[$relationName];
678
-            $this->_model_relations[$relationName] = null;
679
-        } else {
680
-            if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
681
-                && $object_to_remove_or_index_into_array->ID()
682
-            ) {
683
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
684
-                if (is_array($this->_model_relations[$relationName])
685
-                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
686
-                ) {
687
-                    $index_found_at = null;
688
-                    //find this object in the array even though it has a different key
689
-                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
690
-                        if (
691
-                            $obj instanceof EE_Base_Class
692
-                            && (
693
-                                $obj == $object_to_remove_or_index_into_array
694
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
695
-                            )
696
-                        ) {
697
-                            $index_found_at = $index;
698
-                            break;
699
-                        }
700
-                    }
701
-                    if ($index_found_at) {
702
-                        $index_in_cache = $index_found_at;
703
-                    } else {
704
-                        //it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
705
-                        //if it wasn't in it to begin with. So we're done
706
-                        return $object_to_remove_or_index_into_array;
707
-                    }
708
-                }
709
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
710
-                //so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
711
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
712
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
713
-                        $index_in_cache = $index;
714
-                    }
715
-                }
716
-            } else {
717
-                $index_in_cache = $object_to_remove_or_index_into_array;
718
-            }
719
-            //supposedly we've found it. But it could just be that the client code
720
-            //provided a bad index/object
721
-            if (
722
-            isset(
723
-                $this->_model_relations[$relationName],
724
-                $this->_model_relations[$relationName][$index_in_cache]
725
-            )
726
-            ) {
727
-                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
728
-                unset($this->_model_relations[$relationName][$index_in_cache]);
729
-            } else {
730
-                //that thing was never cached anyways.
731
-                $obj_removed = null;
732
-            }
733
-        }
734
-        return $obj_removed;
735
-    }
736
-
737
-
738
-
739
-    /**
740
-     * update_cache_after_object_save
741
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
742
-     * obtained after being saved to the db
743
-     *
744
-     * @param string         $relationName       - the type of object that is cached
745
-     * @param \EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
746
-     * @param string         $current_cache_id   - the ID that was used when originally caching the object
747
-     * @return boolean TRUE on success, FALSE on fail
748
-     * @throws \EE_Error
749
-     */
750
-    public function update_cache_after_object_save(
751
-        $relationName,
752
-        EE_Base_Class $newly_saved_object,
753
-        $current_cache_id = ''
754
-    ) {
755
-        // verify that incoming object is of the correct type
756
-        $obj_class = 'EE_' . $relationName;
757
-        if ($newly_saved_object instanceof $obj_class) {
758
-            /* @type EE_Base_Class $newly_saved_object */
759
-            // now get the type of relation
760
-            $relationship_to_model = $this->get_model()->related_settings_for($relationName);
761
-            // if this is a 1:1 relationship
762
-            if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
763
-                // then just replace the cached object with the newly saved object
764
-                $this->_model_relations[$relationName] = $newly_saved_object;
765
-                return true;
766
-                // or if it's some kind of sordid feral polyamorous relationship...
767
-            } elseif (is_array($this->_model_relations[$relationName])
768
-                      && isset($this->_model_relations[$relationName][$current_cache_id])
769
-            ) {
770
-                // then remove the current cached item
771
-                unset($this->_model_relations[$relationName][$current_cache_id]);
772
-                // and cache the newly saved object using it's new ID
773
-                $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
774
-                return true;
775
-            }
776
-        }
777
-        return false;
778
-    }
779
-
780
-
781
-
782
-    /**
783
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
784
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
785
-     *
786
-     * @param string $relationName
787
-     * @return EE_Base_Class
788
-     */
789
-    public function get_one_from_cache($relationName)
790
-    {
791
-        $cached_array_or_object = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName]
792
-            : null;
793
-        if (is_array($cached_array_or_object)) {
794
-            return array_shift($cached_array_or_object);
795
-        } else {
796
-            return $cached_array_or_object;
797
-        }
798
-    }
799
-
800
-
801
-
802
-    /**
803
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
804
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
805
-     *
806
-     * @param string $relationName
807
-     * @throws \EE_Error
808
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
809
-     */
810
-    public function get_all_from_cache($relationName)
811
-    {
812
-        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
813
-        // if the result is not an array, but exists, make it an array
814
-        $objects = is_array($objects) ? $objects : array($objects);
815
-        //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
816
-        //basically, if this model object was stored in the session, and these cached model objects
817
-        //already have IDs, let's make sure they're in their model's entity mapper
818
-        //otherwise we will have duplicates next time we call
819
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
820
-        $model = EE_Registry::instance()->load_model($relationName);
821
-        foreach ($objects as $model_object) {
822
-            if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
823
-                //ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
824
-                if ($model_object->ID()) {
825
-                    $model->add_to_entity_map($model_object);
826
-                }
827
-            } else {
828
-                throw new EE_Error(
829
-                    sprintf(
830
-                        __(
831
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
832
-                            'event_espresso'
833
-                        ),
834
-                        $relationName,
835
-                        gettype($model_object)
836
-                    )
837
-                );
838
-            }
839
-        }
840
-        return $objects;
841
-    }
842
-
843
-
844
-
845
-    /**
846
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
847
-     * matching the given query conditions.
848
-     *
849
-     * @param null  $field_to_order_by  What field is being used as the reference point.
850
-     * @param int   $limit              How many objects to return.
851
-     * @param array $query_params       Any additional conditions on the query.
852
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
853
-     *                                  you can indicate just the columns you want returned
854
-     * @return array|EE_Base_Class[]
855
-     * @throws \EE_Error
856
-     */
857
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
858
-    {
859
-        $field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
860
-            ? $this->get_model()->get_primary_key_field()->get_name()
861
-            : $field_to_order_by;
862
-        $current_value = ! empty($field) ? $this->get($field) : null;
863
-        if (empty($field) || empty($current_value)) {
864
-            return array();
865
-        }
866
-        return $this->get_model()->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
867
-    }
868
-
869
-
870
-
871
-    /**
872
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
873
-     * matching the given query conditions.
874
-     *
875
-     * @param null  $field_to_order_by  What field is being used as the reference point.
876
-     * @param int   $limit              How many objects to return.
877
-     * @param array $query_params       Any additional conditions on the query.
878
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
879
-     *                                  you can indicate just the columns you want returned
880
-     * @return array|EE_Base_Class[]
881
-     * @throws \EE_Error
882
-     */
883
-    public function previous_x(
884
-        $field_to_order_by = null,
885
-        $limit = 1,
886
-        $query_params = array(),
887
-        $columns_to_select = null
888
-    ) {
889
-        $field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
890
-            ? $this->get_model()->get_primary_key_field()->get_name()
891
-            : $field_to_order_by;
892
-        $current_value = ! empty($field) ? $this->get($field) : null;
893
-        if (empty($field) || empty($current_value)) {
894
-            return array();
895
-        }
896
-        return $this->get_model()->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
897
-    }
898
-
899
-
900
-
901
-    /**
902
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
903
-     * matching the given query conditions.
904
-     *
905
-     * @param null  $field_to_order_by  What field is being used as the reference point.
906
-     * @param array $query_params       Any additional conditions on the query.
907
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
908
-     *                                  you can indicate just the columns you want returned
909
-     * @return array|EE_Base_Class
910
-     * @throws \EE_Error
911
-     */
912
-    public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
913
-    {
914
-        $field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
915
-            ? $this->get_model()->get_primary_key_field()->get_name()
916
-            : $field_to_order_by;
917
-        $current_value = ! empty($field) ? $this->get($field) : null;
918
-        if (empty($field) || empty($current_value)) {
919
-            return array();
920
-        }
921
-        return $this->get_model()->next($current_value, $field, $query_params, $columns_to_select);
922
-    }
923
-
924
-
925
-
926
-    /**
927
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
928
-     * matching the given query conditions.
929
-     *
930
-     * @param null  $field_to_order_by  What field is being used as the reference point.
931
-     * @param array $query_params       Any additional conditions on the query.
932
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
933
-     *                                  you can indicate just the column you want returned
934
-     * @return array|EE_Base_Class
935
-     * @throws \EE_Error
936
-     */
937
-    public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
938
-    {
939
-        $field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
940
-            ? $this->get_model()->get_primary_key_field()->get_name()
941
-            : $field_to_order_by;
942
-        $current_value = ! empty($field) ? $this->get($field) : null;
943
-        if (empty($field) || empty($current_value)) {
944
-            return array();
945
-        }
946
-        return $this->get_model()->previous($current_value, $field, $query_params, $columns_to_select);
947
-    }
948
-
949
-
950
-
951
-    /**
952
-     * Overrides parent because parent expects old models.
953
-     * This also doesn't do any validation, and won't work for serialized arrays
954
-     *
955
-     * @param string $field_name
956
-     * @param mixed  $field_value_from_db
957
-     * @throws \EE_Error
958
-     */
959
-    public function set_from_db($field_name, $field_value_from_db)
960
-    {
961
-        $field_obj = $this->get_model()->field_settings_for($field_name);
962
-        if ($field_obj instanceof EE_Model_Field_Base) {
963
-            //you would think the DB has no NULLs for non-null label fields right? wrong!
964
-            //eg, a CPT model object could have an entry in the posts table, but no
965
-            //entry in the meta table. Meaning that all its columns in the meta table
966
-            //are null! yikes! so when we find one like that, use defaults for its meta columns
967
-            if ($field_value_from_db === null) {
968
-                if ($field_obj->is_nullable()) {
969
-                    //if the field allows nulls, then let it be null
970
-                    $field_value = null;
971
-                } else {
972
-                    $field_value = $field_obj->get_default_value();
973
-                }
974
-            } else {
975
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
976
-            }
977
-            $this->_fields[$field_name] = $field_value;
978
-            $this->_clear_cached_property($field_name);
979
-        }
980
-    }
981
-
982
-
983
-
984
-    /**
985
-     * verifies that the specified field is of the correct type
986
-     *
987
-     * @param string $field_name
988
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
989
-     *                                (in cases where the same property may be used for different outputs
990
-     *                                - i.e. datetime, money etc.)
991
-     * @return mixed
992
-     * @throws \EE_Error
993
-     */
994
-    public function get($field_name, $extra_cache_ref = null)
995
-    {
996
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
997
-    }
998
-
999
-
1000
-
1001
-    /**
1002
-     * This method simply returns the RAW unprocessed value for the given property in this class
1003
-     *
1004
-     * @param  string $field_name A valid fieldname
1005
-     * @return mixed              Whatever the raw value stored on the property is.
1006
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1007
-     */
1008
-    public function get_raw($field_name)
1009
-    {
1010
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1011
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1012
-            ? $this->_fields[$field_name]->format('U')
1013
-            : $this->_fields[$field_name];
1014
-    }
1015
-
1016
-
1017
-
1018
-    /**
1019
-     * This is used to return the internal DateTime object used for a field that is a
1020
-     * EE_Datetime_Field.
1021
-     *
1022
-     * @param string $field_name               The field name retrieving the DateTime object.
1023
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1024
-     * @throws \EE_Error
1025
-     *                                         an error is set and false returned.  If the field IS an
1026
-     *                                         EE_Datetime_Field and but the field value is null, then
1027
-     *                                         just null is returned (because that indicates that likely
1028
-     *                                         this field is nullable).
1029
-     */
1030
-    public function get_DateTime_object($field_name)
1031
-    {
1032
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1033
-        if ( ! $field_settings instanceof EE_Datetime_Field) {
1034
-            EE_Error::add_error(
1035
-                sprintf(
1036
-                    __(
1037
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1038
-                        'event_espresso'
1039
-                    ),
1040
-                    $field_name
1041
-                ),
1042
-                __FILE__,
1043
-                __FUNCTION__,
1044
-                __LINE__
1045
-            );
1046
-            return false;
1047
-        }
1048
-        return $this->_fields[$field_name];
1049
-    }
1050
-
1051
-
1052
-
1053
-    /**
1054
-     * To be used in template to immediately echo out the value, and format it for output.
1055
-     * Eg, should call stripslashes and whatnot before echoing
1056
-     *
1057
-     * @param string $field_name      the name of the field as it appears in the DB
1058
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1059
-     *                                (in cases where the same property may be used for different outputs
1060
-     *                                - i.e. datetime, money etc.)
1061
-     * @return void
1062
-     * @throws \EE_Error
1063
-     */
1064
-    public function e($field_name, $extra_cache_ref = null)
1065
-    {
1066
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1067
-    }
1068
-
1069
-
1070
-
1071
-    /**
1072
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1073
-     * can be easily used as the value of form input.
1074
-     *
1075
-     * @param string $field_name
1076
-     * @return void
1077
-     * @throws \EE_Error
1078
-     */
1079
-    public function f($field_name)
1080
-    {
1081
-        $this->e($field_name, 'form_input');
1082
-    }
1083
-
1084
-
1085
-
1086
-    /**
1087
-     * @param string $field_name
1088
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1089
-     *                                (in cases where the same property may be used for different outputs
1090
-     *                                - i.e. datetime, money etc.)
1091
-     * @return mixed
1092
-     * @throws \EE_Error
1093
-     */
1094
-    public function get_pretty($field_name, $extra_cache_ref = null)
1095
-    {
1096
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1097
-    }
1098
-
1099
-
1100
-
1101
-    /**
1102
-     * This simply returns the datetime for the given field name
1103
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1104
-     * (and the equivalent e_date, e_time, e_datetime).
1105
-     *
1106
-     * @access   protected
1107
-     * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1108
-     * @param string   $dt_frmt      valid datetime format used for date
1109
-     *                               (if '' then we just use the default on the field,
1110
-     *                               if NULL we use the last-used format)
1111
-     * @param string   $tm_frmt      Same as above except this is for time format
1112
-     * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1113
-     * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1114
-     * @return void|string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1115
-     *                               if field is not a valid dtt field, or void if echoing
1116
-     * @throws \EE_Error
1117
-     */
1118
-    protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1119
-    {
1120
-        $in_dt_frmt = empty($dt_frmt) ? $this->_dt_frmt : $dt_frmt;
1121
-        $in_tm_frmt = empty($tm_frmt) ? $this->_tm_frmt : $tm_frmt;
1122
-        //validate field for datetime and returns field settings if valid.
1123
-        $field = $this->_get_dtt_field_settings($field_name);
1124
-        //clear cached property if either formats are not null.
1125
-        if ($dt_frmt !== null || $tm_frmt !== null) {
1126
-            $this->_clear_cached_property($field_name);
1127
-            //reset format properties because they are used in get()
1128
-            $this->_dt_frmt = $in_dt_frmt;
1129
-            $this->_tm_frmt = $in_tm_frmt;
1130
-        }
1131
-        if ($echo) {
1132
-            $field->set_pretty_date_format($in_dt_frmt);
1133
-        } else {
1134
-            $field->set_date_format($in_dt_frmt);
1135
-        }
1136
-        if ($echo) {
1137
-            $field->set_pretty_time_format($in_tm_frmt);
1138
-        } else {
1139
-            $field->set_time_format($in_tm_frmt);
1140
-        }
1141
-        //set timezone in field object
1142
-        $field->set_timezone($this->_timezone);
1143
-        //set the output returned
1144
-        switch ($date_or_time) {
1145
-            case 'D' :
1146
-                $field->set_date_time_output('date');
1147
-                break;
1148
-            case 'T' :
1149
-                $field->set_date_time_output('time');
1150
-                break;
1151
-            default :
1152
-                $field->set_date_time_output();
1153
-        }
1154
-        if ($echo) {
1155
-            $this->e($field_name, $date_or_time);
1156
-            return '';
1157
-        }
1158
-        return $this->get($field_name, $date_or_time);
1159
-    }
1160
-
1161
-
1162
-
1163
-    /**
1164
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1165
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1166
-     * other echoes the pretty value for dtt)
1167
-     *
1168
-     * @param  string $field_name name of model object datetime field holding the value
1169
-     * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1170
-     * @return string            datetime value formatted
1171
-     * @throws \EE_Error
1172
-     */
1173
-    public function get_date($field_name, $format = null)
1174
-    {
1175
-        return $this->_get_datetime($field_name, $format, null, 'D');
1176
-    }
1177
-
1178
-
1179
-
1180
-    /**
1181
-     * @param      $field_name
1182
-     * @param null $format
1183
-     * @throws \EE_Error
1184
-     */
1185
-    public function e_date($field_name, $format = null)
1186
-    {
1187
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1188
-    }
1189
-
1190
-
1191
-
1192
-    /**
1193
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1194
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1195
-     * other echoes the pretty value for dtt)
1196
-     *
1197
-     * @param  string $field_name name of model object datetime field holding the value
1198
-     * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1199
-     * @return string             datetime value formatted
1200
-     * @throws \EE_Error
1201
-     */
1202
-    public function get_time($field_name, $format = null)
1203
-    {
1204
-        return $this->_get_datetime($field_name, null, $format, 'T');
1205
-    }
1206
-
1207
-
1208
-
1209
-    /**
1210
-     * @param      $field_name
1211
-     * @param null $format
1212
-     * @throws \EE_Error
1213
-     */
1214
-    public function e_time($field_name, $format = null)
1215
-    {
1216
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1217
-    }
1218
-
1219
-
1220
-
1221
-    /**
1222
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1223
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1224
-     * other echoes the pretty value for dtt)
1225
-     *
1226
-     * @param  string $field_name name of model object datetime field holding the value
1227
-     * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1228
-     * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1229
-     * @return string             datetime value formatted
1230
-     * @throws \EE_Error
1231
-     */
1232
-    public function get_datetime($field_name, $dt_frmt = null, $tm_frmt = null)
1233
-    {
1234
-        return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1235
-    }
1236
-
1237
-
1238
-
1239
-    /**
1240
-     * @param      $field_name
1241
-     * @param null $dt_frmt
1242
-     * @param null $tm_frmt
1243
-     * @throws \EE_Error
1244
-     */
1245
-    public function e_datetime($field_name, $dt_frmt = null, $tm_frmt = null)
1246
-    {
1247
-        $this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1248
-    }
1249
-
1250
-
1251
-
1252
-    /**
1253
-     * Get the i8ln value for a date using the WordPress @see date_i18n function.
1254
-     *
1255
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1256
-     * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1257
-     *                           on the object will be used.
1258
-     * @return string Date and time string in set locale or false if no field exists for the given
1259
-     * @throws \EE_Error
1260
-     *                           field name.
1261
-     */
1262
-    public function get_i18n_datetime($field_name, $format = null)
1263
-    {
1264
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1265
-        return date_i18n(
1266
-            $format,
1267
-            EEH_DTT_Helper::get_timestamp_with_offset($this->get_raw($field_name), $this->_timezone)
1268
-        );
1269
-    }
1270
-
1271
-
1272
-
1273
-    /**
1274
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
1275
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1276
-     * thrown.
1277
-     *
1278
-     * @param  string $field_name The field name being checked
1279
-     * @throws EE_Error
1280
-     * @return EE_Datetime_Field
1281
-     */
1282
-    protected function _get_dtt_field_settings($field_name)
1283
-    {
1284
-        $field = $this->get_model()->field_settings_for($field_name);
1285
-        //check if field is dtt
1286
-        if ($field instanceof EE_Datetime_Field) {
1287
-            return $field;
1288
-        } else {
1289
-            throw new EE_Error(sprintf(__('The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1290
-                'event_espresso'), $field_name, self::_get_model_classname(get_class($this))));
1291
-        }
1292
-    }
1293
-
1294
-
1295
-
1296
-
1297
-    /**
1298
-     * NOTE ABOUT BELOW:
1299
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1300
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1301
-     * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1302
-     * method and make sure you send the entire datetime value for setting.
1303
-     */
1304
-    /**
1305
-     * sets the time on a datetime property
1306
-     *
1307
-     * @access protected
1308
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1309
-     * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1310
-     * @throws \EE_Error
1311
-     */
1312
-    protected function _set_time_for($time, $fieldname)
1313
-    {
1314
-        $this->_set_date_time('T', $time, $fieldname);
1315
-    }
1316
-
1317
-
1318
-
1319
-    /**
1320
-     * sets the date on a datetime property
1321
-     *
1322
-     * @access protected
1323
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1324
-     * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1325
-     * @throws \EE_Error
1326
-     */
1327
-    protected function _set_date_for($date, $fieldname)
1328
-    {
1329
-        $this->_set_date_time('D', $date, $fieldname);
1330
-    }
1331
-
1332
-
1333
-
1334
-    /**
1335
-     * This takes care of setting a date or time independently on a given model object property. This method also
1336
-     * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1337
-     *
1338
-     * @access protected
1339
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1340
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1341
-     * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1342
-     *                                        EE_Datetime_Field property)
1343
-     * @throws \EE_Error
1344
-     */
1345
-    protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1346
-    {
1347
-        $field = $this->_get_dtt_field_settings($fieldname);
1348
-        $field->set_timezone($this->_timezone);
1349
-        $field->set_date_format($this->_dt_frmt);
1350
-        $field->set_time_format($this->_tm_frmt);
1351
-        switch ($what) {
1352
-            case 'T' :
1353
-                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1354
-                    $datetime_value,
1355
-                    $this->_fields[$fieldname]
1356
-                );
1357
-                break;
1358
-            case 'D' :
1359
-                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1360
-                    $datetime_value,
1361
-                    $this->_fields[$fieldname]
1362
-                );
1363
-                break;
1364
-            case 'B' :
1365
-                $this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1366
-                break;
1367
-        }
1368
-        $this->_clear_cached_property($fieldname);
1369
-    }
1370
-
1371
-
1372
-
1373
-    /**
1374
-     * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1375
-     * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1376
-     * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1377
-     * that could lead to some unexpected results!
1378
-     *
1379
-     * @access public
1380
-     * @param string               $field_name This is the name of the field on the object that contains the date/time
1381
-     *                                         value being returned.
1382
-     * @param string               $callback   must match a valid method in this class (defaults to get_datetime)
1383
-     * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1384
-     * @param string               $prepend    You can include something to prepend on the timestamp
1385
-     * @param string               $append     You can include something to append on the timestamp
1386
-     * @throws EE_Error
1387
-     * @return string timestamp
1388
-     */
1389
-    public function display_in_my_timezone(
1390
-        $field_name,
1391
-        $callback = 'get_datetime',
1392
-        $args = null,
1393
-        $prepend = '',
1394
-        $append = ''
1395
-    ) {
1396
-        $timezone = EEH_DTT_Helper::get_timezone();
1397
-        if ($timezone === $this->_timezone) {
1398
-            return '';
1399
-        }
1400
-        $original_timezone = $this->_timezone;
1401
-        $this->set_timezone($timezone);
1402
-        $fn = (array)$field_name;
1403
-        $args = array_merge($fn, (array)$args);
1404
-        if ( ! method_exists($this, $callback)) {
1405
-            throw new EE_Error(
1406
-                sprintf(
1407
-                    __(
1408
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1409
-                        'event_espresso'
1410
-                    ),
1411
-                    $callback
1412
-                )
1413
-            );
1414
-        }
1415
-        $args = (array)$args;
1416
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1417
-        $this->set_timezone($original_timezone);
1418
-        return $return;
1419
-    }
1420
-
1421
-
1422
-
1423
-    /**
1424
-     * Deletes this model object.
1425
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1426
-     * override
1427
-     * `EE_Base_Class::_delete` NOT this class.
1428
-     *
1429
-     * @return boolean | int
1430
-     * @throws \EE_Error
1431
-     */
1432
-    public function delete()
1433
-    {
1434
-        /**
1435
-         * Called just before the `EE_Base_Class::_delete` method call.
1436
-         * Note: `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1437
-         * should be aware that `_delete` may not always result in a permanent delete.  For example, `EE_Soft_Delete_Base_Class::_delete`
1438
-         * soft deletes (trash) the object and does not permanently delete it.
1439
-         *
1440
-         * @param EE_Base_Class $model_object about to be 'deleted'
1441
-         */
1442
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
1443
-        $result = $this->_delete();
1444
-        /**
1445
-         * Called just after the `EE_Base_Class::_delete` method call.
1446
-         * Note: `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1447
-         * should be aware that `_delete` may not always result in a permanent delete.  For example `EE_Soft_Base_Class::_delete`
1448
-         * soft deletes (trash) the object and does not permanently delete it.
1449
-         *
1450
-         * @param EE_Base_Class $model_object that was just 'deleted'
1451
-         * @param boolean       $result
1452
-         */
1453
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1454
-        return $result;
1455
-    }
1456
-
1457
-
1458
-
1459
-    /**
1460
-     * Calls the specific delete method for the instantiated class.
1461
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1462
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1463
-     * `EE_Base_Class::delete`
1464
-     *
1465
-     * @return bool|int
1466
-     * @throws \EE_Error
1467
-     */
1468
-    protected function _delete()
1469
-    {
1470
-        return $this->delete_permanently();
1471
-    }
1472
-
1473
-
1474
-
1475
-    /**
1476
-     * Deletes this model object permanently from db (but keep in mind related models my block the delete and return an
1477
-     * error)
1478
-     *
1479
-     * @return bool | int
1480
-     * @throws \EE_Error
1481
-     */
1482
-    public function delete_permanently()
1483
-    {
1484
-        /**
1485
-         * Called just before HARD deleting a model object
1486
-         *
1487
-         * @param EE_Base_Class $model_object about to be 'deleted'
1488
-         */
1489
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1490
-        $model = $this->get_model();
1491
-        $result = $model->delete_permanently_by_ID($this->ID());
1492
-        $this->refresh_cache_of_related_objects();
1493
-        /**
1494
-         * Called just after HARD deleting a model object
1495
-         *
1496
-         * @param EE_Base_Class $model_object that was just 'deleted'
1497
-         * @param boolean       $result
1498
-         */
1499
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1500
-        return $result;
1501
-    }
1502
-
1503
-
1504
-
1505
-    /**
1506
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1507
-     * related model objects
1508
-     *
1509
-     * @throws \EE_Error
1510
-     */
1511
-    public function refresh_cache_of_related_objects()
1512
-    {
1513
-        foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
1514
-            if ( ! empty($this->_model_relations[$relation_name])) {
1515
-                $related_objects = $this->_model_relations[$relation_name];
1516
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
1517
-                    //this relation only stores a single model object, not an array
1518
-                    //but let's make it consistent
1519
-                    $related_objects = array($related_objects);
1520
-                }
1521
-                foreach ($related_objects as $related_object) {
1522
-                    //only refresh their cache if they're in memory
1523
-                    if ($related_object instanceof EE_Base_Class) {
1524
-                        $related_object->clear_cache($this->get_model()->get_this_model_name(), $this);
1525
-                    }
1526
-                }
1527
-            }
1528
-        }
1529
-    }
1530
-
1531
-
1532
-
1533
-    /**
1534
-     *        Saves this object to the database. An array may be supplied to set some values on this
1535
-     * object just before saving.
1536
-     *
1537
-     * @access public
1538
-     * @param array $set_cols_n_values keys are field names, values are their new values,
1539
-     *                                 if provided during the save() method (often client code will change the fields'
1540
-     *                                 values before calling save)
1541
-     * @throws \EE_Error
1542
-     * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1543
-     *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1544
-     */
1545
-    public function save($set_cols_n_values = array())
1546
-    {
1547
-        /**
1548
-         * Filters the fields we're about to save on the model object
1549
-         *
1550
-         * @param array         $set_cols_n_values
1551
-         * @param EE_Base_Class $model_object
1552
-         */
1553
-        $set_cols_n_values = (array)apply_filters('FHEE__EE_Base_Class__save__set_cols_n_values', $set_cols_n_values,
1554
-            $this);
1555
-        //set attributes as provided in $set_cols_n_values
1556
-        foreach ($set_cols_n_values as $column => $value) {
1557
-            $this->set($column, $value);
1558
-        }
1559
-        /**
1560
-         * Saving a model object.
1561
-         * Before we perform a save, this action is fired.
1562
-         *
1563
-         * @param EE_Base_Class $model_object the model object about to be saved.
1564
-         */
1565
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
1566
-        if ( ! $this->allow_persist()) {
1567
-            return 0;
1568
-        }
1569
-        //now get current attribute values
1570
-        $save_cols_n_values = $this->_fields;
1571
-        //if the object already has an ID, update it. Otherwise, insert it
1572
-        //also: change the assumption about values passed to the model NOT being prepare dby the model object. They have been
1573
-        $old_assumption_concerning_value_preparation = $this->get_model()
1574
-                                                            ->get_assumption_concerning_values_already_prepared_by_model_object();
1575
-        $this->get_model()->assume_values_already_prepared_by_model_object(true);
1576
-        //does this model have an autoincrement PK?
1577
-        if ($this->get_model()->has_primary_key_field()) {
1578
-            if ($this->get_model()->get_primary_key_field()->is_auto_increment()) {
1579
-                //ok check if it's set, if so: update; if not, insert
1580
-                if ( ! empty($save_cols_n_values[self::_get_primary_key_name(get_class($this))])) {
1581
-                    $results = $this->get_model()->update_by_ID($save_cols_n_values, $this->ID());
1582
-                } else {
1583
-                    unset($save_cols_n_values[self::_get_primary_key_name(get_class($this))]);
1584
-                    $results = $this->get_model()->insert($save_cols_n_values);
1585
-                    if ($results) {
1586
-                        //if successful, set the primary key
1587
-                        //but don't use the normal SET method, because it will check if
1588
-                        //an item with the same ID exists in the mapper & db, then
1589
-                        //will find it in the db (because we just added it) and THAT object
1590
-                        //will get added to the mapper before we can add this one!
1591
-                        //but if we just avoid using the SET method, all that headache can be avoided
1592
-                        $pk_field_name = self::_get_primary_key_name(get_class($this));
1593
-                        $this->_fields[$pk_field_name] = $results;
1594
-                        $this->_clear_cached_property($pk_field_name);
1595
-                        $this->get_model()->add_to_entity_map($this);
1596
-                        $this->_update_cached_related_model_objs_fks();
1597
-                    }
1598
-                }
1599
-            } else {//PK is NOT auto-increment
1600
-                //so check if one like it already exists in the db
1601
-                if ($this->get_model()->exists_by_ID($this->ID())) {
1602
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
1603
-                        throw new EE_Error(
1604
-                            sprintf(
1605
-                                __('Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1606
-                                    'event_espresso'),
1607
-                                get_class($this),
1608
-                                get_class($this->get_model()) . '::instance()->add_to_entity_map()',
1609
-                                get_class($this->get_model()) . '::instance()->get_one_by_ID()',
1610
-                                '<br />'
1611
-                            )
1612
-                        );
1613
-                    }
1614
-                    $results = $this->get_model()->update_by_ID($save_cols_n_values, $this->ID());
1615
-                } else {
1616
-                    $results = $this->get_model()->insert($save_cols_n_values);
1617
-                    $this->_update_cached_related_model_objs_fks();
1618
-                }
1619
-            }
1620
-        } else {//there is NO primary key
1621
-            $already_in_db = false;
1622
-            foreach ($this->get_model()->unique_indexes() as $index) {
1623
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1624
-                if ($this->get_model()->exists(array($uniqueness_where_params))) {
1625
-                    $already_in_db = true;
1626
-                }
1627
-            }
1628
-            if ($already_in_db) {
1629
-                $combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1630
-                    $this->get_model()->get_combined_primary_key_fields());
1631
-                $results = $this->get_model()->update($save_cols_n_values, $combined_pk_fields_n_values);
1632
-            } else {
1633
-                $results = $this->get_model()->insert($save_cols_n_values);
1634
-            }
1635
-        }
1636
-        //restore the old assumption about values being prepared by the model object
1637
-        $this->get_model()
1638
-             ->assume_values_already_prepared_by_model_object($old_assumption_concerning_value_preparation);
1639
-        /**
1640
-         * After saving the model object this action is called
1641
-         *
1642
-         * @param EE_Base_Class $model_object which was just saved
1643
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1644
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1645
-         */
1646
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1647
-        return $results;
1648
-    }
1649
-
1650
-
1651
-
1652
-    /**
1653
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1654
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1655
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1656
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1657
-     * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1658
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1659
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1660
-     *
1661
-     * @return void
1662
-     * @throws \EE_Error
1663
-     */
1664
-    protected function _update_cached_related_model_objs_fks()
1665
-    {
1666
-        foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
1667
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1668
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1669
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1670
-                        $this->get_model()->get_this_model_name()
1671
-                    );
1672
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1673
-                    if ($related_model_obj_in_cache->ID()) {
1674
-                        $related_model_obj_in_cache->save();
1675
-                    }
1676
-                }
1677
-            }
1678
-        }
1679
-    }
1680
-
1681
-
1682
-
1683
-    /**
1684
-     * Saves this model object and its NEW cached relations to the database.
1685
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1686
-     * In order for that to work, we would need to mark model objects as dirty/clean...
1687
-     * because otherwise, there's a potential for infinite looping of saving
1688
-     * Saves the cached related model objects, and ensures the relation between them
1689
-     * and this object and properly setup
1690
-     *
1691
-     * @return int ID of new model object on save; 0 on failure+
1692
-     * @throws \EE_Error
1693
-     */
1694
-    public function save_new_cached_related_model_objs()
1695
-    {
1696
-        //make sure this has been saved
1697
-        if ( ! $this->ID()) {
1698
-            $id = $this->save();
1699
-        } else {
1700
-            $id = $this->ID();
1701
-        }
1702
-        //now save all the NEW cached model objects  (ie they don't exist in the DB)
1703
-        foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1704
-            if ($this->_model_relations[$relationName]) {
1705
-                //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1706
-                //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1707
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
1708
-                    //add a relation to that relation type (which saves the appropriate thing in the process)
1709
-                    //but ONLY if it DOES NOT exist in the DB
1710
-                    /* @var $related_model_obj EE_Base_Class */
1711
-                    $related_model_obj = $this->_model_relations[$relationName];
1712
-                    //					if( ! $related_model_obj->ID()){
1713
-                    $this->_add_relation_to($related_model_obj, $relationName);
1714
-                    $related_model_obj->save_new_cached_related_model_objs();
1715
-                    //					}
1716
-                } else {
1717
-                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
1718
-                        //add a relation to that relation type (which saves the appropriate thing in the process)
1719
-                        //but ONLY if it DOES NOT exist in the DB
1720
-                        //						if( ! $related_model_obj->ID()){
1721
-                        $this->_add_relation_to($related_model_obj, $relationName);
1722
-                        $related_model_obj->save_new_cached_related_model_objs();
1723
-                        //						}
1724
-                    }
1725
-                }
1726
-            }
1727
-        }
1728
-        return $id;
1729
-    }
1730
-
1731
-
1732
-
1733
-    /**
1734
-     * for getting a model while instantiated.
1735
-     *
1736
-     * @return \EEM_Base | \EEM_CPT_Base
1737
-     */
1738
-    public function get_model()
1739
-    {
1740
-        $modelName = self::_get_model_classname(get_class($this));
1741
-        return self::_get_model_instance_with_name($modelName, $this->_timezone);
1742
-    }
1743
-
1744
-
1745
-
1746
-    /**
1747
-     * @param $props_n_values
1748
-     * @param $classname
1749
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
1750
-     * @throws \EE_Error
1751
-     */
1752
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
1753
-    {
1754
-        //TODO: will not work for Term_Relationships because they have no PK!
1755
-        $primary_id_ref = self::_get_primary_key_name($classname);
1756
-        if (array_key_exists($primary_id_ref, $props_n_values) && ! empty($props_n_values[$primary_id_ref])) {
1757
-            $id = $props_n_values[$primary_id_ref];
1758
-            return self::_get_model($classname)->get_from_entity_map($id);
1759
-        }
1760
-        return false;
1761
-    }
1762
-
1763
-
1764
-
1765
-    /**
1766
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
1767
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
1768
-     * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
1769
-     * we return false.
1770
-     *
1771
-     * @param  array  $props_n_values   incoming array of properties and their values
1772
-     * @param  string $classname        the classname of the child class
1773
-     * @param null    $timezone
1774
-     * @param array   $date_formats     incoming date_formats in an array where the first value is the
1775
-     *                                  date_format and the second value is the time format
1776
-     * @return mixed (EE_Base_Class|bool)
1777
-     * @throws \EE_Error
1778
-     */
1779
-    protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
1780
-    {
1781
-        $existing = null;
1782
-        if (self::_get_model($classname)->has_primary_key_field()) {
1783
-            $primary_id_ref = self::_get_primary_key_name($classname);
1784
-            if (array_key_exists($primary_id_ref, $props_n_values)
1785
-                && ! empty($props_n_values[$primary_id_ref])
1786
-            ) {
1787
-                $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
1788
-                    $props_n_values[$primary_id_ref]
1789
-                );
1790
-            }
1791
-        } elseif (self::_get_model($classname, $timezone)->has_all_combined_primary_key_fields($props_n_values)) {
1792
-            //no primary key on this model, but there's still a matching item in the DB
1793
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
1794
-                self::_get_model($classname, $timezone)->get_index_primary_key_string($props_n_values)
1795
-            );
1796
-        }
1797
-        if ($existing) {
1798
-            //set date formats if present before setting values
1799
-            if ( ! empty($date_formats) && is_array($date_formats)) {
1800
-                $existing->set_date_format($date_formats[0]);
1801
-                $existing->set_time_format($date_formats[1]);
1802
-            } else {
1803
-                //set default formats for date and time
1804
-                $existing->set_date_format(get_option('date_format'));
1805
-                $existing->set_time_format(get_option('time_format'));
1806
-            }
1807
-            foreach ($props_n_values as $property => $field_value) {
1808
-                $existing->set($property, $field_value);
1809
-            }
1810
-            return $existing;
1811
-        } else {
1812
-            return false;
1813
-        }
1814
-    }
1815
-
1816
-
1817
-
1818
-    /**
1819
-     * Gets the EEM_*_Model for this class
1820
-     *
1821
-     * @access public now, as this is more convenient
1822
-     * @param      $classname
1823
-     * @param null $timezone
1824
-     * @throws EE_Error
1825
-     * @return EEM_Base
1826
-     */
1827
-    protected static function _get_model($classname, $timezone = null)
1828
-    {
1829
-        //find model for this class
1830
-        if ( ! $classname) {
1831
-            throw new EE_Error(
1832
-                sprintf(
1833
-                    __(
1834
-                        "What were you thinking calling _get_model(%s)?? You need to specify the class name",
1835
-                        "event_espresso"
1836
-                    ),
1837
-                    $classname
1838
-                )
1839
-            );
1840
-        }
1841
-        $modelName = self::_get_model_classname($classname);
1842
-        return self::_get_model_instance_with_name($modelName, $timezone);
1843
-    }
1844
-
1845
-
1846
-
1847
-    /**
1848
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
1849
-     *
1850
-     * @param string $model_classname
1851
-     * @param null   $timezone
1852
-     * @return EEM_Base
1853
-     */
1854
-    protected static function _get_model_instance_with_name($model_classname, $timezone = null)
1855
-    {
1856
-        $model_classname = str_replace('EEM_', '', $model_classname);
1857
-        $model = EE_Registry::instance()->load_model($model_classname);
1858
-        $model->set_timezone($timezone);
1859
-        return $model;
1860
-    }
1861
-
1862
-
1863
-
1864
-    /**
1865
-     * If a model name is provided (eg Registration), gets the model classname for that model.
1866
-     * Also works if a model class's classname is provided (eg EE_Registration).
1867
-     *
1868
-     * @param null $model_name
1869
-     * @return string like EEM_Attendee
1870
-     */
1871
-    private static function _get_model_classname($model_name = null)
1872
-    {
1873
-        if (strpos($model_name, "EE_") === 0) {
1874
-            $model_classname = str_replace("EE_", "EEM_", $model_name);
1875
-        } else {
1876
-            $model_classname = "EEM_" . $model_name;
1877
-        }
1878
-        return $model_classname;
1879
-    }
1880
-
1881
-
1882
-
1883
-    /**
1884
-     * returns the name of the primary key attribute
1885
-     *
1886
-     * @param null $classname
1887
-     * @throws EE_Error
1888
-     * @return string
1889
-     */
1890
-    protected static function _get_primary_key_name($classname = null)
1891
-    {
1892
-        if ( ! $classname) {
1893
-            throw new EE_Error(
1894
-                sprintf(
1895
-                    __("What were you thinking calling _get_primary_key_name(%s)", "event_espresso"),
1896
-                    $classname
1897
-                )
1898
-            );
1899
-        }
1900
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
1901
-    }
1902
-
1903
-
1904
-
1905
-    /**
1906
-     * Gets the value of the primary key.
1907
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
1908
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
1909
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
1910
-     *
1911
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
1912
-     * @throws \EE_Error
1913
-     */
1914
-    public function ID()
1915
-    {
1916
-        //now that we know the name of the variable, use a variable variable to get its value and return its
1917
-        if ($this->get_model()->has_primary_key_field()) {
1918
-            return $this->_fields[self::_get_primary_key_name(get_class($this))];
1919
-        } else {
1920
-            return $this->get_model()->get_index_primary_key_string($this->_fields);
1921
-        }
1922
-    }
1923
-
1924
-
1925
-
1926
-    /**
1927
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
1928
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
1929
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
1930
-     *
1931
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
1932
-     * @param string $relationName                     eg 'Events','Question',etc.
1933
-     *                                                 an attendee to a group, you also want to specify which role they
1934
-     *                                                 will have in that group. So you would use this parameter to
1935
-     *                                                 specify array('role-column-name'=>'role-id')
1936
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
1937
-     *                                                 allow you to further constrict the relation to being added.
1938
-     *                                                 However, keep in mind that the columns (keys) given must match a
1939
-     *                                                 column on the JOIN table and currently only the HABTM models
1940
-     *                                                 accept these additional conditions.  Also remember that if an
1941
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
1942
-     *                                                 NEW row is created in the join table.
1943
-     * @param null   $cache_id
1944
-     * @throws EE_Error
1945
-     * @return EE_Base_Class the object the relation was added to
1946
-     */
1947
-    public function _add_relation_to(
1948
-        $otherObjectModelObjectOrID,
1949
-        $relationName,
1950
-        $extra_join_model_fields_n_values = array(),
1951
-        $cache_id = null
1952
-    ) {
1953
-        //if this thing exists in the DB, save the relation to the DB
1954
-        if ($this->ID()) {
1955
-            $otherObject = $this->get_model()
1956
-                                ->add_relationship_to($this, $otherObjectModelObjectOrID, $relationName,
1957
-                                    $extra_join_model_fields_n_values);
1958
-            //clear cache so future get_many_related and get_first_related() return new results.
1959
-            $this->clear_cache($relationName, $otherObject, true);
1960
-            if ($otherObject instanceof EE_Base_Class) {
1961
-                $otherObject->clear_cache($this->get_model()->get_this_model_name(), $this);
1962
-            }
1963
-        } else {
1964
-            //this thing doesn't exist in the DB,  so just cache it
1965
-            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
1966
-                throw new EE_Error(sprintf(
1967
-                    __('Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
1968
-                        'event_espresso'),
1969
-                    $otherObjectModelObjectOrID,
1970
-                    get_class($this)
1971
-                ));
1972
-            } else {
1973
-                $otherObject = $otherObjectModelObjectOrID;
1974
-            }
1975
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
1976
-        }
1977
-        if ($otherObject instanceof EE_Base_Class) {
1978
-            //fix the reciprocal relation too
1979
-            if ($otherObject->ID()) {
1980
-                //its saved so assumed relations exist in the DB, so we can just
1981
-                //clear the cache so future queries use the updated info in the DB
1982
-                $otherObject->clear_cache($this->get_model()->get_this_model_name(), null, true);
1983
-            } else {
1984
-                //it's not saved, so it caches relations like this
1985
-                $otherObject->cache($this->get_model()->get_this_model_name(), $this);
1986
-            }
1987
-        }
1988
-        return $otherObject;
1989
-    }
1990
-
1991
-
1992
-
1993
-    /**
1994
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
1995
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
1996
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
1997
-     * from the cache
1998
-     *
1999
-     * @param mixed  $otherObjectModelObjectOrID
2000
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2001
-     *                to the DB yet
2002
-     * @param string $relationName
2003
-     * @param array  $where_query
2004
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2005
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2006
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2007
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2008
-     *                created in the join table.
2009
-     * @return EE_Base_Class the relation was removed from
2010
-     * @throws \EE_Error
2011
-     */
2012
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2013
-    {
2014
-        if ($this->ID()) {
2015
-            //if this exists in the DB, save the relation change to the DB too
2016
-            $otherObject = $this->get_model()
2017
-                                ->remove_relationship_to($this, $otherObjectModelObjectOrID, $relationName,
2018
-                                    $where_query);
2019
-            $this->clear_cache($relationName, $otherObject);
2020
-        } else {
2021
-            //this doesn't exist in the DB, just remove it from the cache
2022
-            $otherObject = $this->clear_cache($relationName, $otherObjectModelObjectOrID);
2023
-        }
2024
-        if ($otherObject instanceof EE_Base_Class) {
2025
-            $otherObject->clear_cache($this->get_model()->get_this_model_name(), $this);
2026
-        }
2027
-        return $otherObject;
2028
-    }
2029
-
2030
-
2031
-
2032
-    /**
2033
-     * Removes ALL the related things for the $relationName.
2034
-     *
2035
-     * @param string $relationName
2036
-     * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2037
-     * @return EE_Base_Class
2038
-     * @throws \EE_Error
2039
-     */
2040
-    public function _remove_relations($relationName, $where_query_params = array())
2041
-    {
2042
-        if ($this->ID()) {
2043
-            //if this exists in the DB, save the relation change to the DB too
2044
-            $otherObjects = $this->get_model()->remove_relations($this, $relationName, $where_query_params);
2045
-            $this->clear_cache($relationName, null, true);
2046
-        } else {
2047
-            //this doesn't exist in the DB, just remove it from the cache
2048
-            $otherObjects = $this->clear_cache($relationName, null, true);
2049
-        }
2050
-        if (is_array($otherObjects)) {
2051
-            foreach ($otherObjects as $otherObject) {
2052
-                $otherObject->clear_cache($this->get_model()->get_this_model_name(), $this);
2053
-            }
2054
-        }
2055
-        return $otherObjects;
2056
-    }
2057
-
2058
-
2059
-
2060
-    /**
2061
-     * Gets all the related model objects of the specified type. Eg, if the current class if
2062
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2063
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2064
-     * because we want to get even deleted items etc.
2065
-     *
2066
-     * @param string $relationName key in the model's _model_relations array
2067
-     * @param array  $query_params like EEM_Base::get_all
2068
-     * @return EE_Base_Class[] Results not necessarily indexed by IDs, because some results might not have primary keys
2069
-     * @throws \EE_Error
2070
-     *                             or might not be saved yet. Consider using EEM_Base::get_IDs() on these results if
2071
-     *                             you want IDs
2072
-     */
2073
-    public function get_many_related($relationName, $query_params = array())
2074
-    {
2075
-        if ($this->ID()) {
2076
-            //this exists in the DB, so get the related things from either the cache or the DB
2077
-            //if there are query parameters, forget about caching the related model objects.
2078
-            if ($query_params) {
2079
-                $related_model_objects = $this->get_model()->get_all_related($this, $relationName, $query_params);
2080
-            } else {
2081
-                //did we already cache the result of this query?
2082
-                $cached_results = $this->get_all_from_cache($relationName);
2083
-                if ( ! $cached_results) {
2084
-                    $related_model_objects = $this->get_model()->get_all_related($this, $relationName, $query_params);
2085
-                    //if no query parameters were passed, then we got all the related model objects
2086
-                    //for that relation. We can cache them then.
2087
-                    foreach ($related_model_objects as $related_model_object) {
2088
-                        $this->cache($relationName, $related_model_object);
2089
-                    }
2090
-                } else {
2091
-                    $related_model_objects = $cached_results;
2092
-                }
2093
-            }
2094
-        } else {
2095
-            //this doesn't exist in the DB, so just get the related things from the cache
2096
-            $related_model_objects = $this->get_all_from_cache($relationName);
2097
-        }
2098
-        return $related_model_objects;
2099
-    }
2100
-
2101
-
2102
-
2103
-    /**
2104
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2105
-     * unless otherwise specified in the $query_params
2106
-     *
2107
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2108
-     * @param array  $query_params   like EEM_Base::get_all's
2109
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2110
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2111
-     *                               that by the setting $distinct to TRUE;
2112
-     * @return int
2113
-     */
2114
-    public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2115
-    {
2116
-        return $this->get_model()->count_related($this, $relation_name, $query_params, $field_to_count, $distinct);
2117
-    }
2118
-
2119
-
2120
-
2121
-    /**
2122
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2123
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2124
-     *
2125
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2126
-     * @param array  $query_params  like EEM_Base::get_all's
2127
-     * @param string $field_to_sum  name of field to count by.
2128
-     *                              By default, uses primary key (which doesn't make much sense, so you should probably
2129
-     *                              change it)
2130
-     * @return int
2131
-     */
2132
-    public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2133
-    {
2134
-        return $this->get_model()->sum_related($this, $relation_name, $query_params, $field_to_sum);
2135
-    }
2136
-
2137
-
2138
-
2139
-    /**
2140
-     * Gets the first (ie, one) related model object of the specified type.
2141
-     *
2142
-     * @param string $relationName key in the model's _model_relations array
2143
-     * @param array  $query_params like EEM_Base::get_all
2144
-     * @return EE_Base_Class (not an array, a single object)
2145
-     * @throws \EE_Error
2146
-     */
2147
-    public function get_first_related($relationName, $query_params = array())
2148
-    {
2149
-        if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2150
-            //if they've provided some query parameters, don't bother trying to cache the result
2151
-            //also make sure we're not caching the result of get_first_related
2152
-            //on a relation which should have an array of objects (because the cache might have an array of objects)
2153
-            if ($query_params
2154
-                || ! $this->get_model()->related_settings_for($relationName)
2155
-                     instanceof
2156
-                     EE_Belongs_To_Relation
2157
-            ) {
2158
-                $related_model_object = $this->get_model()->get_first_related($this, $relationName, $query_params);
2159
-            } else {
2160
-                //first, check if we've already cached the result of this query
2161
-                $cached_result = $this->get_one_from_cache($relationName);
2162
-                if ( ! $cached_result) {
2163
-                    $related_model_object = $this->get_model()->get_first_related($this, $relationName, $query_params);
2164
-                    $this->cache($relationName, $related_model_object);
2165
-                } else {
2166
-                    $related_model_object = $cached_result;
2167
-                }
2168
-            }
2169
-        } else {
2170
-            $related_model_object = null;
2171
-            //this doesn't exist in the Db, but maybe the relation is of type belongs to, and so the related thing might
2172
-            if ($this->get_model()->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2173
-                $related_model_object = $this->get_model()->get_first_related($this, $relationName, $query_params);
2174
-            }
2175
-            //this doesn't exist in the DB and apparently the thing it belongs to doesn't either, just get what's cached on this object
2176
-            if ( ! $related_model_object) {
2177
-                $related_model_object = $this->get_one_from_cache($relationName);
2178
-            }
2179
-        }
2180
-        return $related_model_object;
2181
-    }
2182
-
2183
-
2184
-
2185
-    /**
2186
-     * Does a delete on all related objects of type $relationName and removes
2187
-     * the current model object's relation to them. If they can't be deleted (because
2188
-     * of blocking related model objects) does nothing. If the related model objects are
2189
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2190
-     * If this model object doesn't exist yet in the DB, just removes its related things
2191
-     *
2192
-     * @param string $relationName
2193
-     * @param array  $query_params like EEM_Base::get_all's
2194
-     * @return int how many deleted
2195
-     * @throws \EE_Error
2196
-     */
2197
-    public function delete_related($relationName, $query_params = array())
2198
-    {
2199
-        if ($this->ID()) {
2200
-            $count = $this->get_model()->delete_related($this, $relationName, $query_params);
2201
-        } else {
2202
-            $count = count($this->get_all_from_cache($relationName));
2203
-            $this->clear_cache($relationName, null, true);
2204
-        }
2205
-        return $count;
2206
-    }
2207
-
2208
-
2209
-
2210
-    /**
2211
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2212
-     * the current model object's relation to them. If they can't be deleted (because
2213
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2214
-     * If the related thing isn't a soft-deletable model object, this function is identical
2215
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2216
-     *
2217
-     * @param string $relationName
2218
-     * @param array  $query_params like EEM_Base::get_all's
2219
-     * @return int how many deleted (including those soft deleted)
2220
-     * @throws \EE_Error
2221
-     */
2222
-    public function delete_related_permanently($relationName, $query_params = array())
2223
-    {
2224
-        if ($this->ID()) {
2225
-            $count = $this->get_model()->delete_related_permanently($this, $relationName, $query_params);
2226
-        } else {
2227
-            $count = count($this->get_all_from_cache($relationName));
2228
-        }
2229
-        $this->clear_cache($relationName, null, true);
2230
-        return $count;
2231
-    }
2232
-
2233
-
2234
-
2235
-    /**
2236
-     * is_set
2237
-     * Just a simple utility function children can use for checking if property exists
2238
-     *
2239
-     * @access  public
2240
-     * @param  string $field_name property to check
2241
-     * @return bool                              TRUE if existing,FALSE if not.
2242
-     */
2243
-    public function is_set($field_name)
2244
-    {
2245
-        return isset($this->_fields[$field_name]);
2246
-    }
2247
-
2248
-
2249
-
2250
-    /**
2251
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2252
-     * EE_Error exception if they don't
2253
-     *
2254
-     * @param  mixed (string|array) $properties properties to check
2255
-     * @throws EE_Error
2256
-     * @return bool                              TRUE if existing, throw EE_Error if not.
2257
-     */
2258
-    protected function _property_exists($properties)
2259
-    {
2260
-        foreach ((array)$properties as $property_name) {
2261
-            //first make sure this property exists
2262
-            if ( ! $this->_fields[$property_name]) {
2263
-                throw new EE_Error(
2264
-                    sprintf(
2265
-                        __(
2266
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2267
-                            'event_espresso'
2268
-                        ),
2269
-                        $property_name
2270
-                    )
2271
-                );
2272
-            }
2273
-        }
2274
-        return true;
2275
-    }
2276
-
2277
-
2278
-
2279
-    /**
2280
-     * This simply returns an array of model fields for this object
2281
-     *
2282
-     * @return array
2283
-     * @throws \EE_Error
2284
-     */
2285
-    public function model_field_array()
2286
-    {
2287
-        $fields = $this->get_model()->field_settings(false);
2288
-        $properties = array();
2289
-        //remove prepended underscore
2290
-        foreach ($fields as $field_name => $settings) {
2291
-            $properties[$field_name] = $this->get($field_name);
2292
-        }
2293
-        return $properties;
2294
-    }
2295
-
2296
-
2297
-
2298
-    /**
2299
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2300
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2301
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments. Instead of
2302
-     * requiring a plugin to extend the EE_Base_Class (which works fine is there's only 1 plugin, but when will that
2303
-     * happen?) they can add a hook onto 'filters_hook_espresso__{className}__{methodName}' (eg,
2304
-     * filters_hook_espresso__EE_Answer__my_great_function) and accepts 2 arguments: the object on which the function
2305
-     * was called, and an array of the original arguments passed to the function. Whatever their callback function
2306
-     * returns will be returned by this function. Example: in functions.php (or in a plugin):
2307
-     * add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3); function
2308
-     * my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2309
-     * $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2310
-     *        return $previousReturnValue.$returnString;
2311
-     * }
2312
-     * require('EE_Answer.class.php');
2313
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2314
-     * echo $answer->my_callback('monkeys',100);
2315
-     * //will output "you called my_callback! and passed args:monkeys,100"
2316
-     *
2317
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2318
-     * @param array  $args       array of original arguments passed to the function
2319
-     * @throws EE_Error
2320
-     * @return mixed whatever the plugin which calls add_filter decides
2321
-     */
2322
-    public function __call($methodName, $args)
2323
-    {
2324
-        $className = get_class($this);
2325
-        $tagName = "FHEE__{$className}__{$methodName}";
2326
-        if ( ! has_filter($tagName)) {
2327
-            throw new EE_Error(
2328
-                sprintf(
2329
-                    __(
2330
-                        "Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2331
-                        "event_espresso"
2332
-                    ),
2333
-                    $methodName,
2334
-                    $className,
2335
-                    $tagName
2336
-                )
2337
-            );
2338
-        }
2339
-        return apply_filters($tagName, null, $this, $args);
2340
-    }
2341
-
2342
-
2343
-
2344
-    /**
2345
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2346
-     * A $previous_value can be specified in case there are many meta rows with the same key
2347
-     *
2348
-     * @param string $meta_key
2349
-     * @param string $meta_value
2350
-     * @param string $previous_value
2351
-     * @return int records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2352
-     * @throws \EE_Error
2353
-     * NOTE: if the values haven't changed, returns 0
2354
-     */
2355
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2356
-    {
2357
-        $query_params = array(
2358
-            array(
2359
-                'EXM_key'  => $meta_key,
2360
-                'OBJ_ID'   => $this->ID(),
2361
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2362
-            ),
2363
-        );
2364
-        if ($previous_value !== null) {
2365
-            $query_params[0]['EXM_value'] = $meta_value;
2366
-        }
2367
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2368
-        if ( ! $existing_rows_like_that) {
2369
-            return $this->add_extra_meta($meta_key, $meta_value);
2370
-        } else {
2371
-            foreach ($existing_rows_like_that as $existing_row) {
2372
-                $existing_row->save(array('EXM_value' => $meta_value));
2373
-            }
2374
-            return count($existing_rows_like_that);
2375
-        }
2376
-    }
2377
-
2378
-
2379
-
2380
-    /**
2381
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2382
-     * no other extra meta for this model object have the same key. Returns TRUE if the
2383
-     * extra meta row was entered, false if not
2384
-     *
2385
-     * @param string  $meta_key
2386
-     * @param string  $meta_value
2387
-     * @param boolean $unique
2388
-     * @return boolean
2389
-     * @throws \EE_Error
2390
-     */
2391
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
2392
-    {
2393
-        if ($unique) {
2394
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2395
-                array(
2396
-                    array(
2397
-                        'EXM_key'  => $meta_key,
2398
-                        'OBJ_ID'   => $this->ID(),
2399
-                        'EXM_type' => $this->get_model()->get_this_model_name(),
2400
-                    ),
2401
-                )
2402
-            );
2403
-            if ($existing_extra_meta) {
2404
-                return false;
2405
-            }
2406
-        }
2407
-        $new_extra_meta = EE_Extra_Meta::new_instance(
2408
-            array(
2409
-                'EXM_key'   => $meta_key,
2410
-                'EXM_value' => $meta_value,
2411
-                'OBJ_ID'    => $this->ID(),
2412
-                'EXM_type'  => $this->get_model()->get_this_model_name(),
2413
-            )
2414
-        );
2415
-        $new_extra_meta->save();
2416
-        return true;
2417
-    }
2418
-
2419
-
2420
-
2421
-    /**
2422
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2423
-     * is specified, only deletes extra meta records with that value.
2424
-     *
2425
-     * @param string $meta_key
2426
-     * @param string $meta_value
2427
-     * @return int number of extra meta rows deleted
2428
-     * @throws \EE_Error
2429
-     */
2430
-    public function delete_extra_meta($meta_key, $meta_value = null)
2431
-    {
2432
-        $query_params = array(
2433
-            array(
2434
-                'EXM_key'  => $meta_key,
2435
-                'OBJ_ID'   => $this->ID(),
2436
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2437
-            ),
2438
-        );
2439
-        if ($meta_value !== null) {
2440
-            $query_params[0]['EXM_value'] = $meta_value;
2441
-        }
2442
-        return EEM_Extra_Meta::instance()->delete($query_params);
2443
-    }
2444
-
2445
-
2446
-
2447
-    /**
2448
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2449
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2450
-     * You can specify $default is case you haven't found the extra meta
2451
-     *
2452
-     * @param string  $meta_key
2453
-     * @param boolean $single
2454
-     * @param mixed   $default if we don't find anything, what should we return?
2455
-     * @return mixed single value if $single; array if ! $single
2456
-     * @throws \EE_Error
2457
-     */
2458
-    public function get_extra_meta($meta_key, $single = false, $default = null)
2459
-    {
2460
-        if ($single) {
2461
-            $result = $this->get_first_related('Extra_Meta', array(array('EXM_key' => $meta_key)));
2462
-            if ($result instanceof EE_Extra_Meta) {
2463
-                return $result->value();
2464
-            } else {
2465
-                return $default;
2466
-            }
2467
-        } else {
2468
-            $results = $this->get_many_related('Extra_Meta', array(array('EXM_key' => $meta_key)));
2469
-            if ($results) {
2470
-                $values = array();
2471
-                foreach ($results as $result) {
2472
-                    if ($result instanceof EE_Extra_Meta) {
2473
-                        $values[$result->ID()] = $result->value();
2474
-                    }
2475
-                }
2476
-                return $values;
2477
-            } else {
2478
-                return $default;
2479
-            }
2480
-        }
2481
-    }
2482
-
2483
-
2484
-
2485
-    /**
2486
-     * Returns a simple array of all the extra meta associated with this model object.
2487
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2488
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2489
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2490
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2491
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2492
-     * finally the extra meta's value as each sub-value. (eg
2493
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2494
-     *
2495
-     * @param boolean $one_of_each_key
2496
-     * @return array
2497
-     * @throws \EE_Error
2498
-     */
2499
-    public function all_extra_meta_array($one_of_each_key = true)
2500
-    {
2501
-        $return_array = array();
2502
-        if ($one_of_each_key) {
2503
-            $extra_meta_objs = $this->get_many_related('Extra_Meta', array('group_by' => 'EXM_key'));
2504
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2505
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2506
-                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2507
-                }
2508
-            }
2509
-        } else {
2510
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2511
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2512
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2513
-                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2514
-                        $return_array[$extra_meta_obj->key()] = array();
2515
-                    }
2516
-                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2517
-                }
2518
-            }
2519
-        }
2520
-        return $return_array;
2521
-    }
2522
-
2523
-
2524
-
2525
-    /**
2526
-     * Gets a pretty nice displayable nice for this model object. Often overridden
2527
-     *
2528
-     * @return string
2529
-     * @throws \EE_Error
2530
-     */
2531
-    public function name()
2532
-    {
2533
-        //find a field that's not a text field
2534
-        $field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2535
-        if ($field_we_can_use) {
2536
-            return $this->get($field_we_can_use->get_name());
2537
-        } else {
2538
-            $first_few_properties = $this->model_field_array();
2539
-            $first_few_properties = array_slice($first_few_properties, 0, 3);
2540
-            $name_parts = array();
2541
-            foreach ($first_few_properties as $name => $value) {
2542
-                $name_parts[] = "$name:$value";
2543
-            }
2544
-            return implode(",", $name_parts);
2545
-        }
2546
-    }
2547
-
2548
-
2549
-
2550
-    /**
2551
-     * in_entity_map
2552
-     * Checks if this model object has been proven to already be in the entity map
2553
-     *
2554
-     * @return boolean
2555
-     * @throws \EE_Error
2556
-     */
2557
-    public function in_entity_map()
2558
-    {
2559
-        if ($this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this) {
2560
-            //well, if we looked, did we find it in the entity map?
2561
-            return true;
2562
-        } else {
2563
-            return false;
2564
-        }
2565
-    }
2566
-
2567
-
2568
-
2569
-    /**
2570
-     * refresh_from_db
2571
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
2572
-     *
2573
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
2574
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
2575
-     */
2576
-    public function refresh_from_db()
2577
-    {
2578
-        if ($this->ID() && $this->in_entity_map()) {
2579
-            $this->get_model()->refresh_entity_map_from_db($this->ID());
2580
-        } else {
2581
-            //if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
2582
-            //if it has an ID but it's not in the map, and you're asking me to refresh it
2583
-            //that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
2584
-            //absolutely nothing in it for this ID
2585
-            if (WP_DEBUG) {
2586
-                throw new EE_Error(
2587
-                    sprintf(
2588
-                        __('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
2589
-                            'event_espresso'),
2590
-                        $this->ID(),
2591
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2592
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2593
-                    )
2594
-                );
2595
-            }
2596
-        }
2597
-    }
2598
-
2599
-
2600
-
2601
-    /**
2602
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
2603
-     * (probably a bad assumption they have made, oh well)
2604
-     *
2605
-     * @return string
2606
-     */
2607
-    public function __toString()
2608
-    {
2609
-        try {
2610
-            return sprintf('%s (%s)', $this->name(), $this->ID());
2611
-        } catch (Exception $e) {
2612
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
2613
-            return '';
2614
-        }
2615
-    }
2616
-
2617
-
2618
-
2619
-    /**
2620
-     * Clear related model objects if they're already in the DB, because otherwise when we
2621
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
2622
-     * This means if we have made changes to those related model objects, and want to unserialize
2623
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
2624
-     * Instead, those related model objects should be directly serialized and stored.
2625
-     * Eg, the following won't work:
2626
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
2627
-     * $att = $reg->attendee();
2628
-     * $att->set( 'ATT_fname', 'Dirk' );
2629
-     * update_option( 'my_option', serialize( $reg ) );
2630
-     * //END REQUEST
2631
-     * //START NEXT REQUEST
2632
-     * $reg = get_option( 'my_option' );
2633
-     * $reg->attendee()->save();
2634
-     * And would need to be replace with:
2635
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
2636
-     * $att = $reg->attendee();
2637
-     * $att->set( 'ATT_fname', 'Dirk' );
2638
-     * update_option( 'my_option', serialize( $reg ) );
2639
-     * //END REQUEST
2640
-     * //START NEXT REQUEST
2641
-     * $att = get_option( 'my_option' );
2642
-     * $att->save();
2643
-     *
2644
-     * @return array
2645
-     * @throws \EE_Error
2646
-     */
2647
-    public function __sleep()
2648
-    {
2649
-        foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
2650
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
2651
-                $classname = 'EE_' . $this->get_model()->get_this_model_name();
2652
-                if (
2653
-                    $this->get_one_from_cache($relation_name) instanceof $classname
2654
-                    && $this->get_one_from_cache($relation_name)->ID()
2655
-                ) {
2656
-                    $this->clear_cache($relation_name, $this->get_one_from_cache($relation_name)->ID());
2657
-                }
2658
-            }
2659
-        }
2660
-        $this->_props_n_values_provided_in_constructor = array();
2661
-        return array_keys(get_object_vars($this));
2662
-    }
2663
-
2664
-
2665
-
2666
-    /**
2667
-     * restore _props_n_values_provided_in_constructor
2668
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
2669
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
2670
-     * At best, you would only be able to detect if state change has occurred during THIS request.
2671
-     */
2672
-    public function __wakeup()
2673
-    {
2674
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
2675
-    }
28
+	/**
29
+	 * This is an array of the original properties and values provided during construction
30
+	 * of this model object. (keys are model field names, values are their values).
31
+	 * This list is important to remember so that when we are merging data from the db, we know
32
+	 * which values to override and which to not override.
33
+	 *
34
+	 * @var array
35
+	 */
36
+	protected $_props_n_values_provided_in_constructor;
37
+
38
+	/**
39
+	 * Timezone
40
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
41
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
42
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
43
+	 * access to it.
44
+	 *
45
+	 * @var string
46
+	 */
47
+	protected $_timezone;
48
+
49
+
50
+
51
+	/**
52
+	 * date format
53
+	 * pattern or format for displaying dates
54
+	 *
55
+	 * @var string $_dt_frmt
56
+	 */
57
+	protected $_dt_frmt;
58
+
59
+
60
+
61
+	/**
62
+	 * time format
63
+	 * pattern or format for displaying time
64
+	 *
65
+	 * @var string $_tm_frmt
66
+	 */
67
+	protected $_tm_frmt;
68
+
69
+
70
+
71
+	/**
72
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
73
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
74
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
75
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
76
+	 *
77
+	 * @var array
78
+	 */
79
+	protected $_cached_properties = array();
80
+
81
+	/**
82
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
83
+	 * single
84
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
85
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
86
+	 * all others have an array)
87
+	 *
88
+	 * @var array
89
+	 */
90
+	protected $_model_relations = array();
91
+
92
+	/**
93
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
94
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
95
+	 *
96
+	 * @var array
97
+	 */
98
+	protected $_fields = array();
99
+
100
+	/**
101
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
102
+	 * For example, we might create model objects intended to only be used for the duration
103
+	 * of this request and to be thrown away, and if they were accidentally saved
104
+	 * it would be a bug.
105
+	 */
106
+	protected $_allow_persist = true;
107
+
108
+
109
+
110
+	/**
111
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
112
+	 * play nice
113
+	 *
114
+	 * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
115
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
116
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
117
+	 * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
118
+	 *                                                         corresponding db model or not.
119
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
120
+	 *                                                         be in when instantiating a EE_Base_Class object.
121
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
122
+	 *                                                         value is the date_format and second value is the time
123
+	 *                                                         format.
124
+	 * @throws EE_Error
125
+	 */
126
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
127
+	{
128
+		$className = get_class($this);
129
+		do_action("AHEE__{$className}__construct", $this, $fieldValues);
130
+		$model = $this->get_model();
131
+		$model_fields = $model->field_settings(false);
132
+		// ensure $fieldValues is an array
133
+		$fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
134
+		// EEH_Debug_Tools::printr( $fieldValues, '$fieldValues  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>', 'auto' );
135
+		// verify client code has not passed any invalid field names
136
+		foreach ($fieldValues as $field_name => $field_value) {
137
+			if ( ! isset($model_fields[$field_name])) {
138
+				throw new EE_Error(sprintf(__("Invalid field (%s) passed to constructor of %s. Allowed fields are :%s",
139
+					"event_espresso"), $field_name, get_class($this), implode(", ", array_keys($model_fields))));
140
+			}
141
+		}
142
+		// EEH_Debug_Tools::printr( $model_fields, '$model_fields  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>', 'auto' );
143
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
144
+		if ( ! empty($date_formats) && is_array($date_formats)) {
145
+			list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
146
+		} else {
147
+			//set default formats for date and time
148
+			$this->_dt_frmt = (string)get_option('date_format', 'Y-m-d');
149
+			$this->_tm_frmt = (string)get_option('time_format', 'g:i a');
150
+		}
151
+		//if db model is instantiating
152
+		if ($bydb) {
153
+			//client code has indicated these field values are from the database
154
+			foreach ($model_fields as $fieldName => $field) {
155
+				$this->set_from_db($fieldName, isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null);
156
+			}
157
+		} else {
158
+			//we're constructing a brand
159
+			//new instance of the model object. Generally, this means we'll need to do more field validation
160
+			foreach ($model_fields as $fieldName => $field) {
161
+				$this->set($fieldName, isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null, true);
162
+			}
163
+		}
164
+		//remember what values were passed to this constructor
165
+		$this->_props_n_values_provided_in_constructor = $fieldValues;
166
+		//remember in entity mapper
167
+		if ( ! $bydb && $model->has_primary_key_field() && $this->ID()) {
168
+			$model->add_to_entity_map($this);
169
+		}
170
+		//setup all the relations
171
+		foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
172
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
173
+				$this->_model_relations[$relation_name] = null;
174
+			} else {
175
+				$this->_model_relations[$relation_name] = array();
176
+			}
177
+		}
178
+		/**
179
+		 * Action done at the end of each model object construction
180
+		 *
181
+		 * @param EE_Base_Class $this the model object just created
182
+		 */
183
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
184
+	}
185
+
186
+
187
+
188
+	/**
189
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
190
+	 *
191
+	 * @return boolean
192
+	 */
193
+	public function allow_persist()
194
+	{
195
+		return $this->_allow_persist;
196
+	}
197
+
198
+
199
+
200
+	/**
201
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
202
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
203
+	 * you got new information that somehow made you change your mind.
204
+	 *
205
+	 * @param boolean $allow_persist
206
+	 * @return boolean
207
+	 */
208
+	public function set_allow_persist($allow_persist)
209
+	{
210
+		return $this->_allow_persist = $allow_persist;
211
+	}
212
+
213
+
214
+
215
+	/**
216
+	 * Gets the field's original value when this object was constructed during this request.
217
+	 * This can be helpful when determining if a model object has changed or not
218
+	 *
219
+	 * @param string $field_name
220
+	 * @return mixed|null
221
+	 * @throws \EE_Error
222
+	 */
223
+	public function get_original($field_name)
224
+	{
225
+		if (isset($this->_props_n_values_provided_in_constructor[$field_name])
226
+			&& $field_settings = $this->get_model()->field_settings_for($field_name)
227
+		) {
228
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
229
+		} else {
230
+			return null;
231
+		}
232
+	}
233
+
234
+
235
+
236
+	/**
237
+	 * @param EE_Base_Class $obj
238
+	 * @return string
239
+	 */
240
+	public function get_class($obj)
241
+	{
242
+		return get_class($obj);
243
+	}
244
+
245
+
246
+
247
+	/**
248
+	 * Overrides parent because parent expects old models.
249
+	 * This also doesn't do any validation, and won't work for serialized arrays
250
+	 *
251
+	 * @param    string $field_name
252
+	 * @param    mixed  $field_value
253
+	 * @param bool      $use_default
254
+	 * @throws \EE_Error
255
+	 */
256
+	public function set($field_name, $field_value, $use_default = false)
257
+	{
258
+		$field_obj = $this->get_model()->field_settings_for($field_name);
259
+		if ($field_obj instanceof EE_Model_Field_Base) {
260
+			//			if ( method_exists( $field_obj, 'set_timezone' )) {
261
+			if ($field_obj instanceof EE_Datetime_Field) {
262
+				$field_obj->set_timezone($this->_timezone);
263
+				$field_obj->set_date_format($this->_dt_frmt);
264
+				$field_obj->set_time_format($this->_tm_frmt);
265
+			}
266
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
267
+			//should the value be null?
268
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
269
+				$this->_fields[$field_name] = $field_obj->get_default_value();
270
+				/**
271
+				 * To save having to refactor all the models, if a default value is used for a
272
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
273
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
274
+				 * object.
275
+				 *
276
+				 * @since 4.6.10+
277
+				 */
278
+				if (
279
+					$field_obj instanceof EE_Datetime_Field
280
+					&& $this->_fields[$field_name] !== null
281
+					&& ! $this->_fields[$field_name] instanceof DateTime
282
+				) {
283
+					empty($this->_fields[$field_name])
284
+						? $this->set($field_name, time())
285
+						: $this->set($field_name, $this->_fields[$field_name]);
286
+				}
287
+			} else {
288
+				$this->_fields[$field_name] = $holder_of_value;
289
+			}
290
+			//if we're not in the constructor...
291
+			//now check if what we set was a primary key
292
+			if (
293
+				//note: props_n_values_provided_in_constructor is only set at the END of the constructor
294
+				$this->_props_n_values_provided_in_constructor
295
+				&& $field_value
296
+				&& $field_name === self::_get_primary_key_name(get_class($this))
297
+			) {
298
+				//if so, we want all this object's fields to be filled either with
299
+				//what we've explicitly set on this model
300
+				//or what we have in the db
301
+				// echo "setting primary key!";
302
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
303
+				$obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
304
+				foreach ($fields_on_model as $field_obj) {
305
+					if ( ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
306
+						 && $field_obj->get_name() !== $field_name
307
+					) {
308
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
309
+					}
310
+				}
311
+				//oh this model object has an ID? well make sure its in the entity mapper
312
+				$this->get_model()->add_to_entity_map($this);
313
+			}
314
+			//let's unset any cache for this field_name from the $_cached_properties property.
315
+			$this->_clear_cached_property($field_name);
316
+		} else {
317
+			throw new EE_Error(sprintf(__("A valid EE_Model_Field_Base could not be found for the given field name: %s",
318
+				"event_espresso"), $field_name));
319
+		}
320
+	}
321
+
322
+
323
+
324
+	/**
325
+	 * This sets the field value on the db column if it exists for the given $column_name or
326
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
327
+	 *
328
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
329
+	 * @param string $field_name  Must be the exact column name.
330
+	 * @param mixed  $field_value The value to set.
331
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
332
+	 * @throws \EE_Error
333
+	 */
334
+	public function set_field_or_extra_meta($field_name, $field_value)
335
+	{
336
+		if ($this->get_model()->has_field($field_name)) {
337
+			$this->set($field_name, $field_value);
338
+			return true;
339
+		} else {
340
+			//ensure this object is saved first so that extra meta can be properly related.
341
+			$this->save();
342
+			return $this->update_extra_meta($field_name, $field_value);
343
+		}
344
+	}
345
+
346
+
347
+
348
+	/**
349
+	 * This retrieves the value of the db column set on this class or if that's not present
350
+	 * it will attempt to retrieve from extra_meta if found.
351
+	 * Example Usage:
352
+	 * Via EE_Message child class:
353
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
354
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
355
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
356
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
357
+	 * value for those extra fields dynamically via the EE_message object.
358
+	 *
359
+	 * @param  string $field_name expecting the fully qualified field name.
360
+	 * @return mixed|null  value for the field if found.  null if not found.
361
+	 * @throws \EE_Error
362
+	 */
363
+	public function get_field_or_extra_meta($field_name)
364
+	{
365
+		if ($this->get_model()->has_field($field_name)) {
366
+			$column_value = $this->get($field_name);
367
+		} else {
368
+			//This isn't a column in the main table, let's see if it is in the extra meta.
369
+			$column_value = $this->get_extra_meta($field_name, true, null);
370
+		}
371
+		return $column_value;
372
+	}
373
+
374
+
375
+
376
+	/**
377
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
378
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
379
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
380
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
381
+	 *
382
+	 * @access public
383
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
384
+	 * @return void
385
+	 * @throws \EE_Error
386
+	 */
387
+	public function set_timezone($timezone = '')
388
+	{
389
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
390
+		//make sure we clear all cached properties because they won't be relevant now
391
+		$this->_clear_cached_properties();
392
+		//make sure we update field settings and the date for all EE_Datetime_Fields
393
+		$model_fields = $this->get_model()->field_settings(false);
394
+		foreach ($model_fields as $field_name => $field_obj) {
395
+			if ($field_obj instanceof EE_Datetime_Field) {
396
+				$field_obj->set_timezone($this->_timezone);
397
+				if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
398
+					$this->_fields[$field_name]->setTimezone(new DateTimeZone($this->_timezone));
399
+				}
400
+			}
401
+		}
402
+	}
403
+
404
+
405
+
406
+	/**
407
+	 * This just returns whatever is set for the current timezone.
408
+	 *
409
+	 * @access public
410
+	 * @return string timezone string
411
+	 */
412
+	public function get_timezone()
413
+	{
414
+		return $this->_timezone;
415
+	}
416
+
417
+
418
+
419
+	/**
420
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
421
+	 * internally instead of wp set date format options
422
+	 *
423
+	 * @since 4.6
424
+	 * @param string $format should be a format recognizable by PHP date() functions.
425
+	 */
426
+	public function set_date_format($format)
427
+	{
428
+		$this->_dt_frmt = $format;
429
+		//clear cached_properties because they won't be relevant now.
430
+		$this->_clear_cached_properties();
431
+	}
432
+
433
+
434
+
435
+	/**
436
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
437
+	 * class internally instead of wp set time format options.
438
+	 *
439
+	 * @since 4.6
440
+	 * @param string $format should be a format recognizable by PHP date() functions.
441
+	 */
442
+	public function set_time_format($format)
443
+	{
444
+		$this->_tm_frmt = $format;
445
+		//clear cached_properties because they won't be relevant now.
446
+		$this->_clear_cached_properties();
447
+	}
448
+
449
+
450
+
451
+	/**
452
+	 * This returns the current internal set format for the date and time formats.
453
+	 *
454
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
455
+	 *                             where the first value is the date format and the second value is the time format.
456
+	 * @return mixed string|array
457
+	 */
458
+	public function get_format($full = true)
459
+	{
460
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
461
+	}
462
+
463
+
464
+
465
+	/**
466
+	 * cache
467
+	 * stores the passed model object on the current model object.
468
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
469
+	 *
470
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
471
+	 *                                       'Registration' associated with this model object
472
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
473
+	 *                                       that could be a payment or a registration)
474
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
475
+	 *                                       items which will be stored in an array on this object
476
+	 * @throws EE_Error
477
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
478
+	 *                  related thing, no array)
479
+	 */
480
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
481
+	{
482
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
483
+		if ( ! $object_to_cache instanceof EE_Base_Class) {
484
+			return false;
485
+		}
486
+		// also get "how" the object is related, or throw an error
487
+		if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
488
+			throw new EE_Error(sprintf(__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
489
+				$relationName, get_class($this)));
490
+		}
491
+		// how many things are related ?
492
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
493
+			// if it's a "belongs to" relationship, then there's only one related model object  eg, if this is a registration, there's only 1 attendee for it
494
+			// so for these model objects just set it to be cached
495
+			$this->_model_relations[$relationName] = $object_to_cache;
496
+			$return = true;
497
+		} else {
498
+			// otherwise, this is the "many" side of a one to many relationship, so we'll add the object to the array of related objects for that type.
499
+			// eg: if this is an event, there are many registrations for that event, so we cache the registrations in an array
500
+			if ( ! is_array($this->_model_relations[$relationName])) {
501
+				// if for some reason, the cached item is a model object, then stick that in the array, otherwise start with an empty array
502
+				$this->_model_relations[$relationName] = $this->_model_relations[$relationName] instanceof EE_Base_Class
503
+					? array($this->_model_relations[$relationName]) : array();
504
+			}
505
+			// first check for a cache_id which is normally empty
506
+			if ( ! empty($cache_id)) {
507
+				// if the cache_id exists, then it means we are purposely trying to cache this with a known key that can then be used to retrieve the object later on
508
+				$this->_model_relations[$relationName][$cache_id] = $object_to_cache;
509
+				$return = $cache_id;
510
+			} elseif ($object_to_cache->ID()) {
511
+				// OR the cached object originally came from the db, so let's just use it's PK for an ID
512
+				$this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
513
+				$return = $object_to_cache->ID();
514
+			} else {
515
+				// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
516
+				$this->_model_relations[$relationName][] = $object_to_cache;
517
+				// move the internal pointer to the end of the array
518
+				end($this->_model_relations[$relationName]);
519
+				// and grab the key so that we can return it
520
+				$return = key($this->_model_relations[$relationName]);
521
+			}
522
+		}
523
+		return $return;
524
+	}
525
+
526
+
527
+
528
+	/**
529
+	 * For adding an item to the cached_properties property.
530
+	 *
531
+	 * @access protected
532
+	 * @param string      $fieldname the property item the corresponding value is for.
533
+	 * @param mixed       $value     The value we are caching.
534
+	 * @param string|null $cache_type
535
+	 * @return void
536
+	 * @throws \EE_Error
537
+	 */
538
+	protected function _set_cached_property($fieldname, $value, $cache_type = null)
539
+	{
540
+		//first make sure this property exists
541
+		$this->get_model()->field_settings_for($fieldname);
542
+		$cache_type = empty($cache_type) ? 'standard' : $cache_type;
543
+		$this->_cached_properties[$fieldname][$cache_type] = $value;
544
+	}
545
+
546
+
547
+
548
+	/**
549
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
550
+	 * This also SETS the cache if we return the actual property!
551
+	 *
552
+	 * @param string $fieldname        the name of the property we're trying to retrieve
553
+	 * @param bool   $pretty
554
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
555
+	 *                                 (in cases where the same property may be used for different outputs
556
+	 *                                 - i.e. datetime, money etc.)
557
+	 *                                 It can also accept certain pre-defined "schema" strings
558
+	 *                                 to define how to output the property.
559
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
560
+	 * @return mixed                   whatever the value for the property is we're retrieving
561
+	 * @throws \EE_Error
562
+	 */
563
+	protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
564
+	{
565
+		//verify the field exists
566
+		$this->get_model()->field_settings_for($fieldname);
567
+		$cache_type = $pretty ? 'pretty' : 'standard';
568
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
569
+		if (isset($this->_cached_properties[$fieldname][$cache_type])) {
570
+			return $this->_cached_properties[$fieldname][$cache_type];
571
+		}
572
+		$field_obj = $this->get_model()->field_settings_for($fieldname);
573
+		if ($field_obj instanceof EE_Model_Field_Base) {
574
+			/**
575
+			 * maybe this is EE_Datetime_Field.  If so we need to make sure timezone and
576
+			 * formats are correct.
577
+			 */
578
+			if ($field_obj instanceof EE_Datetime_Field) {
579
+				$field_obj->set_timezone($this->_timezone);
580
+				$field_obj->set_date_format($this->_dt_frmt, $pretty);
581
+				$field_obj->set_time_format($this->_tm_frmt, $pretty);
582
+			}
583
+			if ( ! isset($this->_fields[$fieldname])) {
584
+				$this->_fields[$fieldname] = null;
585
+			}
586
+			$value = $pretty
587
+				? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
588
+				: $field_obj->prepare_for_get($this->_fields[$fieldname]);
589
+			$this->_set_cached_property($fieldname, $value, $cache_type);
590
+			return $value;
591
+		}
592
+		return null;
593
+	}
594
+
595
+
596
+
597
+	/**
598
+	 * This just takes care of clearing out the cached_properties
599
+	 *
600
+	 * @return void
601
+	 */
602
+	protected function _clear_cached_properties()
603
+	{
604
+		$this->_cached_properties = array();
605
+	}
606
+
607
+
608
+
609
+	/**
610
+	 * This just clears out ONE property if it exists in the cache
611
+	 *
612
+	 * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
613
+	 * @return void
614
+	 */
615
+	protected function _clear_cached_property($property_name)
616
+	{
617
+		if (isset($this->_cached_properties[$property_name])) {
618
+			unset($this->_cached_properties[$property_name]);
619
+		}
620
+	}
621
+
622
+
623
+
624
+	/**
625
+	 * Ensures that this related thing is a model object.
626
+	 *
627
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
628
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
629
+	 * @return EE_Base_Class
630
+	 * @throws \EE_Error
631
+	 */
632
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
633
+	{
634
+		$other_model_instance = self::_get_model_instance_with_name(
635
+			self::_get_model_classname($model_name),
636
+			$this->_timezone
637
+		);
638
+		return $other_model_instance->ensure_is_obj($object_or_id);
639
+	}
640
+
641
+
642
+
643
+	/**
644
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
645
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
646
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
647
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
648
+	 *
649
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
650
+	 *                                                     Eg 'Registration'
651
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
652
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
653
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
654
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
655
+	 *                                                     this is HasMany or HABTM.
656
+	 * @throws EE_Error
657
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
658
+	 *                       relation from all
659
+	 */
660
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
661
+	{
662
+		$relationship_to_model = $this->get_model()->related_settings_for($relationName);
663
+		$index_in_cache = '';
664
+		if ( ! $relationship_to_model) {
665
+			throw new EE_Error(
666
+				sprintf(
667
+					__("There is no relationship to %s on a %s. Cannot clear that cache", 'event_espresso'),
668
+					$relationName,
669
+					get_class($this)
670
+				)
671
+			);
672
+		}
673
+		if ($clear_all) {
674
+			$obj_removed = true;
675
+			$this->_model_relations[$relationName] = null;
676
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
677
+			$obj_removed = $this->_model_relations[$relationName];
678
+			$this->_model_relations[$relationName] = null;
679
+		} else {
680
+			if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
681
+				&& $object_to_remove_or_index_into_array->ID()
682
+			) {
683
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
684
+				if (is_array($this->_model_relations[$relationName])
685
+					&& ! isset($this->_model_relations[$relationName][$index_in_cache])
686
+				) {
687
+					$index_found_at = null;
688
+					//find this object in the array even though it has a different key
689
+					foreach ($this->_model_relations[$relationName] as $index => $obj) {
690
+						if (
691
+							$obj instanceof EE_Base_Class
692
+							&& (
693
+								$obj == $object_to_remove_or_index_into_array
694
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
695
+							)
696
+						) {
697
+							$index_found_at = $index;
698
+							break;
699
+						}
700
+					}
701
+					if ($index_found_at) {
702
+						$index_in_cache = $index_found_at;
703
+					} else {
704
+						//it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
705
+						//if it wasn't in it to begin with. So we're done
706
+						return $object_to_remove_or_index_into_array;
707
+					}
708
+				}
709
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
710
+				//so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
711
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
712
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
713
+						$index_in_cache = $index;
714
+					}
715
+				}
716
+			} else {
717
+				$index_in_cache = $object_to_remove_or_index_into_array;
718
+			}
719
+			//supposedly we've found it. But it could just be that the client code
720
+			//provided a bad index/object
721
+			if (
722
+			isset(
723
+				$this->_model_relations[$relationName],
724
+				$this->_model_relations[$relationName][$index_in_cache]
725
+			)
726
+			) {
727
+				$obj_removed = $this->_model_relations[$relationName][$index_in_cache];
728
+				unset($this->_model_relations[$relationName][$index_in_cache]);
729
+			} else {
730
+				//that thing was never cached anyways.
731
+				$obj_removed = null;
732
+			}
733
+		}
734
+		return $obj_removed;
735
+	}
736
+
737
+
738
+
739
+	/**
740
+	 * update_cache_after_object_save
741
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
742
+	 * obtained after being saved to the db
743
+	 *
744
+	 * @param string         $relationName       - the type of object that is cached
745
+	 * @param \EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
746
+	 * @param string         $current_cache_id   - the ID that was used when originally caching the object
747
+	 * @return boolean TRUE on success, FALSE on fail
748
+	 * @throws \EE_Error
749
+	 */
750
+	public function update_cache_after_object_save(
751
+		$relationName,
752
+		EE_Base_Class $newly_saved_object,
753
+		$current_cache_id = ''
754
+	) {
755
+		// verify that incoming object is of the correct type
756
+		$obj_class = 'EE_' . $relationName;
757
+		if ($newly_saved_object instanceof $obj_class) {
758
+			/* @type EE_Base_Class $newly_saved_object */
759
+			// now get the type of relation
760
+			$relationship_to_model = $this->get_model()->related_settings_for($relationName);
761
+			// if this is a 1:1 relationship
762
+			if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
763
+				// then just replace the cached object with the newly saved object
764
+				$this->_model_relations[$relationName] = $newly_saved_object;
765
+				return true;
766
+				// or if it's some kind of sordid feral polyamorous relationship...
767
+			} elseif (is_array($this->_model_relations[$relationName])
768
+					  && isset($this->_model_relations[$relationName][$current_cache_id])
769
+			) {
770
+				// then remove the current cached item
771
+				unset($this->_model_relations[$relationName][$current_cache_id]);
772
+				// and cache the newly saved object using it's new ID
773
+				$this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
774
+				return true;
775
+			}
776
+		}
777
+		return false;
778
+	}
779
+
780
+
781
+
782
+	/**
783
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
784
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
785
+	 *
786
+	 * @param string $relationName
787
+	 * @return EE_Base_Class
788
+	 */
789
+	public function get_one_from_cache($relationName)
790
+	{
791
+		$cached_array_or_object = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName]
792
+			: null;
793
+		if (is_array($cached_array_or_object)) {
794
+			return array_shift($cached_array_or_object);
795
+		} else {
796
+			return $cached_array_or_object;
797
+		}
798
+	}
799
+
800
+
801
+
802
+	/**
803
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
804
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
805
+	 *
806
+	 * @param string $relationName
807
+	 * @throws \EE_Error
808
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
809
+	 */
810
+	public function get_all_from_cache($relationName)
811
+	{
812
+		$objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
813
+		// if the result is not an array, but exists, make it an array
814
+		$objects = is_array($objects) ? $objects : array($objects);
815
+		//bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
816
+		//basically, if this model object was stored in the session, and these cached model objects
817
+		//already have IDs, let's make sure they're in their model's entity mapper
818
+		//otherwise we will have duplicates next time we call
819
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
820
+		$model = EE_Registry::instance()->load_model($relationName);
821
+		foreach ($objects as $model_object) {
822
+			if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
823
+				//ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
824
+				if ($model_object->ID()) {
825
+					$model->add_to_entity_map($model_object);
826
+				}
827
+			} else {
828
+				throw new EE_Error(
829
+					sprintf(
830
+						__(
831
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
832
+							'event_espresso'
833
+						),
834
+						$relationName,
835
+						gettype($model_object)
836
+					)
837
+				);
838
+			}
839
+		}
840
+		return $objects;
841
+	}
842
+
843
+
844
+
845
+	/**
846
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
847
+	 * matching the given query conditions.
848
+	 *
849
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
850
+	 * @param int   $limit              How many objects to return.
851
+	 * @param array $query_params       Any additional conditions on the query.
852
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
853
+	 *                                  you can indicate just the columns you want returned
854
+	 * @return array|EE_Base_Class[]
855
+	 * @throws \EE_Error
856
+	 */
857
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
858
+	{
859
+		$field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
860
+			? $this->get_model()->get_primary_key_field()->get_name()
861
+			: $field_to_order_by;
862
+		$current_value = ! empty($field) ? $this->get($field) : null;
863
+		if (empty($field) || empty($current_value)) {
864
+			return array();
865
+		}
866
+		return $this->get_model()->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
867
+	}
868
+
869
+
870
+
871
+	/**
872
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
873
+	 * matching the given query conditions.
874
+	 *
875
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
876
+	 * @param int   $limit              How many objects to return.
877
+	 * @param array $query_params       Any additional conditions on the query.
878
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
879
+	 *                                  you can indicate just the columns you want returned
880
+	 * @return array|EE_Base_Class[]
881
+	 * @throws \EE_Error
882
+	 */
883
+	public function previous_x(
884
+		$field_to_order_by = null,
885
+		$limit = 1,
886
+		$query_params = array(),
887
+		$columns_to_select = null
888
+	) {
889
+		$field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
890
+			? $this->get_model()->get_primary_key_field()->get_name()
891
+			: $field_to_order_by;
892
+		$current_value = ! empty($field) ? $this->get($field) : null;
893
+		if (empty($field) || empty($current_value)) {
894
+			return array();
895
+		}
896
+		return $this->get_model()->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
897
+	}
898
+
899
+
900
+
901
+	/**
902
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
903
+	 * matching the given query conditions.
904
+	 *
905
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
906
+	 * @param array $query_params       Any additional conditions on the query.
907
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
908
+	 *                                  you can indicate just the columns you want returned
909
+	 * @return array|EE_Base_Class
910
+	 * @throws \EE_Error
911
+	 */
912
+	public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
913
+	{
914
+		$field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
915
+			? $this->get_model()->get_primary_key_field()->get_name()
916
+			: $field_to_order_by;
917
+		$current_value = ! empty($field) ? $this->get($field) : null;
918
+		if (empty($field) || empty($current_value)) {
919
+			return array();
920
+		}
921
+		return $this->get_model()->next($current_value, $field, $query_params, $columns_to_select);
922
+	}
923
+
924
+
925
+
926
+	/**
927
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
928
+	 * matching the given query conditions.
929
+	 *
930
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
931
+	 * @param array $query_params       Any additional conditions on the query.
932
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
933
+	 *                                  you can indicate just the column you want returned
934
+	 * @return array|EE_Base_Class
935
+	 * @throws \EE_Error
936
+	 */
937
+	public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
938
+	{
939
+		$field = empty($field_to_order_by) && $this->get_model()->has_primary_key_field()
940
+			? $this->get_model()->get_primary_key_field()->get_name()
941
+			: $field_to_order_by;
942
+		$current_value = ! empty($field) ? $this->get($field) : null;
943
+		if (empty($field) || empty($current_value)) {
944
+			return array();
945
+		}
946
+		return $this->get_model()->previous($current_value, $field, $query_params, $columns_to_select);
947
+	}
948
+
949
+
950
+
951
+	/**
952
+	 * Overrides parent because parent expects old models.
953
+	 * This also doesn't do any validation, and won't work for serialized arrays
954
+	 *
955
+	 * @param string $field_name
956
+	 * @param mixed  $field_value_from_db
957
+	 * @throws \EE_Error
958
+	 */
959
+	public function set_from_db($field_name, $field_value_from_db)
960
+	{
961
+		$field_obj = $this->get_model()->field_settings_for($field_name);
962
+		if ($field_obj instanceof EE_Model_Field_Base) {
963
+			//you would think the DB has no NULLs for non-null label fields right? wrong!
964
+			//eg, a CPT model object could have an entry in the posts table, but no
965
+			//entry in the meta table. Meaning that all its columns in the meta table
966
+			//are null! yikes! so when we find one like that, use defaults for its meta columns
967
+			if ($field_value_from_db === null) {
968
+				if ($field_obj->is_nullable()) {
969
+					//if the field allows nulls, then let it be null
970
+					$field_value = null;
971
+				} else {
972
+					$field_value = $field_obj->get_default_value();
973
+				}
974
+			} else {
975
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
976
+			}
977
+			$this->_fields[$field_name] = $field_value;
978
+			$this->_clear_cached_property($field_name);
979
+		}
980
+	}
981
+
982
+
983
+
984
+	/**
985
+	 * verifies that the specified field is of the correct type
986
+	 *
987
+	 * @param string $field_name
988
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
989
+	 *                                (in cases where the same property may be used for different outputs
990
+	 *                                - i.e. datetime, money etc.)
991
+	 * @return mixed
992
+	 * @throws \EE_Error
993
+	 */
994
+	public function get($field_name, $extra_cache_ref = null)
995
+	{
996
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
997
+	}
998
+
999
+
1000
+
1001
+	/**
1002
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1003
+	 *
1004
+	 * @param  string $field_name A valid fieldname
1005
+	 * @return mixed              Whatever the raw value stored on the property is.
1006
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1007
+	 */
1008
+	public function get_raw($field_name)
1009
+	{
1010
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1011
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1012
+			? $this->_fields[$field_name]->format('U')
1013
+			: $this->_fields[$field_name];
1014
+	}
1015
+
1016
+
1017
+
1018
+	/**
1019
+	 * This is used to return the internal DateTime object used for a field that is a
1020
+	 * EE_Datetime_Field.
1021
+	 *
1022
+	 * @param string $field_name               The field name retrieving the DateTime object.
1023
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1024
+	 * @throws \EE_Error
1025
+	 *                                         an error is set and false returned.  If the field IS an
1026
+	 *                                         EE_Datetime_Field and but the field value is null, then
1027
+	 *                                         just null is returned (because that indicates that likely
1028
+	 *                                         this field is nullable).
1029
+	 */
1030
+	public function get_DateTime_object($field_name)
1031
+	{
1032
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1033
+		if ( ! $field_settings instanceof EE_Datetime_Field) {
1034
+			EE_Error::add_error(
1035
+				sprintf(
1036
+					__(
1037
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1038
+						'event_espresso'
1039
+					),
1040
+					$field_name
1041
+				),
1042
+				__FILE__,
1043
+				__FUNCTION__,
1044
+				__LINE__
1045
+			);
1046
+			return false;
1047
+		}
1048
+		return $this->_fields[$field_name];
1049
+	}
1050
+
1051
+
1052
+
1053
+	/**
1054
+	 * To be used in template to immediately echo out the value, and format it for output.
1055
+	 * Eg, should call stripslashes and whatnot before echoing
1056
+	 *
1057
+	 * @param string $field_name      the name of the field as it appears in the DB
1058
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1059
+	 *                                (in cases where the same property may be used for different outputs
1060
+	 *                                - i.e. datetime, money etc.)
1061
+	 * @return void
1062
+	 * @throws \EE_Error
1063
+	 */
1064
+	public function e($field_name, $extra_cache_ref = null)
1065
+	{
1066
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1067
+	}
1068
+
1069
+
1070
+
1071
+	/**
1072
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1073
+	 * can be easily used as the value of form input.
1074
+	 *
1075
+	 * @param string $field_name
1076
+	 * @return void
1077
+	 * @throws \EE_Error
1078
+	 */
1079
+	public function f($field_name)
1080
+	{
1081
+		$this->e($field_name, 'form_input');
1082
+	}
1083
+
1084
+
1085
+
1086
+	/**
1087
+	 * @param string $field_name
1088
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1089
+	 *                                (in cases where the same property may be used for different outputs
1090
+	 *                                - i.e. datetime, money etc.)
1091
+	 * @return mixed
1092
+	 * @throws \EE_Error
1093
+	 */
1094
+	public function get_pretty($field_name, $extra_cache_ref = null)
1095
+	{
1096
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1097
+	}
1098
+
1099
+
1100
+
1101
+	/**
1102
+	 * This simply returns the datetime for the given field name
1103
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1104
+	 * (and the equivalent e_date, e_time, e_datetime).
1105
+	 *
1106
+	 * @access   protected
1107
+	 * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1108
+	 * @param string   $dt_frmt      valid datetime format used for date
1109
+	 *                               (if '' then we just use the default on the field,
1110
+	 *                               if NULL we use the last-used format)
1111
+	 * @param string   $tm_frmt      Same as above except this is for time format
1112
+	 * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1113
+	 * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1114
+	 * @return void|string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1115
+	 *                               if field is not a valid dtt field, or void if echoing
1116
+	 * @throws \EE_Error
1117
+	 */
1118
+	protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1119
+	{
1120
+		$in_dt_frmt = empty($dt_frmt) ? $this->_dt_frmt : $dt_frmt;
1121
+		$in_tm_frmt = empty($tm_frmt) ? $this->_tm_frmt : $tm_frmt;
1122
+		//validate field for datetime and returns field settings if valid.
1123
+		$field = $this->_get_dtt_field_settings($field_name);
1124
+		//clear cached property if either formats are not null.
1125
+		if ($dt_frmt !== null || $tm_frmt !== null) {
1126
+			$this->_clear_cached_property($field_name);
1127
+			//reset format properties because they are used in get()
1128
+			$this->_dt_frmt = $in_dt_frmt;
1129
+			$this->_tm_frmt = $in_tm_frmt;
1130
+		}
1131
+		if ($echo) {
1132
+			$field->set_pretty_date_format($in_dt_frmt);
1133
+		} else {
1134
+			$field->set_date_format($in_dt_frmt);
1135
+		}
1136
+		if ($echo) {
1137
+			$field->set_pretty_time_format($in_tm_frmt);
1138
+		} else {
1139
+			$field->set_time_format($in_tm_frmt);
1140
+		}
1141
+		//set timezone in field object
1142
+		$field->set_timezone($this->_timezone);
1143
+		//set the output returned
1144
+		switch ($date_or_time) {
1145
+			case 'D' :
1146
+				$field->set_date_time_output('date');
1147
+				break;
1148
+			case 'T' :
1149
+				$field->set_date_time_output('time');
1150
+				break;
1151
+			default :
1152
+				$field->set_date_time_output();
1153
+		}
1154
+		if ($echo) {
1155
+			$this->e($field_name, $date_or_time);
1156
+			return '';
1157
+		}
1158
+		return $this->get($field_name, $date_or_time);
1159
+	}
1160
+
1161
+
1162
+
1163
+	/**
1164
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1165
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1166
+	 * other echoes the pretty value for dtt)
1167
+	 *
1168
+	 * @param  string $field_name name of model object datetime field holding the value
1169
+	 * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1170
+	 * @return string            datetime value formatted
1171
+	 * @throws \EE_Error
1172
+	 */
1173
+	public function get_date($field_name, $format = null)
1174
+	{
1175
+		return $this->_get_datetime($field_name, $format, null, 'D');
1176
+	}
1177
+
1178
+
1179
+
1180
+	/**
1181
+	 * @param      $field_name
1182
+	 * @param null $format
1183
+	 * @throws \EE_Error
1184
+	 */
1185
+	public function e_date($field_name, $format = null)
1186
+	{
1187
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1188
+	}
1189
+
1190
+
1191
+
1192
+	/**
1193
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1194
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1195
+	 * other echoes the pretty value for dtt)
1196
+	 *
1197
+	 * @param  string $field_name name of model object datetime field holding the value
1198
+	 * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1199
+	 * @return string             datetime value formatted
1200
+	 * @throws \EE_Error
1201
+	 */
1202
+	public function get_time($field_name, $format = null)
1203
+	{
1204
+		return $this->_get_datetime($field_name, null, $format, 'T');
1205
+	}
1206
+
1207
+
1208
+
1209
+	/**
1210
+	 * @param      $field_name
1211
+	 * @param null $format
1212
+	 * @throws \EE_Error
1213
+	 */
1214
+	public function e_time($field_name, $format = null)
1215
+	{
1216
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1217
+	}
1218
+
1219
+
1220
+
1221
+	/**
1222
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1223
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1224
+	 * other echoes the pretty value for dtt)
1225
+	 *
1226
+	 * @param  string $field_name name of model object datetime field holding the value
1227
+	 * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1228
+	 * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1229
+	 * @return string             datetime value formatted
1230
+	 * @throws \EE_Error
1231
+	 */
1232
+	public function get_datetime($field_name, $dt_frmt = null, $tm_frmt = null)
1233
+	{
1234
+		return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1235
+	}
1236
+
1237
+
1238
+
1239
+	/**
1240
+	 * @param      $field_name
1241
+	 * @param null $dt_frmt
1242
+	 * @param null $tm_frmt
1243
+	 * @throws \EE_Error
1244
+	 */
1245
+	public function e_datetime($field_name, $dt_frmt = null, $tm_frmt = null)
1246
+	{
1247
+		$this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1248
+	}
1249
+
1250
+
1251
+
1252
+	/**
1253
+	 * Get the i8ln value for a date using the WordPress @see date_i18n function.
1254
+	 *
1255
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1256
+	 * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1257
+	 *                           on the object will be used.
1258
+	 * @return string Date and time string in set locale or false if no field exists for the given
1259
+	 * @throws \EE_Error
1260
+	 *                           field name.
1261
+	 */
1262
+	public function get_i18n_datetime($field_name, $format = null)
1263
+	{
1264
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1265
+		return date_i18n(
1266
+			$format,
1267
+			EEH_DTT_Helper::get_timestamp_with_offset($this->get_raw($field_name), $this->_timezone)
1268
+		);
1269
+	}
1270
+
1271
+
1272
+
1273
+	/**
1274
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
1275
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1276
+	 * thrown.
1277
+	 *
1278
+	 * @param  string $field_name The field name being checked
1279
+	 * @throws EE_Error
1280
+	 * @return EE_Datetime_Field
1281
+	 */
1282
+	protected function _get_dtt_field_settings($field_name)
1283
+	{
1284
+		$field = $this->get_model()->field_settings_for($field_name);
1285
+		//check if field is dtt
1286
+		if ($field instanceof EE_Datetime_Field) {
1287
+			return $field;
1288
+		} else {
1289
+			throw new EE_Error(sprintf(__('The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1290
+				'event_espresso'), $field_name, self::_get_model_classname(get_class($this))));
1291
+		}
1292
+	}
1293
+
1294
+
1295
+
1296
+
1297
+	/**
1298
+	 * NOTE ABOUT BELOW:
1299
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1300
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1301
+	 * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1302
+	 * method and make sure you send the entire datetime value for setting.
1303
+	 */
1304
+	/**
1305
+	 * sets the time on a datetime property
1306
+	 *
1307
+	 * @access protected
1308
+	 * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1309
+	 * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1310
+	 * @throws \EE_Error
1311
+	 */
1312
+	protected function _set_time_for($time, $fieldname)
1313
+	{
1314
+		$this->_set_date_time('T', $time, $fieldname);
1315
+	}
1316
+
1317
+
1318
+
1319
+	/**
1320
+	 * sets the date on a datetime property
1321
+	 *
1322
+	 * @access protected
1323
+	 * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1324
+	 * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1325
+	 * @throws \EE_Error
1326
+	 */
1327
+	protected function _set_date_for($date, $fieldname)
1328
+	{
1329
+		$this->_set_date_time('D', $date, $fieldname);
1330
+	}
1331
+
1332
+
1333
+
1334
+	/**
1335
+	 * This takes care of setting a date or time independently on a given model object property. This method also
1336
+	 * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1337
+	 *
1338
+	 * @access protected
1339
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1340
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1341
+	 * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1342
+	 *                                        EE_Datetime_Field property)
1343
+	 * @throws \EE_Error
1344
+	 */
1345
+	protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1346
+	{
1347
+		$field = $this->_get_dtt_field_settings($fieldname);
1348
+		$field->set_timezone($this->_timezone);
1349
+		$field->set_date_format($this->_dt_frmt);
1350
+		$field->set_time_format($this->_tm_frmt);
1351
+		switch ($what) {
1352
+			case 'T' :
1353
+				$this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1354
+					$datetime_value,
1355
+					$this->_fields[$fieldname]
1356
+				);
1357
+				break;
1358
+			case 'D' :
1359
+				$this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1360
+					$datetime_value,
1361
+					$this->_fields[$fieldname]
1362
+				);
1363
+				break;
1364
+			case 'B' :
1365
+				$this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1366
+				break;
1367
+		}
1368
+		$this->_clear_cached_property($fieldname);
1369
+	}
1370
+
1371
+
1372
+
1373
+	/**
1374
+	 * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1375
+	 * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1376
+	 * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1377
+	 * that could lead to some unexpected results!
1378
+	 *
1379
+	 * @access public
1380
+	 * @param string               $field_name This is the name of the field on the object that contains the date/time
1381
+	 *                                         value being returned.
1382
+	 * @param string               $callback   must match a valid method in this class (defaults to get_datetime)
1383
+	 * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1384
+	 * @param string               $prepend    You can include something to prepend on the timestamp
1385
+	 * @param string               $append     You can include something to append on the timestamp
1386
+	 * @throws EE_Error
1387
+	 * @return string timestamp
1388
+	 */
1389
+	public function display_in_my_timezone(
1390
+		$field_name,
1391
+		$callback = 'get_datetime',
1392
+		$args = null,
1393
+		$prepend = '',
1394
+		$append = ''
1395
+	) {
1396
+		$timezone = EEH_DTT_Helper::get_timezone();
1397
+		if ($timezone === $this->_timezone) {
1398
+			return '';
1399
+		}
1400
+		$original_timezone = $this->_timezone;
1401
+		$this->set_timezone($timezone);
1402
+		$fn = (array)$field_name;
1403
+		$args = array_merge($fn, (array)$args);
1404
+		if ( ! method_exists($this, $callback)) {
1405
+			throw new EE_Error(
1406
+				sprintf(
1407
+					__(
1408
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1409
+						'event_espresso'
1410
+					),
1411
+					$callback
1412
+				)
1413
+			);
1414
+		}
1415
+		$args = (array)$args;
1416
+		$return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1417
+		$this->set_timezone($original_timezone);
1418
+		return $return;
1419
+	}
1420
+
1421
+
1422
+
1423
+	/**
1424
+	 * Deletes this model object.
1425
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1426
+	 * override
1427
+	 * `EE_Base_Class::_delete` NOT this class.
1428
+	 *
1429
+	 * @return boolean | int
1430
+	 * @throws \EE_Error
1431
+	 */
1432
+	public function delete()
1433
+	{
1434
+		/**
1435
+		 * Called just before the `EE_Base_Class::_delete` method call.
1436
+		 * Note: `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1437
+		 * should be aware that `_delete` may not always result in a permanent delete.  For example, `EE_Soft_Delete_Base_Class::_delete`
1438
+		 * soft deletes (trash) the object and does not permanently delete it.
1439
+		 *
1440
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1441
+		 */
1442
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
1443
+		$result = $this->_delete();
1444
+		/**
1445
+		 * Called just after the `EE_Base_Class::_delete` method call.
1446
+		 * Note: `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1447
+		 * should be aware that `_delete` may not always result in a permanent delete.  For example `EE_Soft_Base_Class::_delete`
1448
+		 * soft deletes (trash) the object and does not permanently delete it.
1449
+		 *
1450
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1451
+		 * @param boolean       $result
1452
+		 */
1453
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1454
+		return $result;
1455
+	}
1456
+
1457
+
1458
+
1459
+	/**
1460
+	 * Calls the specific delete method for the instantiated class.
1461
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1462
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1463
+	 * `EE_Base_Class::delete`
1464
+	 *
1465
+	 * @return bool|int
1466
+	 * @throws \EE_Error
1467
+	 */
1468
+	protected function _delete()
1469
+	{
1470
+		return $this->delete_permanently();
1471
+	}
1472
+
1473
+
1474
+
1475
+	/**
1476
+	 * Deletes this model object permanently from db (but keep in mind related models my block the delete and return an
1477
+	 * error)
1478
+	 *
1479
+	 * @return bool | int
1480
+	 * @throws \EE_Error
1481
+	 */
1482
+	public function delete_permanently()
1483
+	{
1484
+		/**
1485
+		 * Called just before HARD deleting a model object
1486
+		 *
1487
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1488
+		 */
1489
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1490
+		$model = $this->get_model();
1491
+		$result = $model->delete_permanently_by_ID($this->ID());
1492
+		$this->refresh_cache_of_related_objects();
1493
+		/**
1494
+		 * Called just after HARD deleting a model object
1495
+		 *
1496
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1497
+		 * @param boolean       $result
1498
+		 */
1499
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1500
+		return $result;
1501
+	}
1502
+
1503
+
1504
+
1505
+	/**
1506
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1507
+	 * related model objects
1508
+	 *
1509
+	 * @throws \EE_Error
1510
+	 */
1511
+	public function refresh_cache_of_related_objects()
1512
+	{
1513
+		foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
1514
+			if ( ! empty($this->_model_relations[$relation_name])) {
1515
+				$related_objects = $this->_model_relations[$relation_name];
1516
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
1517
+					//this relation only stores a single model object, not an array
1518
+					//but let's make it consistent
1519
+					$related_objects = array($related_objects);
1520
+				}
1521
+				foreach ($related_objects as $related_object) {
1522
+					//only refresh their cache if they're in memory
1523
+					if ($related_object instanceof EE_Base_Class) {
1524
+						$related_object->clear_cache($this->get_model()->get_this_model_name(), $this);
1525
+					}
1526
+				}
1527
+			}
1528
+		}
1529
+	}
1530
+
1531
+
1532
+
1533
+	/**
1534
+	 *        Saves this object to the database. An array may be supplied to set some values on this
1535
+	 * object just before saving.
1536
+	 *
1537
+	 * @access public
1538
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
1539
+	 *                                 if provided during the save() method (often client code will change the fields'
1540
+	 *                                 values before calling save)
1541
+	 * @throws \EE_Error
1542
+	 * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1543
+	 *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1544
+	 */
1545
+	public function save($set_cols_n_values = array())
1546
+	{
1547
+		/**
1548
+		 * Filters the fields we're about to save on the model object
1549
+		 *
1550
+		 * @param array         $set_cols_n_values
1551
+		 * @param EE_Base_Class $model_object
1552
+		 */
1553
+		$set_cols_n_values = (array)apply_filters('FHEE__EE_Base_Class__save__set_cols_n_values', $set_cols_n_values,
1554
+			$this);
1555
+		//set attributes as provided in $set_cols_n_values
1556
+		foreach ($set_cols_n_values as $column => $value) {
1557
+			$this->set($column, $value);
1558
+		}
1559
+		/**
1560
+		 * Saving a model object.
1561
+		 * Before we perform a save, this action is fired.
1562
+		 *
1563
+		 * @param EE_Base_Class $model_object the model object about to be saved.
1564
+		 */
1565
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
1566
+		if ( ! $this->allow_persist()) {
1567
+			return 0;
1568
+		}
1569
+		//now get current attribute values
1570
+		$save_cols_n_values = $this->_fields;
1571
+		//if the object already has an ID, update it. Otherwise, insert it
1572
+		//also: change the assumption about values passed to the model NOT being prepare dby the model object. They have been
1573
+		$old_assumption_concerning_value_preparation = $this->get_model()
1574
+															->get_assumption_concerning_values_already_prepared_by_model_object();
1575
+		$this->get_model()->assume_values_already_prepared_by_model_object(true);
1576
+		//does this model have an autoincrement PK?
1577
+		if ($this->get_model()->has_primary_key_field()) {
1578
+			if ($this->get_model()->get_primary_key_field()->is_auto_increment()) {
1579
+				//ok check if it's set, if so: update; if not, insert
1580
+				if ( ! empty($save_cols_n_values[self::_get_primary_key_name(get_class($this))])) {
1581
+					$results = $this->get_model()->update_by_ID($save_cols_n_values, $this->ID());
1582
+				} else {
1583
+					unset($save_cols_n_values[self::_get_primary_key_name(get_class($this))]);
1584
+					$results = $this->get_model()->insert($save_cols_n_values);
1585
+					if ($results) {
1586
+						//if successful, set the primary key
1587
+						//but don't use the normal SET method, because it will check if
1588
+						//an item with the same ID exists in the mapper & db, then
1589
+						//will find it in the db (because we just added it) and THAT object
1590
+						//will get added to the mapper before we can add this one!
1591
+						//but if we just avoid using the SET method, all that headache can be avoided
1592
+						$pk_field_name = self::_get_primary_key_name(get_class($this));
1593
+						$this->_fields[$pk_field_name] = $results;
1594
+						$this->_clear_cached_property($pk_field_name);
1595
+						$this->get_model()->add_to_entity_map($this);
1596
+						$this->_update_cached_related_model_objs_fks();
1597
+					}
1598
+				}
1599
+			} else {//PK is NOT auto-increment
1600
+				//so check if one like it already exists in the db
1601
+				if ($this->get_model()->exists_by_ID($this->ID())) {
1602
+					if (WP_DEBUG && ! $this->in_entity_map()) {
1603
+						throw new EE_Error(
1604
+							sprintf(
1605
+								__('Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1606
+									'event_espresso'),
1607
+								get_class($this),
1608
+								get_class($this->get_model()) . '::instance()->add_to_entity_map()',
1609
+								get_class($this->get_model()) . '::instance()->get_one_by_ID()',
1610
+								'<br />'
1611
+							)
1612
+						);
1613
+					}
1614
+					$results = $this->get_model()->update_by_ID($save_cols_n_values, $this->ID());
1615
+				} else {
1616
+					$results = $this->get_model()->insert($save_cols_n_values);
1617
+					$this->_update_cached_related_model_objs_fks();
1618
+				}
1619
+			}
1620
+		} else {//there is NO primary key
1621
+			$already_in_db = false;
1622
+			foreach ($this->get_model()->unique_indexes() as $index) {
1623
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1624
+				if ($this->get_model()->exists(array($uniqueness_where_params))) {
1625
+					$already_in_db = true;
1626
+				}
1627
+			}
1628
+			if ($already_in_db) {
1629
+				$combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1630
+					$this->get_model()->get_combined_primary_key_fields());
1631
+				$results = $this->get_model()->update($save_cols_n_values, $combined_pk_fields_n_values);
1632
+			} else {
1633
+				$results = $this->get_model()->insert($save_cols_n_values);
1634
+			}
1635
+		}
1636
+		//restore the old assumption about values being prepared by the model object
1637
+		$this->get_model()
1638
+			 ->assume_values_already_prepared_by_model_object($old_assumption_concerning_value_preparation);
1639
+		/**
1640
+		 * After saving the model object this action is called
1641
+		 *
1642
+		 * @param EE_Base_Class $model_object which was just saved
1643
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1644
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1645
+		 */
1646
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1647
+		return $results;
1648
+	}
1649
+
1650
+
1651
+
1652
+	/**
1653
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1654
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1655
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1656
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1657
+	 * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1658
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1659
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1660
+	 *
1661
+	 * @return void
1662
+	 * @throws \EE_Error
1663
+	 */
1664
+	protected function _update_cached_related_model_objs_fks()
1665
+	{
1666
+		foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
1667
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1668
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1669
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1670
+						$this->get_model()->get_this_model_name()
1671
+					);
1672
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1673
+					if ($related_model_obj_in_cache->ID()) {
1674
+						$related_model_obj_in_cache->save();
1675
+					}
1676
+				}
1677
+			}
1678
+		}
1679
+	}
1680
+
1681
+
1682
+
1683
+	/**
1684
+	 * Saves this model object and its NEW cached relations to the database.
1685
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1686
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
1687
+	 * because otherwise, there's a potential for infinite looping of saving
1688
+	 * Saves the cached related model objects, and ensures the relation between them
1689
+	 * and this object and properly setup
1690
+	 *
1691
+	 * @return int ID of new model object on save; 0 on failure+
1692
+	 * @throws \EE_Error
1693
+	 */
1694
+	public function save_new_cached_related_model_objs()
1695
+	{
1696
+		//make sure this has been saved
1697
+		if ( ! $this->ID()) {
1698
+			$id = $this->save();
1699
+		} else {
1700
+			$id = $this->ID();
1701
+		}
1702
+		//now save all the NEW cached model objects  (ie they don't exist in the DB)
1703
+		foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1704
+			if ($this->_model_relations[$relationName]) {
1705
+				//is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1706
+				//or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1707
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
1708
+					//add a relation to that relation type (which saves the appropriate thing in the process)
1709
+					//but ONLY if it DOES NOT exist in the DB
1710
+					/* @var $related_model_obj EE_Base_Class */
1711
+					$related_model_obj = $this->_model_relations[$relationName];
1712
+					//					if( ! $related_model_obj->ID()){
1713
+					$this->_add_relation_to($related_model_obj, $relationName);
1714
+					$related_model_obj->save_new_cached_related_model_objs();
1715
+					//					}
1716
+				} else {
1717
+					foreach ($this->_model_relations[$relationName] as $related_model_obj) {
1718
+						//add a relation to that relation type (which saves the appropriate thing in the process)
1719
+						//but ONLY if it DOES NOT exist in the DB
1720
+						//						if( ! $related_model_obj->ID()){
1721
+						$this->_add_relation_to($related_model_obj, $relationName);
1722
+						$related_model_obj->save_new_cached_related_model_objs();
1723
+						//						}
1724
+					}
1725
+				}
1726
+			}
1727
+		}
1728
+		return $id;
1729
+	}
1730
+
1731
+
1732
+
1733
+	/**
1734
+	 * for getting a model while instantiated.
1735
+	 *
1736
+	 * @return \EEM_Base | \EEM_CPT_Base
1737
+	 */
1738
+	public function get_model()
1739
+	{
1740
+		$modelName = self::_get_model_classname(get_class($this));
1741
+		return self::_get_model_instance_with_name($modelName, $this->_timezone);
1742
+	}
1743
+
1744
+
1745
+
1746
+	/**
1747
+	 * @param $props_n_values
1748
+	 * @param $classname
1749
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
1750
+	 * @throws \EE_Error
1751
+	 */
1752
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
1753
+	{
1754
+		//TODO: will not work for Term_Relationships because they have no PK!
1755
+		$primary_id_ref = self::_get_primary_key_name($classname);
1756
+		if (array_key_exists($primary_id_ref, $props_n_values) && ! empty($props_n_values[$primary_id_ref])) {
1757
+			$id = $props_n_values[$primary_id_ref];
1758
+			return self::_get_model($classname)->get_from_entity_map($id);
1759
+		}
1760
+		return false;
1761
+	}
1762
+
1763
+
1764
+
1765
+	/**
1766
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
1767
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
1768
+	 * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
1769
+	 * we return false.
1770
+	 *
1771
+	 * @param  array  $props_n_values   incoming array of properties and their values
1772
+	 * @param  string $classname        the classname of the child class
1773
+	 * @param null    $timezone
1774
+	 * @param array   $date_formats     incoming date_formats in an array where the first value is the
1775
+	 *                                  date_format and the second value is the time format
1776
+	 * @return mixed (EE_Base_Class|bool)
1777
+	 * @throws \EE_Error
1778
+	 */
1779
+	protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
1780
+	{
1781
+		$existing = null;
1782
+		if (self::_get_model($classname)->has_primary_key_field()) {
1783
+			$primary_id_ref = self::_get_primary_key_name($classname);
1784
+			if (array_key_exists($primary_id_ref, $props_n_values)
1785
+				&& ! empty($props_n_values[$primary_id_ref])
1786
+			) {
1787
+				$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
1788
+					$props_n_values[$primary_id_ref]
1789
+				);
1790
+			}
1791
+		} elseif (self::_get_model($classname, $timezone)->has_all_combined_primary_key_fields($props_n_values)) {
1792
+			//no primary key on this model, but there's still a matching item in the DB
1793
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
1794
+				self::_get_model($classname, $timezone)->get_index_primary_key_string($props_n_values)
1795
+			);
1796
+		}
1797
+		if ($existing) {
1798
+			//set date formats if present before setting values
1799
+			if ( ! empty($date_formats) && is_array($date_formats)) {
1800
+				$existing->set_date_format($date_formats[0]);
1801
+				$existing->set_time_format($date_formats[1]);
1802
+			} else {
1803
+				//set default formats for date and time
1804
+				$existing->set_date_format(get_option('date_format'));
1805
+				$existing->set_time_format(get_option('time_format'));
1806
+			}
1807
+			foreach ($props_n_values as $property => $field_value) {
1808
+				$existing->set($property, $field_value);
1809
+			}
1810
+			return $existing;
1811
+		} else {
1812
+			return false;
1813
+		}
1814
+	}
1815
+
1816
+
1817
+
1818
+	/**
1819
+	 * Gets the EEM_*_Model for this class
1820
+	 *
1821
+	 * @access public now, as this is more convenient
1822
+	 * @param      $classname
1823
+	 * @param null $timezone
1824
+	 * @throws EE_Error
1825
+	 * @return EEM_Base
1826
+	 */
1827
+	protected static function _get_model($classname, $timezone = null)
1828
+	{
1829
+		//find model for this class
1830
+		if ( ! $classname) {
1831
+			throw new EE_Error(
1832
+				sprintf(
1833
+					__(
1834
+						"What were you thinking calling _get_model(%s)?? You need to specify the class name",
1835
+						"event_espresso"
1836
+					),
1837
+					$classname
1838
+				)
1839
+			);
1840
+		}
1841
+		$modelName = self::_get_model_classname($classname);
1842
+		return self::_get_model_instance_with_name($modelName, $timezone);
1843
+	}
1844
+
1845
+
1846
+
1847
+	/**
1848
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
1849
+	 *
1850
+	 * @param string $model_classname
1851
+	 * @param null   $timezone
1852
+	 * @return EEM_Base
1853
+	 */
1854
+	protected static function _get_model_instance_with_name($model_classname, $timezone = null)
1855
+	{
1856
+		$model_classname = str_replace('EEM_', '', $model_classname);
1857
+		$model = EE_Registry::instance()->load_model($model_classname);
1858
+		$model->set_timezone($timezone);
1859
+		return $model;
1860
+	}
1861
+
1862
+
1863
+
1864
+	/**
1865
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
1866
+	 * Also works if a model class's classname is provided (eg EE_Registration).
1867
+	 *
1868
+	 * @param null $model_name
1869
+	 * @return string like EEM_Attendee
1870
+	 */
1871
+	private static function _get_model_classname($model_name = null)
1872
+	{
1873
+		if (strpos($model_name, "EE_") === 0) {
1874
+			$model_classname = str_replace("EE_", "EEM_", $model_name);
1875
+		} else {
1876
+			$model_classname = "EEM_" . $model_name;
1877
+		}
1878
+		return $model_classname;
1879
+	}
1880
+
1881
+
1882
+
1883
+	/**
1884
+	 * returns the name of the primary key attribute
1885
+	 *
1886
+	 * @param null $classname
1887
+	 * @throws EE_Error
1888
+	 * @return string
1889
+	 */
1890
+	protected static function _get_primary_key_name($classname = null)
1891
+	{
1892
+		if ( ! $classname) {
1893
+			throw new EE_Error(
1894
+				sprintf(
1895
+					__("What were you thinking calling _get_primary_key_name(%s)", "event_espresso"),
1896
+					$classname
1897
+				)
1898
+			);
1899
+		}
1900
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
1901
+	}
1902
+
1903
+
1904
+
1905
+	/**
1906
+	 * Gets the value of the primary key.
1907
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
1908
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
1909
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
1910
+	 *
1911
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
1912
+	 * @throws \EE_Error
1913
+	 */
1914
+	public function ID()
1915
+	{
1916
+		//now that we know the name of the variable, use a variable variable to get its value and return its
1917
+		if ($this->get_model()->has_primary_key_field()) {
1918
+			return $this->_fields[self::_get_primary_key_name(get_class($this))];
1919
+		} else {
1920
+			return $this->get_model()->get_index_primary_key_string($this->_fields);
1921
+		}
1922
+	}
1923
+
1924
+
1925
+
1926
+	/**
1927
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
1928
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
1929
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
1930
+	 *
1931
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
1932
+	 * @param string $relationName                     eg 'Events','Question',etc.
1933
+	 *                                                 an attendee to a group, you also want to specify which role they
1934
+	 *                                                 will have in that group. So you would use this parameter to
1935
+	 *                                                 specify array('role-column-name'=>'role-id')
1936
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
1937
+	 *                                                 allow you to further constrict the relation to being added.
1938
+	 *                                                 However, keep in mind that the columns (keys) given must match a
1939
+	 *                                                 column on the JOIN table and currently only the HABTM models
1940
+	 *                                                 accept these additional conditions.  Also remember that if an
1941
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
1942
+	 *                                                 NEW row is created in the join table.
1943
+	 * @param null   $cache_id
1944
+	 * @throws EE_Error
1945
+	 * @return EE_Base_Class the object the relation was added to
1946
+	 */
1947
+	public function _add_relation_to(
1948
+		$otherObjectModelObjectOrID,
1949
+		$relationName,
1950
+		$extra_join_model_fields_n_values = array(),
1951
+		$cache_id = null
1952
+	) {
1953
+		//if this thing exists in the DB, save the relation to the DB
1954
+		if ($this->ID()) {
1955
+			$otherObject = $this->get_model()
1956
+								->add_relationship_to($this, $otherObjectModelObjectOrID, $relationName,
1957
+									$extra_join_model_fields_n_values);
1958
+			//clear cache so future get_many_related and get_first_related() return new results.
1959
+			$this->clear_cache($relationName, $otherObject, true);
1960
+			if ($otherObject instanceof EE_Base_Class) {
1961
+				$otherObject->clear_cache($this->get_model()->get_this_model_name(), $this);
1962
+			}
1963
+		} else {
1964
+			//this thing doesn't exist in the DB,  so just cache it
1965
+			if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
1966
+				throw new EE_Error(sprintf(
1967
+					__('Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
1968
+						'event_espresso'),
1969
+					$otherObjectModelObjectOrID,
1970
+					get_class($this)
1971
+				));
1972
+			} else {
1973
+				$otherObject = $otherObjectModelObjectOrID;
1974
+			}
1975
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
1976
+		}
1977
+		if ($otherObject instanceof EE_Base_Class) {
1978
+			//fix the reciprocal relation too
1979
+			if ($otherObject->ID()) {
1980
+				//its saved so assumed relations exist in the DB, so we can just
1981
+				//clear the cache so future queries use the updated info in the DB
1982
+				$otherObject->clear_cache($this->get_model()->get_this_model_name(), null, true);
1983
+			} else {
1984
+				//it's not saved, so it caches relations like this
1985
+				$otherObject->cache($this->get_model()->get_this_model_name(), $this);
1986
+			}
1987
+		}
1988
+		return $otherObject;
1989
+	}
1990
+
1991
+
1992
+
1993
+	/**
1994
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
1995
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
1996
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
1997
+	 * from the cache
1998
+	 *
1999
+	 * @param mixed  $otherObjectModelObjectOrID
2000
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2001
+	 *                to the DB yet
2002
+	 * @param string $relationName
2003
+	 * @param array  $where_query
2004
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2005
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2006
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2007
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2008
+	 *                created in the join table.
2009
+	 * @return EE_Base_Class the relation was removed from
2010
+	 * @throws \EE_Error
2011
+	 */
2012
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2013
+	{
2014
+		if ($this->ID()) {
2015
+			//if this exists in the DB, save the relation change to the DB too
2016
+			$otherObject = $this->get_model()
2017
+								->remove_relationship_to($this, $otherObjectModelObjectOrID, $relationName,
2018
+									$where_query);
2019
+			$this->clear_cache($relationName, $otherObject);
2020
+		} else {
2021
+			//this doesn't exist in the DB, just remove it from the cache
2022
+			$otherObject = $this->clear_cache($relationName, $otherObjectModelObjectOrID);
2023
+		}
2024
+		if ($otherObject instanceof EE_Base_Class) {
2025
+			$otherObject->clear_cache($this->get_model()->get_this_model_name(), $this);
2026
+		}
2027
+		return $otherObject;
2028
+	}
2029
+
2030
+
2031
+
2032
+	/**
2033
+	 * Removes ALL the related things for the $relationName.
2034
+	 *
2035
+	 * @param string $relationName
2036
+	 * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2037
+	 * @return EE_Base_Class
2038
+	 * @throws \EE_Error
2039
+	 */
2040
+	public function _remove_relations($relationName, $where_query_params = array())
2041
+	{
2042
+		if ($this->ID()) {
2043
+			//if this exists in the DB, save the relation change to the DB too
2044
+			$otherObjects = $this->get_model()->remove_relations($this, $relationName, $where_query_params);
2045
+			$this->clear_cache($relationName, null, true);
2046
+		} else {
2047
+			//this doesn't exist in the DB, just remove it from the cache
2048
+			$otherObjects = $this->clear_cache($relationName, null, true);
2049
+		}
2050
+		if (is_array($otherObjects)) {
2051
+			foreach ($otherObjects as $otherObject) {
2052
+				$otherObject->clear_cache($this->get_model()->get_this_model_name(), $this);
2053
+			}
2054
+		}
2055
+		return $otherObjects;
2056
+	}
2057
+
2058
+
2059
+
2060
+	/**
2061
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
2062
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2063
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2064
+	 * because we want to get even deleted items etc.
2065
+	 *
2066
+	 * @param string $relationName key in the model's _model_relations array
2067
+	 * @param array  $query_params like EEM_Base::get_all
2068
+	 * @return EE_Base_Class[] Results not necessarily indexed by IDs, because some results might not have primary keys
2069
+	 * @throws \EE_Error
2070
+	 *                             or might not be saved yet. Consider using EEM_Base::get_IDs() on these results if
2071
+	 *                             you want IDs
2072
+	 */
2073
+	public function get_many_related($relationName, $query_params = array())
2074
+	{
2075
+		if ($this->ID()) {
2076
+			//this exists in the DB, so get the related things from either the cache or the DB
2077
+			//if there are query parameters, forget about caching the related model objects.
2078
+			if ($query_params) {
2079
+				$related_model_objects = $this->get_model()->get_all_related($this, $relationName, $query_params);
2080
+			} else {
2081
+				//did we already cache the result of this query?
2082
+				$cached_results = $this->get_all_from_cache($relationName);
2083
+				if ( ! $cached_results) {
2084
+					$related_model_objects = $this->get_model()->get_all_related($this, $relationName, $query_params);
2085
+					//if no query parameters were passed, then we got all the related model objects
2086
+					//for that relation. We can cache them then.
2087
+					foreach ($related_model_objects as $related_model_object) {
2088
+						$this->cache($relationName, $related_model_object);
2089
+					}
2090
+				} else {
2091
+					$related_model_objects = $cached_results;
2092
+				}
2093
+			}
2094
+		} else {
2095
+			//this doesn't exist in the DB, so just get the related things from the cache
2096
+			$related_model_objects = $this->get_all_from_cache($relationName);
2097
+		}
2098
+		return $related_model_objects;
2099
+	}
2100
+
2101
+
2102
+
2103
+	/**
2104
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2105
+	 * unless otherwise specified in the $query_params
2106
+	 *
2107
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2108
+	 * @param array  $query_params   like EEM_Base::get_all's
2109
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2110
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2111
+	 *                               that by the setting $distinct to TRUE;
2112
+	 * @return int
2113
+	 */
2114
+	public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2115
+	{
2116
+		return $this->get_model()->count_related($this, $relation_name, $query_params, $field_to_count, $distinct);
2117
+	}
2118
+
2119
+
2120
+
2121
+	/**
2122
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2123
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2124
+	 *
2125
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2126
+	 * @param array  $query_params  like EEM_Base::get_all's
2127
+	 * @param string $field_to_sum  name of field to count by.
2128
+	 *                              By default, uses primary key (which doesn't make much sense, so you should probably
2129
+	 *                              change it)
2130
+	 * @return int
2131
+	 */
2132
+	public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2133
+	{
2134
+		return $this->get_model()->sum_related($this, $relation_name, $query_params, $field_to_sum);
2135
+	}
2136
+
2137
+
2138
+
2139
+	/**
2140
+	 * Gets the first (ie, one) related model object of the specified type.
2141
+	 *
2142
+	 * @param string $relationName key in the model's _model_relations array
2143
+	 * @param array  $query_params like EEM_Base::get_all
2144
+	 * @return EE_Base_Class (not an array, a single object)
2145
+	 * @throws \EE_Error
2146
+	 */
2147
+	public function get_first_related($relationName, $query_params = array())
2148
+	{
2149
+		if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2150
+			//if they've provided some query parameters, don't bother trying to cache the result
2151
+			//also make sure we're not caching the result of get_first_related
2152
+			//on a relation which should have an array of objects (because the cache might have an array of objects)
2153
+			if ($query_params
2154
+				|| ! $this->get_model()->related_settings_for($relationName)
2155
+					 instanceof
2156
+					 EE_Belongs_To_Relation
2157
+			) {
2158
+				$related_model_object = $this->get_model()->get_first_related($this, $relationName, $query_params);
2159
+			} else {
2160
+				//first, check if we've already cached the result of this query
2161
+				$cached_result = $this->get_one_from_cache($relationName);
2162
+				if ( ! $cached_result) {
2163
+					$related_model_object = $this->get_model()->get_first_related($this, $relationName, $query_params);
2164
+					$this->cache($relationName, $related_model_object);
2165
+				} else {
2166
+					$related_model_object = $cached_result;
2167
+				}
2168
+			}
2169
+		} else {
2170
+			$related_model_object = null;
2171
+			//this doesn't exist in the Db, but maybe the relation is of type belongs to, and so the related thing might
2172
+			if ($this->get_model()->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2173
+				$related_model_object = $this->get_model()->get_first_related($this, $relationName, $query_params);
2174
+			}
2175
+			//this doesn't exist in the DB and apparently the thing it belongs to doesn't either, just get what's cached on this object
2176
+			if ( ! $related_model_object) {
2177
+				$related_model_object = $this->get_one_from_cache($relationName);
2178
+			}
2179
+		}
2180
+		return $related_model_object;
2181
+	}
2182
+
2183
+
2184
+
2185
+	/**
2186
+	 * Does a delete on all related objects of type $relationName and removes
2187
+	 * the current model object's relation to them. If they can't be deleted (because
2188
+	 * of blocking related model objects) does nothing. If the related model objects are
2189
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2190
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2191
+	 *
2192
+	 * @param string $relationName
2193
+	 * @param array  $query_params like EEM_Base::get_all's
2194
+	 * @return int how many deleted
2195
+	 * @throws \EE_Error
2196
+	 */
2197
+	public function delete_related($relationName, $query_params = array())
2198
+	{
2199
+		if ($this->ID()) {
2200
+			$count = $this->get_model()->delete_related($this, $relationName, $query_params);
2201
+		} else {
2202
+			$count = count($this->get_all_from_cache($relationName));
2203
+			$this->clear_cache($relationName, null, true);
2204
+		}
2205
+		return $count;
2206
+	}
2207
+
2208
+
2209
+
2210
+	/**
2211
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2212
+	 * the current model object's relation to them. If they can't be deleted (because
2213
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2214
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2215
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2216
+	 *
2217
+	 * @param string $relationName
2218
+	 * @param array  $query_params like EEM_Base::get_all's
2219
+	 * @return int how many deleted (including those soft deleted)
2220
+	 * @throws \EE_Error
2221
+	 */
2222
+	public function delete_related_permanently($relationName, $query_params = array())
2223
+	{
2224
+		if ($this->ID()) {
2225
+			$count = $this->get_model()->delete_related_permanently($this, $relationName, $query_params);
2226
+		} else {
2227
+			$count = count($this->get_all_from_cache($relationName));
2228
+		}
2229
+		$this->clear_cache($relationName, null, true);
2230
+		return $count;
2231
+	}
2232
+
2233
+
2234
+
2235
+	/**
2236
+	 * is_set
2237
+	 * Just a simple utility function children can use for checking if property exists
2238
+	 *
2239
+	 * @access  public
2240
+	 * @param  string $field_name property to check
2241
+	 * @return bool                              TRUE if existing,FALSE if not.
2242
+	 */
2243
+	public function is_set($field_name)
2244
+	{
2245
+		return isset($this->_fields[$field_name]);
2246
+	}
2247
+
2248
+
2249
+
2250
+	/**
2251
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2252
+	 * EE_Error exception if they don't
2253
+	 *
2254
+	 * @param  mixed (string|array) $properties properties to check
2255
+	 * @throws EE_Error
2256
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
2257
+	 */
2258
+	protected function _property_exists($properties)
2259
+	{
2260
+		foreach ((array)$properties as $property_name) {
2261
+			//first make sure this property exists
2262
+			if ( ! $this->_fields[$property_name]) {
2263
+				throw new EE_Error(
2264
+					sprintf(
2265
+						__(
2266
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2267
+							'event_espresso'
2268
+						),
2269
+						$property_name
2270
+					)
2271
+				);
2272
+			}
2273
+		}
2274
+		return true;
2275
+	}
2276
+
2277
+
2278
+
2279
+	/**
2280
+	 * This simply returns an array of model fields for this object
2281
+	 *
2282
+	 * @return array
2283
+	 * @throws \EE_Error
2284
+	 */
2285
+	public function model_field_array()
2286
+	{
2287
+		$fields = $this->get_model()->field_settings(false);
2288
+		$properties = array();
2289
+		//remove prepended underscore
2290
+		foreach ($fields as $field_name => $settings) {
2291
+			$properties[$field_name] = $this->get($field_name);
2292
+		}
2293
+		return $properties;
2294
+	}
2295
+
2296
+
2297
+
2298
+	/**
2299
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2300
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2301
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments. Instead of
2302
+	 * requiring a plugin to extend the EE_Base_Class (which works fine is there's only 1 plugin, but when will that
2303
+	 * happen?) they can add a hook onto 'filters_hook_espresso__{className}__{methodName}' (eg,
2304
+	 * filters_hook_espresso__EE_Answer__my_great_function) and accepts 2 arguments: the object on which the function
2305
+	 * was called, and an array of the original arguments passed to the function. Whatever their callback function
2306
+	 * returns will be returned by this function. Example: in functions.php (or in a plugin):
2307
+	 * add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3); function
2308
+	 * my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2309
+	 * $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2310
+	 *        return $previousReturnValue.$returnString;
2311
+	 * }
2312
+	 * require('EE_Answer.class.php');
2313
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2314
+	 * echo $answer->my_callback('monkeys',100);
2315
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2316
+	 *
2317
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2318
+	 * @param array  $args       array of original arguments passed to the function
2319
+	 * @throws EE_Error
2320
+	 * @return mixed whatever the plugin which calls add_filter decides
2321
+	 */
2322
+	public function __call($methodName, $args)
2323
+	{
2324
+		$className = get_class($this);
2325
+		$tagName = "FHEE__{$className}__{$methodName}";
2326
+		if ( ! has_filter($tagName)) {
2327
+			throw new EE_Error(
2328
+				sprintf(
2329
+					__(
2330
+						"Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2331
+						"event_espresso"
2332
+					),
2333
+					$methodName,
2334
+					$className,
2335
+					$tagName
2336
+				)
2337
+			);
2338
+		}
2339
+		return apply_filters($tagName, null, $this, $args);
2340
+	}
2341
+
2342
+
2343
+
2344
+	/**
2345
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2346
+	 * A $previous_value can be specified in case there are many meta rows with the same key
2347
+	 *
2348
+	 * @param string $meta_key
2349
+	 * @param string $meta_value
2350
+	 * @param string $previous_value
2351
+	 * @return int records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2352
+	 * @throws \EE_Error
2353
+	 * NOTE: if the values haven't changed, returns 0
2354
+	 */
2355
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2356
+	{
2357
+		$query_params = array(
2358
+			array(
2359
+				'EXM_key'  => $meta_key,
2360
+				'OBJ_ID'   => $this->ID(),
2361
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2362
+			),
2363
+		);
2364
+		if ($previous_value !== null) {
2365
+			$query_params[0]['EXM_value'] = $meta_value;
2366
+		}
2367
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2368
+		if ( ! $existing_rows_like_that) {
2369
+			return $this->add_extra_meta($meta_key, $meta_value);
2370
+		} else {
2371
+			foreach ($existing_rows_like_that as $existing_row) {
2372
+				$existing_row->save(array('EXM_value' => $meta_value));
2373
+			}
2374
+			return count($existing_rows_like_that);
2375
+		}
2376
+	}
2377
+
2378
+
2379
+
2380
+	/**
2381
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2382
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
2383
+	 * extra meta row was entered, false if not
2384
+	 *
2385
+	 * @param string  $meta_key
2386
+	 * @param string  $meta_value
2387
+	 * @param boolean $unique
2388
+	 * @return boolean
2389
+	 * @throws \EE_Error
2390
+	 */
2391
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
2392
+	{
2393
+		if ($unique) {
2394
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2395
+				array(
2396
+					array(
2397
+						'EXM_key'  => $meta_key,
2398
+						'OBJ_ID'   => $this->ID(),
2399
+						'EXM_type' => $this->get_model()->get_this_model_name(),
2400
+					),
2401
+				)
2402
+			);
2403
+			if ($existing_extra_meta) {
2404
+				return false;
2405
+			}
2406
+		}
2407
+		$new_extra_meta = EE_Extra_Meta::new_instance(
2408
+			array(
2409
+				'EXM_key'   => $meta_key,
2410
+				'EXM_value' => $meta_value,
2411
+				'OBJ_ID'    => $this->ID(),
2412
+				'EXM_type'  => $this->get_model()->get_this_model_name(),
2413
+			)
2414
+		);
2415
+		$new_extra_meta->save();
2416
+		return true;
2417
+	}
2418
+
2419
+
2420
+
2421
+	/**
2422
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2423
+	 * is specified, only deletes extra meta records with that value.
2424
+	 *
2425
+	 * @param string $meta_key
2426
+	 * @param string $meta_value
2427
+	 * @return int number of extra meta rows deleted
2428
+	 * @throws \EE_Error
2429
+	 */
2430
+	public function delete_extra_meta($meta_key, $meta_value = null)
2431
+	{
2432
+		$query_params = array(
2433
+			array(
2434
+				'EXM_key'  => $meta_key,
2435
+				'OBJ_ID'   => $this->ID(),
2436
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2437
+			),
2438
+		);
2439
+		if ($meta_value !== null) {
2440
+			$query_params[0]['EXM_value'] = $meta_value;
2441
+		}
2442
+		return EEM_Extra_Meta::instance()->delete($query_params);
2443
+	}
2444
+
2445
+
2446
+
2447
+	/**
2448
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2449
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2450
+	 * You can specify $default is case you haven't found the extra meta
2451
+	 *
2452
+	 * @param string  $meta_key
2453
+	 * @param boolean $single
2454
+	 * @param mixed   $default if we don't find anything, what should we return?
2455
+	 * @return mixed single value if $single; array if ! $single
2456
+	 * @throws \EE_Error
2457
+	 */
2458
+	public function get_extra_meta($meta_key, $single = false, $default = null)
2459
+	{
2460
+		if ($single) {
2461
+			$result = $this->get_first_related('Extra_Meta', array(array('EXM_key' => $meta_key)));
2462
+			if ($result instanceof EE_Extra_Meta) {
2463
+				return $result->value();
2464
+			} else {
2465
+				return $default;
2466
+			}
2467
+		} else {
2468
+			$results = $this->get_many_related('Extra_Meta', array(array('EXM_key' => $meta_key)));
2469
+			if ($results) {
2470
+				$values = array();
2471
+				foreach ($results as $result) {
2472
+					if ($result instanceof EE_Extra_Meta) {
2473
+						$values[$result->ID()] = $result->value();
2474
+					}
2475
+				}
2476
+				return $values;
2477
+			} else {
2478
+				return $default;
2479
+			}
2480
+		}
2481
+	}
2482
+
2483
+
2484
+
2485
+	/**
2486
+	 * Returns a simple array of all the extra meta associated with this model object.
2487
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2488
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2489
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2490
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2491
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2492
+	 * finally the extra meta's value as each sub-value. (eg
2493
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2494
+	 *
2495
+	 * @param boolean $one_of_each_key
2496
+	 * @return array
2497
+	 * @throws \EE_Error
2498
+	 */
2499
+	public function all_extra_meta_array($one_of_each_key = true)
2500
+	{
2501
+		$return_array = array();
2502
+		if ($one_of_each_key) {
2503
+			$extra_meta_objs = $this->get_many_related('Extra_Meta', array('group_by' => 'EXM_key'));
2504
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2505
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2506
+					$return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2507
+				}
2508
+			}
2509
+		} else {
2510
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2511
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2512
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2513
+					if ( ! isset($return_array[$extra_meta_obj->key()])) {
2514
+						$return_array[$extra_meta_obj->key()] = array();
2515
+					}
2516
+					$return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2517
+				}
2518
+			}
2519
+		}
2520
+		return $return_array;
2521
+	}
2522
+
2523
+
2524
+
2525
+	/**
2526
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
2527
+	 *
2528
+	 * @return string
2529
+	 * @throws \EE_Error
2530
+	 */
2531
+	public function name()
2532
+	{
2533
+		//find a field that's not a text field
2534
+		$field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2535
+		if ($field_we_can_use) {
2536
+			return $this->get($field_we_can_use->get_name());
2537
+		} else {
2538
+			$first_few_properties = $this->model_field_array();
2539
+			$first_few_properties = array_slice($first_few_properties, 0, 3);
2540
+			$name_parts = array();
2541
+			foreach ($first_few_properties as $name => $value) {
2542
+				$name_parts[] = "$name:$value";
2543
+			}
2544
+			return implode(",", $name_parts);
2545
+		}
2546
+	}
2547
+
2548
+
2549
+
2550
+	/**
2551
+	 * in_entity_map
2552
+	 * Checks if this model object has been proven to already be in the entity map
2553
+	 *
2554
+	 * @return boolean
2555
+	 * @throws \EE_Error
2556
+	 */
2557
+	public function in_entity_map()
2558
+	{
2559
+		if ($this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this) {
2560
+			//well, if we looked, did we find it in the entity map?
2561
+			return true;
2562
+		} else {
2563
+			return false;
2564
+		}
2565
+	}
2566
+
2567
+
2568
+
2569
+	/**
2570
+	 * refresh_from_db
2571
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
2572
+	 *
2573
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
2574
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
2575
+	 */
2576
+	public function refresh_from_db()
2577
+	{
2578
+		if ($this->ID() && $this->in_entity_map()) {
2579
+			$this->get_model()->refresh_entity_map_from_db($this->ID());
2580
+		} else {
2581
+			//if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
2582
+			//if it has an ID but it's not in the map, and you're asking me to refresh it
2583
+			//that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
2584
+			//absolutely nothing in it for this ID
2585
+			if (WP_DEBUG) {
2586
+				throw new EE_Error(
2587
+					sprintf(
2588
+						__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
2589
+							'event_espresso'),
2590
+						$this->ID(),
2591
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2592
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2593
+					)
2594
+				);
2595
+			}
2596
+		}
2597
+	}
2598
+
2599
+
2600
+
2601
+	/**
2602
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
2603
+	 * (probably a bad assumption they have made, oh well)
2604
+	 *
2605
+	 * @return string
2606
+	 */
2607
+	public function __toString()
2608
+	{
2609
+		try {
2610
+			return sprintf('%s (%s)', $this->name(), $this->ID());
2611
+		} catch (Exception $e) {
2612
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
2613
+			return '';
2614
+		}
2615
+	}
2616
+
2617
+
2618
+
2619
+	/**
2620
+	 * Clear related model objects if they're already in the DB, because otherwise when we
2621
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
2622
+	 * This means if we have made changes to those related model objects, and want to unserialize
2623
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
2624
+	 * Instead, those related model objects should be directly serialized and stored.
2625
+	 * Eg, the following won't work:
2626
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
2627
+	 * $att = $reg->attendee();
2628
+	 * $att->set( 'ATT_fname', 'Dirk' );
2629
+	 * update_option( 'my_option', serialize( $reg ) );
2630
+	 * //END REQUEST
2631
+	 * //START NEXT REQUEST
2632
+	 * $reg = get_option( 'my_option' );
2633
+	 * $reg->attendee()->save();
2634
+	 * And would need to be replace with:
2635
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
2636
+	 * $att = $reg->attendee();
2637
+	 * $att->set( 'ATT_fname', 'Dirk' );
2638
+	 * update_option( 'my_option', serialize( $reg ) );
2639
+	 * //END REQUEST
2640
+	 * //START NEXT REQUEST
2641
+	 * $att = get_option( 'my_option' );
2642
+	 * $att->save();
2643
+	 *
2644
+	 * @return array
2645
+	 * @throws \EE_Error
2646
+	 */
2647
+	public function __sleep()
2648
+	{
2649
+		foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
2650
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
2651
+				$classname = 'EE_' . $this->get_model()->get_this_model_name();
2652
+				if (
2653
+					$this->get_one_from_cache($relation_name) instanceof $classname
2654
+					&& $this->get_one_from_cache($relation_name)->ID()
2655
+				) {
2656
+					$this->clear_cache($relation_name, $this->get_one_from_cache($relation_name)->ID());
2657
+				}
2658
+			}
2659
+		}
2660
+		$this->_props_n_values_provided_in_constructor = array();
2661
+		return array_keys(get_object_vars($this));
2662
+	}
2663
+
2664
+
2665
+
2666
+	/**
2667
+	 * restore _props_n_values_provided_in_constructor
2668
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
2669
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
2670
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
2671
+	 */
2672
+	public function __wakeup()
2673
+	{
2674
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
2675
+	}
2676 2676
 
2677 2677
 
2678 2678
 
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -145,8 +145,8 @@  discard block
 block discarded – undo
145 145
             list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
146 146
         } else {
147 147
             //set default formats for date and time
148
-            $this->_dt_frmt = (string)get_option('date_format', 'Y-m-d');
149
-            $this->_tm_frmt = (string)get_option('time_format', 'g:i a');
148
+            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
149
+            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
150 150
         }
151 151
         //if db model is instantiating
152 152
         if ($bydb) {
@@ -457,7 +457,7 @@  discard block
 block discarded – undo
457 457
      */
458 458
     public function get_format($full = true)
459 459
     {
460
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
460
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
461 461
     }
462 462
 
463 463
 
@@ -565,7 +565,7 @@  discard block
 block discarded – undo
565 565
         //verify the field exists
566 566
         $this->get_model()->field_settings_for($fieldname);
567 567
         $cache_type = $pretty ? 'pretty' : 'standard';
568
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
568
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
569 569
         if (isset($this->_cached_properties[$fieldname][$cache_type])) {
570 570
             return $this->_cached_properties[$fieldname][$cache_type];
571 571
         }
@@ -753,7 +753,7 @@  discard block
 block discarded – undo
753 753
         $current_cache_id = ''
754 754
     ) {
755 755
         // verify that incoming object is of the correct type
756
-        $obj_class = 'EE_' . $relationName;
756
+        $obj_class = 'EE_'.$relationName;
757 757
         if ($newly_saved_object instanceof $obj_class) {
758 758
             /* @type EE_Base_Class $newly_saved_object */
759 759
             // now get the type of relation
@@ -1261,7 +1261,7 @@  discard block
 block discarded – undo
1261 1261
      */
1262 1262
     public function get_i18n_datetime($field_name, $format = null)
1263 1263
     {
1264
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1264
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1265 1265
         return date_i18n(
1266 1266
             $format,
1267 1267
             EEH_DTT_Helper::get_timestamp_with_offset($this->get_raw($field_name), $this->_timezone)
@@ -1399,8 +1399,8 @@  discard block
 block discarded – undo
1399 1399
         }
1400 1400
         $original_timezone = $this->_timezone;
1401 1401
         $this->set_timezone($timezone);
1402
-        $fn = (array)$field_name;
1403
-        $args = array_merge($fn, (array)$args);
1402
+        $fn = (array) $field_name;
1403
+        $args = array_merge($fn, (array) $args);
1404 1404
         if ( ! method_exists($this, $callback)) {
1405 1405
             throw new EE_Error(
1406 1406
                 sprintf(
@@ -1412,8 +1412,8 @@  discard block
 block discarded – undo
1412 1412
                 )
1413 1413
             );
1414 1414
         }
1415
-        $args = (array)$args;
1416
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1415
+        $args = (array) $args;
1416
+        $return = $prepend.call_user_func_array(array($this, $callback), $args).$append;
1417 1417
         $this->set_timezone($original_timezone);
1418 1418
         return $return;
1419 1419
     }
@@ -1550,7 +1550,7 @@  discard block
 block discarded – undo
1550 1550
          * @param array         $set_cols_n_values
1551 1551
          * @param EE_Base_Class $model_object
1552 1552
          */
1553
-        $set_cols_n_values = (array)apply_filters('FHEE__EE_Base_Class__save__set_cols_n_values', $set_cols_n_values,
1553
+        $set_cols_n_values = (array) apply_filters('FHEE__EE_Base_Class__save__set_cols_n_values', $set_cols_n_values,
1554 1554
             $this);
1555 1555
         //set attributes as provided in $set_cols_n_values
1556 1556
         foreach ($set_cols_n_values as $column => $value) {
@@ -1605,8 +1605,8 @@  discard block
 block discarded – undo
1605 1605
                                 __('Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1606 1606
                                     'event_espresso'),
1607 1607
                                 get_class($this),
1608
-                                get_class($this->get_model()) . '::instance()->add_to_entity_map()',
1609
-                                get_class($this->get_model()) . '::instance()->get_one_by_ID()',
1608
+                                get_class($this->get_model()).'::instance()->add_to_entity_map()',
1609
+                                get_class($this->get_model()).'::instance()->get_one_by_ID()',
1610 1610
                                 '<br />'
1611 1611
                             )
1612 1612
                         );
@@ -1873,7 +1873,7 @@  discard block
 block discarded – undo
1873 1873
         if (strpos($model_name, "EE_") === 0) {
1874 1874
             $model_classname = str_replace("EE_", "EEM_", $model_name);
1875 1875
         } else {
1876
-            $model_classname = "EEM_" . $model_name;
1876
+            $model_classname = "EEM_".$model_name;
1877 1877
         }
1878 1878
         return $model_classname;
1879 1879
     }
@@ -2257,7 +2257,7 @@  discard block
 block discarded – undo
2257 2257
      */
2258 2258
     protected function _property_exists($properties)
2259 2259
     {
2260
-        foreach ((array)$properties as $property_name) {
2260
+        foreach ((array) $properties as $property_name) {
2261 2261
             //first make sure this property exists
2262 2262
             if ( ! $this->_fields[$property_name]) {
2263 2263
                 throw new EE_Error(
@@ -2588,8 +2588,8 @@  discard block
 block discarded – undo
2588 2588
                         __('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
2589 2589
                             'event_espresso'),
2590 2590
                         $this->ID(),
2591
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2592
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2591
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
2592
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
2593 2593
                     )
2594 2594
                 );
2595 2595
             }
@@ -2648,7 +2648,7 @@  discard block
 block discarded – undo
2648 2648
     {
2649 2649
         foreach ($this->get_model()->relation_settings() as $relation_name => $relation_obj) {
2650 2650
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
2651
-                $classname = 'EE_' . $this->get_model()->get_this_model_name();
2651
+                $classname = 'EE_'.$this->get_model()->get_this_model_name();
2652 2652
                 if (
2653 2653
                     $this->get_one_from_cache($relation_name) instanceof $classname
2654 2654
                     && $this->get_one_from_cache($relation_name)->ID()
Please login to merge, or discard this patch.