Completed
Branch FET-3467-waitlists (f9ed0c)
by
unknown
90:26 queued 78:09
created
core/db_classes/EE_Event.class.php 1 patch
Indentation   +1394 added lines, -1394 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,1482 +14,1482 @@  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
-     * @throws \EE_Error
42
-     */
43
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
44
-    {
45
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
47
-    }
48
-
49
-
50
-
51
-    /**
52
-     * @param array  $props_n_values  incoming values from the database
53
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
54
-     *                                the website will be used.
55
-     * @return EE_Event
56
-     * @throws \EE_Error
57
-     */
58
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
59
-    {
60
-        return new self($props_n_values, true, $timezone);
61
-    }
62
-
63
-
64
-
65
-    /**
66
-     * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
67
-     *
68
-     * @param string $field_name
69
-     * @param mixed  $field_value
70
-     * @param bool   $use_default
71
-     * @throws \EE_Error
72
-     */
73
-    public function set($field_name, $field_value, $use_default = false)
74
-    {
75
-        switch ($field_name) {
76
-            case 'status' :
77
-                $this->set_status($field_value, $use_default);
78
-                break;
79
-            default :
80
-                parent::set($field_name, $field_value, $use_default);
81
-        }
82
-    }
83
-
84
-
85
-
86
-    /**
87
-     *    set_status
88
-     * Checks if event status is being changed to SOLD OUT
89
-     * and updates event meta data with previous event status
90
-     * so that we can revert things if/when the event is no longer sold out
91
-     *
92
-     * @access public
93
-     * @param string $new_status
94
-     * @param bool   $use_default
95
-     * @return void
96
-     * @throws \EE_Error
97
-     */
98
-    public function set_status($new_status = null, $use_default = false)
99
-    {
100
-        // if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
101
-        if (empty($new_status) && ! $use_default) {
102
-            return;
103
-        }
104
-        // get current Event status
105
-        $old_status = $this->status();
106
-        // if status has changed
107
-        if ($old_status !== $new_status) {
108
-            // TO sold_out
109
-            if ($new_status === EEM_Event::sold_out) {
110
-                // save the previous event status so that we can revert if the event is no longer sold out
111
-                $this->add_post_meta('_previous_event_status', $old_status);
112
-                do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
113
-                // OR FROM  sold_out
114
-            } else if ($old_status === EEM_Event::sold_out) {
115
-                $this->delete_post_meta('_previous_event_status');
116
-                do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
117
-            }
118
-            // update status
119
-            parent::set('status', $new_status, $use_default);
120
-            do_action('AHEE__EE_Event__set_status__after_update', $this);
121
-            return;
122
-        } else {
123
-            // even though the old value matches the new value, it's still good to
124
-            // allow the parent set method to have a say
125
-            parent::set('status', $new_status, $use_default);
126
-            return;
127
-        }
128
-    }
129
-
130
-
131
-
132
-    /**
133
-     * Gets all the datetimes for this event
134
-     *
135
-     * @param array $query_params like EEM_Base::get_all
136
-     * @return EE_Datetime[]
137
-     * @throws \EE_Error
138
-     */
139
-    public function datetimes($query_params = array())
140
-    {
141
-        return $this->get_many_related('Datetime', $query_params);
142
-    }
143
-
144
-
145
-
146
-    /**
147
-     * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
148
-     *
149
-     * @return EE_Datetime[]
150
-     * @throws \EE_Error
151
-     */
152
-    public function datetimes_in_chronological_order()
153
-    {
154
-        return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
155
-    }
156
-
157
-
158
-
159
-    /**
160
-     * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
161
-     * @darren, we should probably UNSET timezone on the EEM_Datetime model
162
-     * after running our query, so that this timezone isn't set for EVERY query
163
-     * on EEM_Datetime for the rest of the request, no?
164
-     *
165
-     * @param boolean $show_expired whether or not to include expired events
166
-     * @param boolean $show_deleted whether or not to include deleted events
167
-     * @param null    $limit
168
-     * @return \EE_Datetime[]
169
-     * @throws \EE_Error
170
-     */
171
-    public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
172
-    {
173
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
174
-            $this->ID(),
175
-            $show_expired,
176
-            $show_deleted,
177
-            $limit
178
-        );
179
-    }
180
-
181
-
182
-
183
-    /**
184
-     * Returns one related datetime. Mostly only used by some legacy code.
185
-     *
186
-     * @return EE_Datetime
187
-     * @throws \EE_Error
188
-     */
189
-    public function first_datetime()
190
-    {
191
-        return $this->get_first_related('Datetime');
192
-    }
193
-
194
-
195
-
196
-    /**
197
-     * Returns the 'primary' datetime for the event
198
-     *
199
-     * @param bool $try_to_exclude_expired
200
-     * @param bool $try_to_exclude_deleted
201
-     * @return EE_Datetime
202
-     * @throws \EE_Error
203
-     */
204
-    public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
205
-    {
206
-        if ( ! empty ($this->_Primary_Datetime)) {
207
-            return $this->_Primary_Datetime;
208
-        }
209
-        $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
210
-            $this->ID(),
211
-            $try_to_exclude_expired,
212
-            $try_to_exclude_deleted
213
-        );
214
-        return $this->_Primary_Datetime;
215
-    }
216
-
217
-
218
-
219
-    /**
220
-     * Gets all the tickets available for purchase of this event
221
-     *
222
-     * @param array $query_params like EEM_Base::get_all
223
-     * @return EE_Ticket[]
224
-     * @throws \EE_Error
225
-     */
226
-    public function tickets($query_params = array())
227
-    {
228
-        //first get all datetimes
229
-        $datetimes = $this->datetimes_ordered();
230
-        if ( ! $datetimes) {
231
-            return array();
232
-        }
233
-        $datetime_ids = array();
234
-        foreach ($datetimes as $datetime) {
235
-            $datetime_ids[] = $datetime->ID();
236
-        }
237
-        $where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
238
-        //if incoming $query_params has where conditions let's merge but not override existing.
239
-        if (is_array($query_params) && isset($query_params[0])) {
240
-            $where_params = array_merge($query_params[0], $where_params);
241
-            unset($query_params[0]);
242
-        }
243
-        //now add $where_params to $query_params
244
-        $query_params[0] = $where_params;
245
-        return EEM_Ticket::instance()->get_all($query_params);
246
-    }
247
-
248
-
249
-
250
-    /**
251
-     * get all unexpired untrashed tickets
252
-     *
253
-     * @return bool|EE_Ticket[]
254
-     * @throws EE_Error
255
-     */
256
-    public function active_tickets()
257
-    {
258
-        return $this->tickets(array(
259
-            array(
260
-                'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
261
-                'TKT_deleted'  => false,
262
-            ),
263
-        ));
264
-    }
265
-
266
-
267
-
268
-    /**
269
-     * @return bool
270
-     * @throws \EE_Error
271
-     */
272
-    public function additional_limit()
273
-    {
274
-        return $this->get('EVT_additional_limit');
275
-    }
276
-
277
-
278
-
279
-    /**
280
-     * @return bool
281
-     * @throws \EE_Error
282
-     */
283
-    public function allow_overflow()
284
-    {
285
-        return $this->get('EVT_allow_overflow');
286
-    }
287
-
288
-
289
-
290
-    /**
291
-     * @return bool
292
-     * @throws \EE_Error
293
-     */
294
-    public function created()
295
-    {
296
-        return $this->get('EVT_created');
297
-    }
298
-
299
-
300
-
301
-    /**
302
-     * @return bool
303
-     * @throws \EE_Error
304
-     */
305
-    public function description()
306
-    {
307
-        return $this->get('EVT_desc');
308
-    }
309
-
310
-
311
-
312
-    /**
313
-     * Runs do_shortcode and wpautop on the description
314
-     *
315
-     * @return string of html
316
-     * @throws \EE_Error
317
-     */
318
-    public function description_filtered()
319
-    {
320
-        return $this->get_pretty('EVT_desc');
321
-    }
322
-
323
-
324
-
325
-    /**
326
-     * @return bool
327
-     * @throws \EE_Error
328
-     */
329
-    public function display_description()
330
-    {
331
-        return $this->get('EVT_display_desc');
332
-    }
333
-
334
-
335
-
336
-    /**
337
-     * @return bool
338
-     * @throws \EE_Error
339
-     */
340
-    public function display_ticket_selector()
341
-    {
342
-        return (bool)$this->get('EVT_display_ticket_selector');
343
-    }
344
-
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
+	 * @throws \EE_Error
42
+	 */
43
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
44
+	{
45
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
47
+	}
48
+
49
+
50
+
51
+	/**
52
+	 * @param array  $props_n_values  incoming values from the database
53
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
54
+	 *                                the website will be used.
55
+	 * @return EE_Event
56
+	 * @throws \EE_Error
57
+	 */
58
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
59
+	{
60
+		return new self($props_n_values, true, $timezone);
61
+	}
62
+
63
+
64
+
65
+	/**
66
+	 * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
67
+	 *
68
+	 * @param string $field_name
69
+	 * @param mixed  $field_value
70
+	 * @param bool   $use_default
71
+	 * @throws \EE_Error
72
+	 */
73
+	public function set($field_name, $field_value, $use_default = false)
74
+	{
75
+		switch ($field_name) {
76
+			case 'status' :
77
+				$this->set_status($field_value, $use_default);
78
+				break;
79
+			default :
80
+				parent::set($field_name, $field_value, $use_default);
81
+		}
82
+	}
83
+
84
+
85
+
86
+	/**
87
+	 *    set_status
88
+	 * Checks if event status is being changed to SOLD OUT
89
+	 * and updates event meta data with previous event status
90
+	 * so that we can revert things if/when the event is no longer sold out
91
+	 *
92
+	 * @access public
93
+	 * @param string $new_status
94
+	 * @param bool   $use_default
95
+	 * @return void
96
+	 * @throws \EE_Error
97
+	 */
98
+	public function set_status($new_status = null, $use_default = false)
99
+	{
100
+		// if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
101
+		if (empty($new_status) && ! $use_default) {
102
+			return;
103
+		}
104
+		// get current Event status
105
+		$old_status = $this->status();
106
+		// if status has changed
107
+		if ($old_status !== $new_status) {
108
+			// TO sold_out
109
+			if ($new_status === EEM_Event::sold_out) {
110
+				// save the previous event status so that we can revert if the event is no longer sold out
111
+				$this->add_post_meta('_previous_event_status', $old_status);
112
+				do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
113
+				// OR FROM  sold_out
114
+			} else if ($old_status === EEM_Event::sold_out) {
115
+				$this->delete_post_meta('_previous_event_status');
116
+				do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
117
+			}
118
+			// update status
119
+			parent::set('status', $new_status, $use_default);
120
+			do_action('AHEE__EE_Event__set_status__after_update', $this);
121
+			return;
122
+		} else {
123
+			// even though the old value matches the new value, it's still good to
124
+			// allow the parent set method to have a say
125
+			parent::set('status', $new_status, $use_default);
126
+			return;
127
+		}
128
+	}
129
+
130
+
131
+
132
+	/**
133
+	 * Gets all the datetimes for this event
134
+	 *
135
+	 * @param array $query_params like EEM_Base::get_all
136
+	 * @return EE_Datetime[]
137
+	 * @throws \EE_Error
138
+	 */
139
+	public function datetimes($query_params = array())
140
+	{
141
+		return $this->get_many_related('Datetime', $query_params);
142
+	}
143
+
144
+
145
+
146
+	/**
147
+	 * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
148
+	 *
149
+	 * @return EE_Datetime[]
150
+	 * @throws \EE_Error
151
+	 */
152
+	public function datetimes_in_chronological_order()
153
+	{
154
+		return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
155
+	}
156
+
157
+
158
+
159
+	/**
160
+	 * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
161
+	 * @darren, we should probably UNSET timezone on the EEM_Datetime model
162
+	 * after running our query, so that this timezone isn't set for EVERY query
163
+	 * on EEM_Datetime for the rest of the request, no?
164
+	 *
165
+	 * @param boolean $show_expired whether or not to include expired events
166
+	 * @param boolean $show_deleted whether or not to include deleted events
167
+	 * @param null    $limit
168
+	 * @return \EE_Datetime[]
169
+	 * @throws \EE_Error
170
+	 */
171
+	public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
172
+	{
173
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
174
+			$this->ID(),
175
+			$show_expired,
176
+			$show_deleted,
177
+			$limit
178
+		);
179
+	}
180
+
181
+
182
+
183
+	/**
184
+	 * Returns one related datetime. Mostly only used by some legacy code.
185
+	 *
186
+	 * @return EE_Datetime
187
+	 * @throws \EE_Error
188
+	 */
189
+	public function first_datetime()
190
+	{
191
+		return $this->get_first_related('Datetime');
192
+	}
193
+
194
+
195
+
196
+	/**
197
+	 * Returns the 'primary' datetime for the event
198
+	 *
199
+	 * @param bool $try_to_exclude_expired
200
+	 * @param bool $try_to_exclude_deleted
201
+	 * @return EE_Datetime
202
+	 * @throws \EE_Error
203
+	 */
204
+	public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
205
+	{
206
+		if ( ! empty ($this->_Primary_Datetime)) {
207
+			return $this->_Primary_Datetime;
208
+		}
209
+		$this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
210
+			$this->ID(),
211
+			$try_to_exclude_expired,
212
+			$try_to_exclude_deleted
213
+		);
214
+		return $this->_Primary_Datetime;
215
+	}
216
+
217
+
218
+
219
+	/**
220
+	 * Gets all the tickets available for purchase of this event
221
+	 *
222
+	 * @param array $query_params like EEM_Base::get_all
223
+	 * @return EE_Ticket[]
224
+	 * @throws \EE_Error
225
+	 */
226
+	public function tickets($query_params = array())
227
+	{
228
+		//first get all datetimes
229
+		$datetimes = $this->datetimes_ordered();
230
+		if ( ! $datetimes) {
231
+			return array();
232
+		}
233
+		$datetime_ids = array();
234
+		foreach ($datetimes as $datetime) {
235
+			$datetime_ids[] = $datetime->ID();
236
+		}
237
+		$where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
238
+		//if incoming $query_params has where conditions let's merge but not override existing.
239
+		if (is_array($query_params) && isset($query_params[0])) {
240
+			$where_params = array_merge($query_params[0], $where_params);
241
+			unset($query_params[0]);
242
+		}
243
+		//now add $where_params to $query_params
244
+		$query_params[0] = $where_params;
245
+		return EEM_Ticket::instance()->get_all($query_params);
246
+	}
247
+
248
+
249
+
250
+	/**
251
+	 * get all unexpired untrashed tickets
252
+	 *
253
+	 * @return bool|EE_Ticket[]
254
+	 * @throws EE_Error
255
+	 */
256
+	public function active_tickets()
257
+	{
258
+		return $this->tickets(array(
259
+			array(
260
+				'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
261
+				'TKT_deleted'  => false,
262
+			),
263
+		));
264
+	}
265
+
266
+
267
+
268
+	/**
269
+	 * @return bool
270
+	 * @throws \EE_Error
271
+	 */
272
+	public function additional_limit()
273
+	{
274
+		return $this->get('EVT_additional_limit');
275
+	}
276
+
277
+
278
+
279
+	/**
280
+	 * @return bool
281
+	 * @throws \EE_Error
282
+	 */
283
+	public function allow_overflow()
284
+	{
285
+		return $this->get('EVT_allow_overflow');
286
+	}
287
+
288
+
289
+
290
+	/**
291
+	 * @return bool
292
+	 * @throws \EE_Error
293
+	 */
294
+	public function created()
295
+	{
296
+		return $this->get('EVT_created');
297
+	}
298
+
299
+
300
+
301
+	/**
302
+	 * @return bool
303
+	 * @throws \EE_Error
304
+	 */
305
+	public function description()
306
+	{
307
+		return $this->get('EVT_desc');
308
+	}
309
+
310
+
311
+
312
+	/**
313
+	 * Runs do_shortcode and wpautop on the description
314
+	 *
315
+	 * @return string of html
316
+	 * @throws \EE_Error
317
+	 */
318
+	public function description_filtered()
319
+	{
320
+		return $this->get_pretty('EVT_desc');
321
+	}
322
+
323
+
324
+
325
+	/**
326
+	 * @return bool
327
+	 * @throws \EE_Error
328
+	 */
329
+	public function display_description()
330
+	{
331
+		return $this->get('EVT_display_desc');
332
+	}
333
+
334
+
335
+
336
+	/**
337
+	 * @return bool
338
+	 * @throws \EE_Error
339
+	 */
340
+	public function display_ticket_selector()
341
+	{
342
+		return (bool)$this->get('EVT_display_ticket_selector');
343
+	}
344
+
345 345
 
346 346
 
347
-    /**
348
-     * @return bool
349
-     * @throws \EE_Error
350
-     */
351
-    public function external_url()
352
-    {
353
-        return $this->get('EVT_external_URL');
354
-    }
355
-
347
+	/**
348
+	 * @return bool
349
+	 * @throws \EE_Error
350
+	 */
351
+	public function external_url()
352
+	{
353
+		return $this->get('EVT_external_URL');
354
+	}
355
+
356 356
 
357 357
 
358
-    /**
359
-     * @return bool
360
-     * @throws \EE_Error
361
-     */
362
-    public function member_only()
363
-    {
364
-        return $this->get('EVT_member_only');
365
-    }
358
+	/**
359
+	 * @return bool
360
+	 * @throws \EE_Error
361
+	 */
362
+	public function member_only()
363
+	{
364
+		return $this->get('EVT_member_only');
365
+	}
366 366
 
367 367
 
368 368
 
369
-    /**
370
-     * @return bool
371
-     * @throws \EE_Error
372
-     */
373
-    public function phone()
374
-    {
375
-        return $this->get('EVT_phone');
376
-    }
369
+	/**
370
+	 * @return bool
371
+	 * @throws \EE_Error
372
+	 */
373
+	public function phone()
374
+	{
375
+		return $this->get('EVT_phone');
376
+	}
377 377
 
378 378
 
379 379
 
380
-    /**
381
-     * @return bool
382
-     * @throws \EE_Error
383
-     */
384
-    public function modified()
385
-    {
386
-        return $this->get('EVT_modified');
387
-    }
380
+	/**
381
+	 * @return bool
382
+	 * @throws \EE_Error
383
+	 */
384
+	public function modified()
385
+	{
386
+		return $this->get('EVT_modified');
387
+	}
388 388
 
389 389
 
390 390
 
391
-    /**
392
-     * @return bool
393
-     * @throws \EE_Error
394
-     */
395
-    public function name()
396
-    {
397
-        return $this->get('EVT_name');
398
-    }
391
+	/**
392
+	 * @return bool
393
+	 * @throws \EE_Error
394
+	 */
395
+	public function name()
396
+	{
397
+		return $this->get('EVT_name');
398
+	}
399 399
 
400 400
 
401 401
 
402
-    /**
403
-     * @return bool
404
-     * @throws \EE_Error
405
-     */
406
-    public function order()
407
-    {
408
-        return $this->get('EVT_order');
409
-    }
402
+	/**
403
+	 * @return bool
404
+	 * @throws \EE_Error
405
+	 */
406
+	public function order()
407
+	{
408
+		return $this->get('EVT_order');
409
+	}
410 410
 
411 411
 
412 412
 
413
-    /**
414
-     * @return bool|string
415
-     * @throws \EE_Error
416
-     */
417
-    public function default_registration_status()
418
-    {
419
-        $event_default_registration_status = $this->get('EVT_default_registration_status');
420
-        return ! empty($event_default_registration_status)
421
-            ? $event_default_registration_status
422
-            : EE_Registry::instance()->CFG->registration->default_STS_ID;
423
-    }
413
+	/**
414
+	 * @return bool|string
415
+	 * @throws \EE_Error
416
+	 */
417
+	public function default_registration_status()
418
+	{
419
+		$event_default_registration_status = $this->get('EVT_default_registration_status');
420
+		return ! empty($event_default_registration_status)
421
+			? $event_default_registration_status
422
+			: EE_Registry::instance()->CFG->registration->default_STS_ID;
423
+	}
424 424
 
425 425
 
426 426
 
427
-    /**
428
-     * @param int  $num_words
429
-     * @param null $more
430
-     * @param bool $not_full_desc
431
-     * @return bool|string
432
-     * @throws \EE_Error
433
-     */
434
-    public function short_description($num_words = 55, $more = null, $not_full_desc = false)
435
-    {
436
-        $short_desc = $this->get('EVT_short_desc');
437
-        if ( ! empty($short_desc) || $not_full_desc) {
438
-            return $short_desc;
439
-        } else {
440
-            $full_desc = $this->get('EVT_desc');
441
-            return wp_trim_words($full_desc, $num_words, $more);
442
-        }
443
-    }
427
+	/**
428
+	 * @param int  $num_words
429
+	 * @param null $more
430
+	 * @param bool $not_full_desc
431
+	 * @return bool|string
432
+	 * @throws \EE_Error
433
+	 */
434
+	public function short_description($num_words = 55, $more = null, $not_full_desc = false)
435
+	{
436
+		$short_desc = $this->get('EVT_short_desc');
437
+		if ( ! empty($short_desc) || $not_full_desc) {
438
+			return $short_desc;
439
+		} else {
440
+			$full_desc = $this->get('EVT_desc');
441
+			return wp_trim_words($full_desc, $num_words, $more);
442
+		}
443
+	}
444 444
 
445 445
 
446 446
 
447
-    /**
448
-     * @return bool
449
-     * @throws \EE_Error
450
-     */
451
-    public function slug()
452
-    {
453
-        return $this->get('EVT_slug');
454
-    }
447
+	/**
448
+	 * @return bool
449
+	 * @throws \EE_Error
450
+	 */
451
+	public function slug()
452
+	{
453
+		return $this->get('EVT_slug');
454
+	}
455 455
 
456 456
 
457 457
 
458
-    /**
459
-     * @return bool
460
-     * @throws \EE_Error
461
-     */
462
-    public function timezone_string()
463
-    {
464
-        return $this->get('EVT_timezone_string');
465
-    }
458
+	/**
459
+	 * @return bool
460
+	 * @throws \EE_Error
461
+	 */
462
+	public function timezone_string()
463
+	{
464
+		return $this->get('EVT_timezone_string');
465
+	}
466 466
 
467 467
 
468 468
 
469
-    /**
470
-     * @return bool
471
-     * @throws \EE_Error
472
-     */
473
-    public function visible_on()
474
-    {
475
-        return $this->get('EVT_visible_on');
476
-    }
469
+	/**
470
+	 * @return bool
471
+	 * @throws \EE_Error
472
+	 */
473
+	public function visible_on()
474
+	{
475
+		return $this->get('EVT_visible_on');
476
+	}
477 477
 
478 478
 
479 479
 
480
-    /**
481
-     * @return int
482
-     * @throws \EE_Error
483
-     */
484
-    public function wp_user()
485
-    {
486
-        return $this->get('EVT_wp_user');
487
-    }
480
+	/**
481
+	 * @return int
482
+	 * @throws \EE_Error
483
+	 */
484
+	public function wp_user()
485
+	{
486
+		return $this->get('EVT_wp_user');
487
+	}
488 488
 
489 489
 
490 490
 
491
-    /**
492
-     * @return bool
493
-     * @throws \EE_Error
494
-     */
495
-    public function donations()
496
-    {
497
-        return $this->get('EVT_donations');
498
-    }
491
+	/**
492
+	 * @return bool
493
+	 * @throws \EE_Error
494
+	 */
495
+	public function donations()
496
+	{
497
+		return $this->get('EVT_donations');
498
+	}
499 499
 
500 500
 
501 501
 
502
-    /**
503
-     * @param $limit
504
-     * @throws \EE_Error
505
-     */
506
-    public function set_additional_limit($limit)
507
-    {
508
-        $this->set('EVT_additional_limit', $limit);
509
-    }
502
+	/**
503
+	 * @param $limit
504
+	 * @throws \EE_Error
505
+	 */
506
+	public function set_additional_limit($limit)
507
+	{
508
+		$this->set('EVT_additional_limit', $limit);
509
+	}
510 510
 
511 511
 
512 512
 
513
-    /**
514
-     * @param $created
515
-     * @throws \EE_Error
516
-     */
517
-    public function set_created($created)
518
-    {
519
-        $this->set('EVT_created', $created);
520
-    }
513
+	/**
514
+	 * @param $created
515
+	 * @throws \EE_Error
516
+	 */
517
+	public function set_created($created)
518
+	{
519
+		$this->set('EVT_created', $created);
520
+	}
521 521
 
522 522
 
523 523
 
524
-    /**
525
-     * @param $desc
526
-     * @throws \EE_Error
527
-     */
528
-    public function set_description($desc)
529
-    {
530
-        $this->set('EVT_desc', $desc);
531
-    }
524
+	/**
525
+	 * @param $desc
526
+	 * @throws \EE_Error
527
+	 */
528
+	public function set_description($desc)
529
+	{
530
+		$this->set('EVT_desc', $desc);
531
+	}
532 532
 
533 533
 
534 534
 
535
-    /**
536
-     * @param $display_desc
537
-     * @throws \EE_Error
538
-     */
539
-    public function set_display_description($display_desc)
540
-    {
541
-        $this->set('EVT_display_desc', $display_desc);
542
-    }
535
+	/**
536
+	 * @param $display_desc
537
+	 * @throws \EE_Error
538
+	 */
539
+	public function set_display_description($display_desc)
540
+	{
541
+		$this->set('EVT_display_desc', $display_desc);
542
+	}
543 543
 
544 544
 
545 545
 
546
-    /**
547
-     * @param $display_ticket_selector
548
-     * @throws \EE_Error
549
-     */
550
-    public function set_display_ticket_selector($display_ticket_selector)
551
-    {
552
-        $this->set('EVT_display_ticket_selector', $display_ticket_selector);
553
-    }
546
+	/**
547
+	 * @param $display_ticket_selector
548
+	 * @throws \EE_Error
549
+	 */
550
+	public function set_display_ticket_selector($display_ticket_selector)
551
+	{
552
+		$this->set('EVT_display_ticket_selector', $display_ticket_selector);
553
+	}
554 554
 
555 555
 
556 556
 
557
-    /**
558
-     * @param $external_url
559
-     * @throws \EE_Error
560
-     */
561
-    public function set_external_url($external_url)
562
-    {
563
-        $this->set('EVT_external_URL', $external_url);
564
-    }
557
+	/**
558
+	 * @param $external_url
559
+	 * @throws \EE_Error
560
+	 */
561
+	public function set_external_url($external_url)
562
+	{
563
+		$this->set('EVT_external_URL', $external_url);
564
+	}
565 565
 
566 566
 
567 567
 
568
-    /**
569
-     * @param $member_only
570
-     * @throws \EE_Error
571
-     */
572
-    public function set_member_only($member_only)
573
-    {
574
-        $this->set('EVT_member_only', $member_only);
575
-    }
568
+	/**
569
+	 * @param $member_only
570
+	 * @throws \EE_Error
571
+	 */
572
+	public function set_member_only($member_only)
573
+	{
574
+		$this->set('EVT_member_only', $member_only);
575
+	}
576 576
 
577 577
 
578 578
 
579
-    /**
580
-     * @param $event_phone
581
-     * @throws \EE_Error
582
-     */
583
-    public function set_event_phone($event_phone)
584
-    {
585
-        $this->set('EVT_phone', $event_phone);
586
-    }
579
+	/**
580
+	 * @param $event_phone
581
+	 * @throws \EE_Error
582
+	 */
583
+	public function set_event_phone($event_phone)
584
+	{
585
+		$this->set('EVT_phone', $event_phone);
586
+	}
587 587
 
588 588
 
589 589
 
590
-    /**
591
-     * @param $modified
592
-     * @throws \EE_Error
593
-     */
594
-    public function set_modified($modified)
595
-    {
596
-        $this->set('EVT_modified', $modified);
597
-    }
590
+	/**
591
+	 * @param $modified
592
+	 * @throws \EE_Error
593
+	 */
594
+	public function set_modified($modified)
595
+	{
596
+		$this->set('EVT_modified', $modified);
597
+	}
598 598
 
599 599
 
600 600
 
601
-    /**
602
-     * @param $name
603
-     * @throws \EE_Error
604
-     */
605
-    public function set_name($name)
606
-    {
607
-        $this->set('EVT_name', $name);
608
-    }
601
+	/**
602
+	 * @param $name
603
+	 * @throws \EE_Error
604
+	 */
605
+	public function set_name($name)
606
+	{
607
+		$this->set('EVT_name', $name);
608
+	}
609 609
 
610 610
 
611 611
 
612
-    /**
613
-     * @param $order
614
-     * @throws \EE_Error
615
-     */
616
-    public function set_order($order)
617
-    {
618
-        $this->set('EVT_order', $order);
619
-    }
612
+	/**
613
+	 * @param $order
614
+	 * @throws \EE_Error
615
+	 */
616
+	public function set_order($order)
617
+	{
618
+		$this->set('EVT_order', $order);
619
+	}
620 620
 
621 621
 
622 622
 
623
-    /**
624
-     * @param $short_desc
625
-     * @throws \EE_Error
626
-     */
627
-    public function set_short_description($short_desc)
628
-    {
629
-        $this->set('EVT_short_desc', $short_desc);
630
-    }
623
+	/**
624
+	 * @param $short_desc
625
+	 * @throws \EE_Error
626
+	 */
627
+	public function set_short_description($short_desc)
628
+	{
629
+		$this->set('EVT_short_desc', $short_desc);
630
+	}
631 631
 
632 632
 
633 633
 
634
-    /**
635
-     * @param $slug
636
-     * @throws \EE_Error
637
-     */
638
-    public function set_slug($slug)
639
-    {
640
-        $this->set('EVT_slug', $slug);
641
-    }
634
+	/**
635
+	 * @param $slug
636
+	 * @throws \EE_Error
637
+	 */
638
+	public function set_slug($slug)
639
+	{
640
+		$this->set('EVT_slug', $slug);
641
+	}
642 642
 
643 643
 
644 644
 
645
-    /**
646
-     * @param $timezone_string
647
-     * @throws \EE_Error
648
-     */
649
-    public function set_timezone_string($timezone_string)
650
-    {
651
-        $this->set('EVT_timezone_string', $timezone_string);
652
-    }
645
+	/**
646
+	 * @param $timezone_string
647
+	 * @throws \EE_Error
648
+	 */
649
+	public function set_timezone_string($timezone_string)
650
+	{
651
+		$this->set('EVT_timezone_string', $timezone_string);
652
+	}
653 653
 
654 654
 
655 655
 
656
-    /**
657
-     * @param $visible_on
658
-     * @throws \EE_Error
659
-     */
660
-    public function set_visible_on($visible_on)
661
-    {
662
-        $this->set('EVT_visible_on', $visible_on);
663
-    }
664
-
656
+	/**
657
+	 * @param $visible_on
658
+	 * @throws \EE_Error
659
+	 */
660
+	public function set_visible_on($visible_on)
661
+	{
662
+		$this->set('EVT_visible_on', $visible_on);
663
+	}
664
+
665 665
 
666 666
 
667
-    /**
668
-     * @param $wp_user
669
-     * @throws \EE_Error
670
-     */
671
-    public function set_wp_user($wp_user)
672
-    {
673
-        $this->set('EVT_wp_user', $wp_user);
674
-    }
675
-
676
-
677
-
678
-    /**
679
-     * @param $default_registration_status
680
-     * @throws \EE_Error
681
-     */
682
-    public function set_default_registration_status($default_registration_status)
683
-    {
684
-        $this->set('EVT_default_registration_status', $default_registration_status);
685
-    }
686
-
687
-
688
-
689
-    /**
690
-     * @param $donations
691
-     * @throws \EE_Error
692
-     */
693
-    public function set_donations($donations)
694
-    {
695
-        $this->set('EVT_donations', $donations);
696
-    }
697
-
698
-
699
-
700
-    /**
701
-     * Adds a venue to this event
702
-     *
703
-     * @param EE_Venue /int $venue_id_or_obj
704
-     * @return EE_Venue
705
-     * @throws \EE_Error
706
-     */
707
-    public function add_venue($venue_id_or_obj)
708
-    {
709
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
710
-    }
711
-
712
-
713
-
714
-    /**
715
-     * Removes a venue from the event
716
-     *
717
-     * @param EE_Venue /int $venue_id_or_obj
718
-     * @return EE_Venue
719
-     * @throws \EE_Error
720
-     */
721
-    public function remove_venue($venue_id_or_obj)
722
-    {
723
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
724
-    }
725
-
726
-
727
-
728
-    /**
729
-     * Gets all the venues related ot the event. May provide additional $query_params if desired
730
-     *
731
-     * @param array $query_params like EEM_Base::get_all's $query_params
732
-     * @return EE_Venue[]
733
-     * @throws \EE_Error
734
-     */
735
-    public function venues($query_params = array())
736
-    {
737
-        return $this->get_many_related('Venue', $query_params);
738
-    }
739
-
740
-
741
-
742
-    /**
743
-     * check if event id is present and if event is published
744
-     *
745
-     * @access public
746
-     * @return boolean true yes, false no
747
-     * @throws \EE_Error
748
-     */
749
-    private function _has_ID_and_is_published()
750
-    {
751
-        // first check if event id is present and not NULL,
752
-        // then check if this event is published (or any of the equivalent "published" statuses)
753
-        return
754
-            $this->ID() && $this->ID() !== null
755
-            && (
756
-                $this->status() === 'publish'
757
-                || $this->status() === EEM_Event::sold_out
758
-                || $this->status() === EEM_Event::postponed
759
-                || $this->status() === EEM_Event::cancelled
760
-            )
761
-            ? true
762
-            : false;
763
-    }
764
-
765
-
766
-
767
-    /**
768
-     * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
769
-     *
770
-     * @access public
771
-     * @return boolean true yes, false no
772
-     * @throws \EE_Error
773
-     */
774
-    public function is_upcoming()
775
-    {
776
-        // check if event id is present and if this event is published
777
-        if ($this->is_inactive()) {
778
-            return false;
779
-        }
780
-        // set initial value
781
-        $upcoming = false;
782
-        //next let's get all datetimes and loop through them
783
-        $datetimes = $this->datetimes_in_chronological_order();
784
-        foreach ($datetimes as $datetime) {
785
-            if ($datetime instanceof EE_Datetime) {
786
-                //if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
787
-                if ($datetime->is_expired()) {
788
-                    continue;
789
-                }
790
-                //if this dtt is active then we return false.
791
-                if ($datetime->is_active()) {
792
-                    return false;
793
-                }
794
-                //otherwise let's check upcoming status
795
-                $upcoming = $datetime->is_upcoming();
796
-            }
797
-        }
798
-        return $upcoming;
799
-    }
800
-
801
-
802
-
803
-    /**
804
-     * @return bool
805
-     * @throws \EE_Error
806
-     */
807
-    public function is_active()
808
-    {
809
-        // check if event id is present and if this event is published
810
-        if ($this->is_inactive()) {
811
-            return false;
812
-        }
813
-        // set initial value
814
-        $active = false;
815
-        //next let's get all datetimes and loop through them
816
-        $datetimes = $this->datetimes_in_chronological_order();
817
-        foreach ($datetimes as $datetime) {
818
-            if ($datetime instanceof EE_Datetime) {
819
-                //if this dtt is expired then we continue cause one of the other datetimes might be active.
820
-                if ($datetime->is_expired()) {
821
-                    continue;
822
-                }
823
-                //if this dtt is upcoming then we return false.
824
-                if ($datetime->is_upcoming()) {
825
-                    return false;
826
-                }
827
-                //otherwise let's check active status
828
-                $active = $datetime->is_active();
829
-            }
830
-        }
831
-        return $active;
832
-    }
833
-
834
-
835
-
836
-    /**
837
-     * @return bool
838
-     * @throws \EE_Error
839
-     */
840
-    public function is_expired()
841
-    {
842
-        // check if event id is present and if this event is published
843
-        if ($this->is_inactive()) {
844
-            return false;
845
-        }
846
-        // set initial value
847
-        $expired = false;
848
-        //first let's get all datetimes and loop through them
849
-        $datetimes = $this->datetimes_in_chronological_order();
850
-        foreach ($datetimes as $datetime) {
851
-            if ($datetime instanceof EE_Datetime) {
852
-                //if this dtt is upcoming or active then we return false.
853
-                if ($datetime->is_upcoming() || $datetime->is_active()) {
854
-                    return false;
855
-                }
856
-                //otherwise let's check active status
857
-                $expired = $datetime->is_expired();
858
-            }
859
-        }
860
-        return $expired;
861
-    }
862
-
863
-
864
-
865
-    /**
866
-     * @return bool
867
-     * @throws \EE_Error
868
-     */
869
-    public function is_inactive()
870
-    {
871
-        // check if event id is present and if this event is published
872
-        if ($this->_has_ID_and_is_published()) {
873
-            return false;
874
-        }
875
-        return true;
876
-    }
877
-
878
-
879
-
880
-    /**
881
-     * calculate spaces remaining based on "saleable" tickets
882
-     *
883
-     * @param array $tickets
884
-     * @return int
885
-     * @throws EE_Error
886
-     */
887
-    public function spaces_remaining($tickets = array(), $filtered = true)
888
-    {
889
-        // get all unexpired untrashed tickets if nothing was passed
890
-        $tickets = ! empty($tickets) ? $tickets : $this->active_tickets();
891
-        // set initial value
892
-        $spaces_remaining = 0;
893
-        foreach ($tickets as $ticket) {
894
-            if ($ticket instanceof EE_Ticket) {
895
-                $spaces_remaining += $ticket->qty('saleable');
896
-            }
897
-        }
898
-        return $filtered
899
-            ? (int) apply_filters(
900
-                'FHEE_EE_Event__spaces_remaining',
901
-                $spaces_remaining,
902
-                $this,
903
-                $tickets
904
-            )
905
-            : $spaces_remaining;
906
-    }
907
-
908
-
909
-    /**
910
-     *    perform_sold_out_status_check
911
-     *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces available...
912
-     *    if NOT, then the event status will get toggled to 'sold_out'
913
-     *
914
-     * @access public
915
-     * @return bool    return the ACTUAL sold out state.
916
-     * @throws \EE_Error
917
-     */
918
-    public function perform_sold_out_status_check()
919
-    {
920
-        // get all unexpired untrashed tickets
921
-        $tickets = $this->active_tickets();
922
-        // if all the tickets are just expired, then don't update the event status to sold out
923
-        if (empty($tickets)) {
924
-            return true;
925
-        }
926
-        $spaces_remaining = $this->spaces_remaining($tickets);
927
-        if ($spaces_remaining < 1) {
928
-            $this->set_status(EEM_Event::sold_out);
929
-            $this->save();
930
-            $sold_out = true;
931
-        } else {
932
-            $sold_out = false;
933
-            // was event previously marked as sold out ?
934
-            if ($this->status() === EEM_Event::sold_out) {
935
-                // revert status to previous value, if it was set
936
-                $previous_event_status = $this->get_post_meta('_previous_event_status', true);
937
-                if ($previous_event_status) {
938
-                    $this->set_status($previous_event_status);
939
-                    $this->save();
940
-                }
941
-            }
942
-        }
943
-        do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
944
-        return $sold_out;
945
-    }
946
-
947
-
948
-
949
-    /**
950
-     * This returns the total remaining spaces for sale on this event.
951
-     * ############################
952
-     * VERY IMPORTANT FOR DEVELOPERS:
953
-     * While included here, this method is still being tested internally, so its signature and behaviour COULD change.
954
-     * While this comment block is in place, usage is at your own risk and know that it may change in future builds.
955
-     * ############################
956
-     *
957
-     * @uses EE_Event::total_available_spaces()
958
-     * @return float|int  (EE_INF is returned as float)
959
-     * @throws \EE_Error
960
-     */
961
-    public function spaces_remaining_for_sale()
962
-    {
963
-        //first get total available spaces including consideration for tickets that have already sold.
964
-        $spaces_available = $this->total_available_spaces(true);
965
-        //if total available = 0, then exit right away because that means everything is expired.
966
-        if ($spaces_available === 0) {
967
-            return 0;
968
-        }
969
-        //subtract total approved registrations from spaces available to get how many are remaining.
970
-        $spots_taken = EEM_Registration::instance()->event_reg_count_for_status($this);
971
-        $spaces_remaining = $spaces_available - $spots_taken;
972
-        return $spaces_remaining > 0 ? $spaces_remaining : 0;
973
-    }
974
-
975
-
976
-
977
-    /**
978
-     * This returns the total spaces available for an event while considering all the qtys on the tickets and the reg limits
979
-     * on the datetimes attached to this event.
980
-     * ############################
981
-     * VERY IMPORTANT FOR DEVELOPERS:
982
-     * While included here, this method is still being tested internally, so its signature and behaviour COULD change. While
983
-     * this comment block is in place, usage is at your own risk and know that it may change in future builds.
984
-     * ############################
985
-     * Note: by "spaces available" we are not returning how many spaces remain.  That is a calculation involving using the value
986
-     * from this method and subtracting the approved registrations for the event.
987
-     *
988
-     * @param   bool $current_total_available       Whether to consider any tickets that have already sold in our calculation.
989
-     *                                              If this is false, then we return the most tickets that could ever be sold
990
-     *                                              for this event with the datetime and tickets setup on the event under optimal
991
-     *                                              selling conditions.  Otherwise we return a live calculation of spaces available
992
-     *                                              based on tickets sold.  Depending on setup and stage of sales, this
993
-     *                                              may appear to equal remaining tickets.  However, the more tickets are
994
-     *                                              sold out, the more accurate the "live" total is.
995
-     * @return  int|float  (Note: if EE_INF is returned its considered a float by PHP)
996
-     * @throws \EE_Error
997
-     */
998
-    public function total_available_spaces($current_total_available = false)
999
-    {
1000
-        $spaces_available = 0;
1001
-        //first get all tickets on the event and include expired tickets
1002
-        $tickets = $this->tickets(array('default_where_conditions' => 'none'));
1003
-        $ticket_sums = array();
1004
-        $datetimes = array();
1005
-        $datetime_limits = array();
1006
-        //loop through tickets and normalize them
1007
-        foreach ($tickets as $ticket) {
1008
-            $datetimes = $ticket->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
1009
-            if (empty($datetimes)) {
1010
-                continue;
1011
-            }
1012
-            //first datetime should be the lowest datetime
1013
-            $least_datetime = reset($datetimes);
1014
-            //lets reset the ticket quantity to be the lower of either the lowest datetime reg limit or the ticket quantity
1015
-            //IF datetimes sold (and we're not doing current live total available, then use spaces remaining for datetime, not reg_limit.
1016
-            if ($current_total_available) {
1017
-                if ($ticket->is_remaining()) {
1018
-                    $remaining = $ticket->remaining();
1019
-                } else {
1020
-                    $spaces_available += $ticket->sold();
1021
-                    //and we don't cache this ticket to our list because its sold out.
1022
-                    continue;
1023
-                }
1024
-            } else {
1025
-                $remaining = min($ticket->qty(), $least_datetime->reg_limit());
1026
-            }
1027
-            //if $ticket_limit == infinity then let's drop out right away and just return that because any infinity amount trumps all other "available" amounts.
1028
-            if ($remaining === EE_INF) {
1029
-                return EE_INF;
1030
-            }
1031
-            //multiply normalized $tkt quantity by the number of datetimes on the ticket as the "sum"
1032
-            //also include the sum of all the datetime reg limits on the ticket for breaking ties.
1033
-            $ticket_sums[$ticket->ID()]['sum'] = $remaining * count($datetimes);
1034
-            $ticket_sums[$ticket->ID()]['datetime_sums'] = 0;
1035
-            foreach ($datetimes as $datetime) {
1036
-                if ($datetime->reg_limit() === EE_INF) {
1037
-                    $ticket_sums[$ticket->ID()]['datetime_sums'] = EE_INF;
1038
-                } else {
1039
-                    $ticket_sums[$ticket->ID()]['datetime_sums'] += $current_total_available
1040
-                        ? $datetime->spaces_remaining()
1041
-                        : $datetime->reg_limit();
1042
-                }
1043
-                $datetime_limits[$datetime->ID()] = $current_total_available
1044
-                    ? $datetime->spaces_remaining()
1045
-                    : $datetime->reg_limit();
1046
-            }
1047
-            $ticket_sums[$ticket->ID()]['ticket'] = $ticket;
1048
-        }
1049
-        //The order is sorted by lowest available first (which is calculated for each ticket by multiplying the normalized
1050
-        //ticket quantity by the number of datetimes on the ticket).  For tie-breakers, then the next sort is based on the
1051
-        //ticket with the greatest sum of all remaining datetime->spaces_remaining() ( or $datetime->reg_limit() if not
1052
-        //$current_total_available ) for the datetimes on the ticket.
1053
-        usort($ticket_sums, function ($a, $b) {
1054
-            if ($a['sum'] === $b['sum']) {
1055
-                if ($a['datetime_sums'] === $b['datetime_sums']) {
1056
-                    return 0;
1057
-                }
1058
-                return $a['datetime_sums'] < $b['datetime_sums'] ? 1 : -1;
1059
-            }
1060
-            return ($a['sum'] < $b['sum']) ? -1 : 1;
1061
-        });
1062
-        //now let's loop through the sorted tickets and simulate sellouts
1063
-        foreach ($ticket_sums as $ticket_info) {
1064
-            if ($ticket_info['ticket'] instanceof EE_Ticket) {
1065
-                $datetimes = $ticket_info['ticket']->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
1066
-                //need to sort these $datetimes by remaining (only if $current_total_available)
1067
-                //setup datetimes for simulation
1068
-                $ticket_datetimes_remaining = array();
1069
-                foreach ($datetimes as $datetime) {
1070
-                    $ticket_datetimes_remaining[$datetime->ID()]['rem'] = $datetime_limits[$datetime->ID()];
1071
-                    $ticket_datetimes_remaining[$datetime->ID()]['datetime'] = $datetime;
1072
-                }
1073
-                usort($ticket_datetimes_remaining, function ($a, $b) {
1074
-                    if ($a['rem'] === $b['rem']) {
1075
-                        return 0;
1076
-                    }
1077
-                    return ($a['rem'] < $b['rem']) ? -1 : 1;
1078
-                });
1079
-                //get the remaining on the first datetime (which should be the one with the least remaining) and that is
1080
-                //what we add to the spaces_available running total.  Then we need to decrease the remaining on our datetime tracker.
1081
-                $lowest_datetime = reset($ticket_datetimes_remaining);
1082
-                //need to get the lower of; what the remaining is on the lowest datetime, and the remaining on the ticket.
1083
-                // If this ends up being 0 (because of previous tickets in our simulation selling out), then it has already
1084
-                // been tracked on $spaces available and this ticket is now sold out for the simulation, so we can continue
1085
-                // to the next ticket.
1086
-                if ($current_total_available) {
1087
-                    $remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->remaining());
1088
-                } else {
1089
-                    $remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->qty());
1090
-                }
1091
-                //if $remaining is infinite that means that all datetimes on this ticket are infinite but we've made it here because all
1092
-                //tickets have a quantity.  So we don't have to track datetimes, we can just use ticket quantities for total
1093
-                //available.
1094
-                if ($remaining === EE_INF) {
1095
-                    $spaces_available += $ticket_info['ticket']->qty();
1096
-                    continue;
1097
-                }
1098
-                //if ticket has sold amounts then we also need to add that (but only if doing live counts)
1099
-                if ($current_total_available) {
1100
-                    $spaces_available += $ticket_info['ticket']->sold();
1101
-                }
1102
-                if ($remaining <= 0) {
1103
-                    continue;
1104
-                } else {
1105
-                    $spaces_available += $remaining;
1106
-                }
1107
-                //loop through the datetimes and sell them out!
1108
-                foreach ($ticket_datetimes_remaining as $datetime_info) {
1109
-                    if ($datetime_info['datetime'] instanceof EE_Datetime) {
1110
-                        $datetime_limits[$datetime_info['datetime']->ID()] += -$remaining;
1111
-                    }
1112
-                }
1113
-            }
1114
-        }
1115
-        return apply_filters(
1116
-            'FHEE_EE_Event__total_available_spaces__spaces_available',
1117
-            $spaces_available,
1118
-            $this,
1119
-            $datetimes,
1120
-            $tickets
1121
-        );
1122
-    }
1123
-
1124
-
1125
-
1126
-    /**
1127
-     * Checks if the event is set to sold out
1128
-     *
1129
-     * @param  bool $actual whether or not to perform calculations to not only figure the
1130
-     *                      actual status but also to flip the status if necessary to sold
1131
-     *                      out If false, we just check the existing status of the event
1132
-     * @return boolean
1133
-     * @throws \EE_Error
1134
-     */
1135
-    public function is_sold_out($actual = false)
1136
-    {
1137
-        if ( ! $actual) {
1138
-            return $this->status() === EEM_Event::sold_out;
1139
-        } else {
1140
-            return $this->perform_sold_out_status_check();
1141
-        }
1142
-    }
1143
-
1144
-
1145
-
1146
-    /**
1147
-     * Checks if the event is marked as postponed
1148
-     *
1149
-     * @return boolean
1150
-     */
1151
-    public function is_postponed()
1152
-    {
1153
-        return $this->status() === EEM_Event::postponed;
1154
-    }
1155
-
1156
-
1157
-
1158
-    /**
1159
-     * Checks if the event is marked as cancelled
1160
-     *
1161
-     * @return boolean
1162
-     */
1163
-    public function is_cancelled()
1164
-    {
1165
-        return $this->status() === EEM_Event::cancelled;
1166
-    }
1167
-
1168
-
1169
-
1170
-    /**
1171
-     * Get the logical active status in a hierarchical order for all the datetimes.  Note
1172
-     * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1173
-     * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1174
-     * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1175
-     * the event is considered expired.
1176
-     * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a status
1177
-     * set on the EVENT when it is not published and thus is done
1178
-     *
1179
-     * @param bool $reset
1180
-     * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1181
-     * @throws \EE_Error
1182
-     */
1183
-    public function get_active_status($reset = false)
1184
-    {
1185
-        // if the active status has already been set, then just use that value (unless we are resetting it)
1186
-        if ( ! empty($this->_active_status) && ! $reset) {
1187
-            return $this->_active_status;
1188
-        }
1189
-        //first check if event id is present on this object
1190
-        if ( ! $this->ID()) {
1191
-            return false;
1192
-        }
1193
-        $where_params_for_event = array(array('EVT_ID' => $this->ID()));
1194
-        //if event is published:
1195
-        if ($this->status() === 'publish') {
1196
-            //active?
1197
-            if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::active, $where_params_for_event) > 0) {
1198
-                $this->_active_status = EE_Datetime::active;
1199
-            } else {
1200
-                //upcoming?
1201
-                if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::upcoming, $where_params_for_event) > 0) {
1202
-                    $this->_active_status = EE_Datetime::upcoming;
1203
-                } else {
1204
-                    //expired?
1205
-                    if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::expired, $where_params_for_event) > 0) {
1206
-                        $this->_active_status = EE_Datetime::expired;
1207
-                    } else {
1208
-                        //it would be odd if things make it this far because it basically means there are no datetime's
1209
-                        //attached to the event.  So in this case it will just be considered inactive.
1210
-                        $this->_active_status = EE_Datetime::inactive;
1211
-                    }
1212
-                }
1213
-            }
1214
-        } else {
1215
-            //the event is not published, so let's just set it's active status according to its' post status
1216
-            switch ($this->status()) {
1217
-                case EEM_Event::sold_out :
1218
-                    $this->_active_status = EE_Datetime::sold_out;
1219
-                    break;
1220
-                case EEM_Event::cancelled :
1221
-                    $this->_active_status = EE_Datetime::cancelled;
1222
-                    break;
1223
-                case EEM_Event::postponed :
1224
-                    $this->_active_status = EE_Datetime::postponed;
1225
-                    break;
1226
-                default :
1227
-                    $this->_active_status = EE_Datetime::inactive;
1228
-            }
1229
-        }
1230
-        return $this->_active_status;
1231
-    }
1232
-
1233
-
1234
-
1235
-    /**
1236
-     *    pretty_active_status
1237
-     *
1238
-     * @access public
1239
-     * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1240
-     * @return mixed void|string
1241
-     * @throws \EE_Error
1242
-     */
1243
-    public function pretty_active_status($echo = true)
1244
-    {
1245
-        $active_status = $this->get_active_status();
1246
-        $status = '<span class="ee-status event-active-status-'
1247
-                  . $active_status
1248
-                  . '">'
1249
-                  . EEH_Template::pretty_status($active_status, false, 'sentence')
1250
-                  . '</span>';
1251
-        if ($echo) {
1252
-            echo $status;
1253
-            return '';
1254
-        }
1255
-        return $status;
1256
-    }
1257
-
1258
-
1259
-
1260
-    /**
1261
-     * @return bool|int
1262
-     * @throws \EE_Error
1263
-     */
1264
-    public function get_number_of_tickets_sold()
1265
-    {
1266
-        $tkt_sold = 0;
1267
-        if ( ! $this->ID()) {
1268
-            return 0;
1269
-        }
1270
-        $datetimes = $this->datetimes();
1271
-        foreach ($datetimes as $datetime) {
1272
-            if ($datetime instanceof EE_Datetime) {
1273
-                $tkt_sold += $datetime->sold();
1274
-            }
1275
-        }
1276
-        return $tkt_sold;
1277
-    }
1278
-
1279
-
1280
-
1281
-    /**
1282
-     * This just returns a count of all the registrations for this event
1283
-     *
1284
-     * @access  public
1285
-     * @return int
1286
-     * @throws \EE_Error
1287
-     */
1288
-    public function get_count_of_all_registrations()
1289
-    {
1290
-        return EEM_Event::instance()->count_related($this, 'Registration');
1291
-    }
1292
-
1293
-
1294
-
1295
-    /**
1296
-     * This returns the ticket with the earliest start time that is
1297
-     * available for this event (across all datetimes attached to the event)
1298
-     *
1299
-     * @return EE_Ticket
1300
-     * @throws \EE_Error
1301
-     */
1302
-    public function get_ticket_with_earliest_start_time()
1303
-    {
1304
-        $where['Datetime.EVT_ID'] = $this->ID();
1305
-        $query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1306
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1307
-    }
1308
-
1309
-
1310
-
1311
-    /**
1312
-     * This returns the ticket with the latest end time that is available
1313
-     * for this event (across all datetimes attached to the event)
1314
-     *
1315
-     * @return EE_Ticket
1316
-     * @throws \EE_Error
1317
-     */
1318
-    public function get_ticket_with_latest_end_time()
1319
-    {
1320
-        $where['Datetime.EVT_ID'] = $this->ID();
1321
-        $query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1322
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1323
-    }
1324
-
1325
-
1326
-
1327
-    /**
1328
-     * This returns whether there are any tickets on sale for this event.
1329
-     *
1330
-     * @return bool true = YES tickets on sale.
1331
-     * @throws \EE_Error
1332
-     */
1333
-    public function tickets_on_sale()
1334
-    {
1335
-        $earliest_ticket = $this->get_ticket_with_earliest_start_time();
1336
-        $latest_ticket = $this->get_ticket_with_latest_end_time();
1337
-        if ( ! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1338
-            return false;
1339
-        }
1340
-        //check on sale for these two tickets.
1341
-        if ($latest_ticket->is_on_sale() || $earliest_ticket->is_on_sale()) {
1342
-            return true;
1343
-        }
1344
-        return false;
1345
-    }
1346
-
1347
-
1348
-
1349
-    /**
1350
-     * Gets the URL for viewing this event on the front-end. Overrides parent
1351
-     * to check for an external URL first
1352
-     *
1353
-     * @return string
1354
-     * @throws \EE_Error
1355
-     */
1356
-    public function get_permalink()
1357
-    {
1358
-        if ($this->external_url()) {
1359
-            return $this->external_url();
1360
-        } else {
1361
-            return parent::get_permalink();
1362
-        }
1363
-    }
1364
-
1365
-
1366
-
1367
-    /**
1368
-     * Gets the first term for 'espresso_event_categories' we can find
1369
-     *
1370
-     * @param array $query_params like EEM_Base::get_all
1371
-     * @return EE_Term
1372
-     * @throws \EE_Error
1373
-     */
1374
-    public function first_event_category($query_params = array())
1375
-    {
1376
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1377
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1378
-        return EEM_Term::instance()->get_one($query_params);
1379
-    }
1380
-
1381
-
1382
-
1383
-    /**
1384
-     * Gets all terms for 'espresso_event_categories' we can find
1385
-     *
1386
-     * @param array $query_params
1387
-     * @return EE_Term[]
1388
-     * @throws \EE_Error
1389
-     */
1390
-    public function get_all_event_categories($query_params = array())
1391
-    {
1392
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1393
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1394
-        return EEM_Term::instance()->get_all($query_params);
1395
-    }
1396
-
1397
-
1398
-
1399
-    /**
1400
-     * Gets all the question groups, ordering them by QSG_order ascending
1401
-     *
1402
-     * @param array $query_params @see EEM_Base::get_all
1403
-     * @return EE_Question_Group[]
1404
-     * @throws \EE_Error
1405
-     */
1406
-    public function question_groups($query_params = array())
1407
-    {
1408
-        $query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1409
-        return $this->get_many_related('Question_Group', $query_params);
1410
-    }
1411
-
1412
-
1413
-
1414
-    /**
1415
-     * Implementation for EEI_Has_Icon interface method.
1416
-     *
1417
-     * @see EEI_Visual_Representation for comments
1418
-     * @return string
1419
-     */
1420
-    public function get_icon()
1421
-    {
1422
-        return '<span class="dashicons dashicons-flag"></span>';
1423
-    }
1424
-
1425
-
1426
-
1427
-    /**
1428
-     * Implementation for EEI_Admin_Links interface method.
1429
-     *
1430
-     * @see EEI_Admin_Links for comments
1431
-     * @return string
1432
-     * @throws \EE_Error
1433
-     */
1434
-    public function get_admin_details_link()
1435
-    {
1436
-        return $this->get_admin_edit_link();
1437
-    }
1438
-
1439
-
1440
-
1441
-    /**
1442
-     * Implementation for EEI_Admin_Links interface method.
1443
-     *
1444
-     * @see EEI_Admin_Links for comments
1445
-     * @return string
1446
-     * @throws \EE_Error
1447
-     */
1448
-    public function get_admin_edit_link()
1449
-    {
1450
-        return EEH_URL::add_query_args_and_nonce(array(
1451
-            'page'   => 'espresso_events',
1452
-            'action' => 'edit',
1453
-            'post'   => $this->ID(),
1454
-        ),
1455
-            admin_url('admin.php')
1456
-        );
1457
-    }
1458
-
1459
-
1460
-
1461
-    /**
1462
-     * Implementation for EEI_Admin_Links interface method.
1463
-     *
1464
-     * @see EEI_Admin_Links for comments
1465
-     * @return string
1466
-     */
1467
-    public function get_admin_settings_link()
1468
-    {
1469
-        return EEH_URL::add_query_args_and_nonce(array(
1470
-            'page'   => 'espresso_events',
1471
-            'action' => 'default_event_settings',
1472
-        ),
1473
-            admin_url('admin.php')
1474
-        );
1475
-    }
1476
-
1477
-
1478
-
1479
-    /**
1480
-     * Implementation for EEI_Admin_Links interface method.
1481
-     *
1482
-     * @see EEI_Admin_Links for comments
1483
-     * @return string
1484
-     */
1485
-    public function get_admin_overview_link()
1486
-    {
1487
-        return EEH_URL::add_query_args_and_nonce(array(
1488
-            'page'   => 'espresso_events',
1489
-            'action' => 'default',
1490
-        ),
1491
-            admin_url('admin.php')
1492
-        );
1493
-    }
667
+	/**
668
+	 * @param $wp_user
669
+	 * @throws \EE_Error
670
+	 */
671
+	public function set_wp_user($wp_user)
672
+	{
673
+		$this->set('EVT_wp_user', $wp_user);
674
+	}
675
+
676
+
677
+
678
+	/**
679
+	 * @param $default_registration_status
680
+	 * @throws \EE_Error
681
+	 */
682
+	public function set_default_registration_status($default_registration_status)
683
+	{
684
+		$this->set('EVT_default_registration_status', $default_registration_status);
685
+	}
686
+
687
+
688
+
689
+	/**
690
+	 * @param $donations
691
+	 * @throws \EE_Error
692
+	 */
693
+	public function set_donations($donations)
694
+	{
695
+		$this->set('EVT_donations', $donations);
696
+	}
697
+
698
+
699
+
700
+	/**
701
+	 * Adds a venue to this event
702
+	 *
703
+	 * @param EE_Venue /int $venue_id_or_obj
704
+	 * @return EE_Venue
705
+	 * @throws \EE_Error
706
+	 */
707
+	public function add_venue($venue_id_or_obj)
708
+	{
709
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
710
+	}
711
+
712
+
713
+
714
+	/**
715
+	 * Removes a venue from the event
716
+	 *
717
+	 * @param EE_Venue /int $venue_id_or_obj
718
+	 * @return EE_Venue
719
+	 * @throws \EE_Error
720
+	 */
721
+	public function remove_venue($venue_id_or_obj)
722
+	{
723
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
724
+	}
725
+
726
+
727
+
728
+	/**
729
+	 * Gets all the venues related ot the event. May provide additional $query_params if desired
730
+	 *
731
+	 * @param array $query_params like EEM_Base::get_all's $query_params
732
+	 * @return EE_Venue[]
733
+	 * @throws \EE_Error
734
+	 */
735
+	public function venues($query_params = array())
736
+	{
737
+		return $this->get_many_related('Venue', $query_params);
738
+	}
739
+
740
+
741
+
742
+	/**
743
+	 * check if event id is present and if event is published
744
+	 *
745
+	 * @access public
746
+	 * @return boolean true yes, false no
747
+	 * @throws \EE_Error
748
+	 */
749
+	private function _has_ID_and_is_published()
750
+	{
751
+		// first check if event id is present and not NULL,
752
+		// then check if this event is published (or any of the equivalent "published" statuses)
753
+		return
754
+			$this->ID() && $this->ID() !== null
755
+			&& (
756
+				$this->status() === 'publish'
757
+				|| $this->status() === EEM_Event::sold_out
758
+				|| $this->status() === EEM_Event::postponed
759
+				|| $this->status() === EEM_Event::cancelled
760
+			)
761
+			? true
762
+			: false;
763
+	}
764
+
765
+
766
+
767
+	/**
768
+	 * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
769
+	 *
770
+	 * @access public
771
+	 * @return boolean true yes, false no
772
+	 * @throws \EE_Error
773
+	 */
774
+	public function is_upcoming()
775
+	{
776
+		// check if event id is present and if this event is published
777
+		if ($this->is_inactive()) {
778
+			return false;
779
+		}
780
+		// set initial value
781
+		$upcoming = false;
782
+		//next let's get all datetimes and loop through them
783
+		$datetimes = $this->datetimes_in_chronological_order();
784
+		foreach ($datetimes as $datetime) {
785
+			if ($datetime instanceof EE_Datetime) {
786
+				//if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
787
+				if ($datetime->is_expired()) {
788
+					continue;
789
+				}
790
+				//if this dtt is active then we return false.
791
+				if ($datetime->is_active()) {
792
+					return false;
793
+				}
794
+				//otherwise let's check upcoming status
795
+				$upcoming = $datetime->is_upcoming();
796
+			}
797
+		}
798
+		return $upcoming;
799
+	}
800
+
801
+
802
+
803
+	/**
804
+	 * @return bool
805
+	 * @throws \EE_Error
806
+	 */
807
+	public function is_active()
808
+	{
809
+		// check if event id is present and if this event is published
810
+		if ($this->is_inactive()) {
811
+			return false;
812
+		}
813
+		// set initial value
814
+		$active = false;
815
+		//next let's get all datetimes and loop through them
816
+		$datetimes = $this->datetimes_in_chronological_order();
817
+		foreach ($datetimes as $datetime) {
818
+			if ($datetime instanceof EE_Datetime) {
819
+				//if this dtt is expired then we continue cause one of the other datetimes might be active.
820
+				if ($datetime->is_expired()) {
821
+					continue;
822
+				}
823
+				//if this dtt is upcoming then we return false.
824
+				if ($datetime->is_upcoming()) {
825
+					return false;
826
+				}
827
+				//otherwise let's check active status
828
+				$active = $datetime->is_active();
829
+			}
830
+		}
831
+		return $active;
832
+	}
833
+
834
+
835
+
836
+	/**
837
+	 * @return bool
838
+	 * @throws \EE_Error
839
+	 */
840
+	public function is_expired()
841
+	{
842
+		// check if event id is present and if this event is published
843
+		if ($this->is_inactive()) {
844
+			return false;
845
+		}
846
+		// set initial value
847
+		$expired = false;
848
+		//first let's get all datetimes and loop through them
849
+		$datetimes = $this->datetimes_in_chronological_order();
850
+		foreach ($datetimes as $datetime) {
851
+			if ($datetime instanceof EE_Datetime) {
852
+				//if this dtt is upcoming or active then we return false.
853
+				if ($datetime->is_upcoming() || $datetime->is_active()) {
854
+					return false;
855
+				}
856
+				//otherwise let's check active status
857
+				$expired = $datetime->is_expired();
858
+			}
859
+		}
860
+		return $expired;
861
+	}
862
+
863
+
864
+
865
+	/**
866
+	 * @return bool
867
+	 * @throws \EE_Error
868
+	 */
869
+	public function is_inactive()
870
+	{
871
+		// check if event id is present and if this event is published
872
+		if ($this->_has_ID_and_is_published()) {
873
+			return false;
874
+		}
875
+		return true;
876
+	}
877
+
878
+
879
+
880
+	/**
881
+	 * calculate spaces remaining based on "saleable" tickets
882
+	 *
883
+	 * @param array $tickets
884
+	 * @return int
885
+	 * @throws EE_Error
886
+	 */
887
+	public function spaces_remaining($tickets = array(), $filtered = true)
888
+	{
889
+		// get all unexpired untrashed tickets if nothing was passed
890
+		$tickets = ! empty($tickets) ? $tickets : $this->active_tickets();
891
+		// set initial value
892
+		$spaces_remaining = 0;
893
+		foreach ($tickets as $ticket) {
894
+			if ($ticket instanceof EE_Ticket) {
895
+				$spaces_remaining += $ticket->qty('saleable');
896
+			}
897
+		}
898
+		return $filtered
899
+			? (int) apply_filters(
900
+				'FHEE_EE_Event__spaces_remaining',
901
+				$spaces_remaining,
902
+				$this,
903
+				$tickets
904
+			)
905
+			: $spaces_remaining;
906
+	}
907
+
908
+
909
+	/**
910
+	 *    perform_sold_out_status_check
911
+	 *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces available...
912
+	 *    if NOT, then the event status will get toggled to 'sold_out'
913
+	 *
914
+	 * @access public
915
+	 * @return bool    return the ACTUAL sold out state.
916
+	 * @throws \EE_Error
917
+	 */
918
+	public function perform_sold_out_status_check()
919
+	{
920
+		// get all unexpired untrashed tickets
921
+		$tickets = $this->active_tickets();
922
+		// if all the tickets are just expired, then don't update the event status to sold out
923
+		if (empty($tickets)) {
924
+			return true;
925
+		}
926
+		$spaces_remaining = $this->spaces_remaining($tickets);
927
+		if ($spaces_remaining < 1) {
928
+			$this->set_status(EEM_Event::sold_out);
929
+			$this->save();
930
+			$sold_out = true;
931
+		} else {
932
+			$sold_out = false;
933
+			// was event previously marked as sold out ?
934
+			if ($this->status() === EEM_Event::sold_out) {
935
+				// revert status to previous value, if it was set
936
+				$previous_event_status = $this->get_post_meta('_previous_event_status', true);
937
+				if ($previous_event_status) {
938
+					$this->set_status($previous_event_status);
939
+					$this->save();
940
+				}
941
+			}
942
+		}
943
+		do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
944
+		return $sold_out;
945
+	}
946
+
947
+
948
+
949
+	/**
950
+	 * This returns the total remaining spaces for sale on this event.
951
+	 * ############################
952
+	 * VERY IMPORTANT FOR DEVELOPERS:
953
+	 * While included here, this method is still being tested internally, so its signature and behaviour COULD change.
954
+	 * While this comment block is in place, usage is at your own risk and know that it may change in future builds.
955
+	 * ############################
956
+	 *
957
+	 * @uses EE_Event::total_available_spaces()
958
+	 * @return float|int  (EE_INF is returned as float)
959
+	 * @throws \EE_Error
960
+	 */
961
+	public function spaces_remaining_for_sale()
962
+	{
963
+		//first get total available spaces including consideration for tickets that have already sold.
964
+		$spaces_available = $this->total_available_spaces(true);
965
+		//if total available = 0, then exit right away because that means everything is expired.
966
+		if ($spaces_available === 0) {
967
+			return 0;
968
+		}
969
+		//subtract total approved registrations from spaces available to get how many are remaining.
970
+		$spots_taken = EEM_Registration::instance()->event_reg_count_for_status($this);
971
+		$spaces_remaining = $spaces_available - $spots_taken;
972
+		return $spaces_remaining > 0 ? $spaces_remaining : 0;
973
+	}
974
+
975
+
976
+
977
+	/**
978
+	 * This returns the total spaces available for an event while considering all the qtys on the tickets and the reg limits
979
+	 * on the datetimes attached to this event.
980
+	 * ############################
981
+	 * VERY IMPORTANT FOR DEVELOPERS:
982
+	 * While included here, this method is still being tested internally, so its signature and behaviour COULD change. While
983
+	 * this comment block is in place, usage is at your own risk and know that it may change in future builds.
984
+	 * ############################
985
+	 * Note: by "spaces available" we are not returning how many spaces remain.  That is a calculation involving using the value
986
+	 * from this method and subtracting the approved registrations for the event.
987
+	 *
988
+	 * @param   bool $current_total_available       Whether to consider any tickets that have already sold in our calculation.
989
+	 *                                              If this is false, then we return the most tickets that could ever be sold
990
+	 *                                              for this event with the datetime and tickets setup on the event under optimal
991
+	 *                                              selling conditions.  Otherwise we return a live calculation of spaces available
992
+	 *                                              based on tickets sold.  Depending on setup and stage of sales, this
993
+	 *                                              may appear to equal remaining tickets.  However, the more tickets are
994
+	 *                                              sold out, the more accurate the "live" total is.
995
+	 * @return  int|float  (Note: if EE_INF is returned its considered a float by PHP)
996
+	 * @throws \EE_Error
997
+	 */
998
+	public function total_available_spaces($current_total_available = false)
999
+	{
1000
+		$spaces_available = 0;
1001
+		//first get all tickets on the event and include expired tickets
1002
+		$tickets = $this->tickets(array('default_where_conditions' => 'none'));
1003
+		$ticket_sums = array();
1004
+		$datetimes = array();
1005
+		$datetime_limits = array();
1006
+		//loop through tickets and normalize them
1007
+		foreach ($tickets as $ticket) {
1008
+			$datetimes = $ticket->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
1009
+			if (empty($datetimes)) {
1010
+				continue;
1011
+			}
1012
+			//first datetime should be the lowest datetime
1013
+			$least_datetime = reset($datetimes);
1014
+			//lets reset the ticket quantity to be the lower of either the lowest datetime reg limit or the ticket quantity
1015
+			//IF datetimes sold (and we're not doing current live total available, then use spaces remaining for datetime, not reg_limit.
1016
+			if ($current_total_available) {
1017
+				if ($ticket->is_remaining()) {
1018
+					$remaining = $ticket->remaining();
1019
+				} else {
1020
+					$spaces_available += $ticket->sold();
1021
+					//and we don't cache this ticket to our list because its sold out.
1022
+					continue;
1023
+				}
1024
+			} else {
1025
+				$remaining = min($ticket->qty(), $least_datetime->reg_limit());
1026
+			}
1027
+			//if $ticket_limit == infinity then let's drop out right away and just return that because any infinity amount trumps all other "available" amounts.
1028
+			if ($remaining === EE_INF) {
1029
+				return EE_INF;
1030
+			}
1031
+			//multiply normalized $tkt quantity by the number of datetimes on the ticket as the "sum"
1032
+			//also include the sum of all the datetime reg limits on the ticket for breaking ties.
1033
+			$ticket_sums[$ticket->ID()]['sum'] = $remaining * count($datetimes);
1034
+			$ticket_sums[$ticket->ID()]['datetime_sums'] = 0;
1035
+			foreach ($datetimes as $datetime) {
1036
+				if ($datetime->reg_limit() === EE_INF) {
1037
+					$ticket_sums[$ticket->ID()]['datetime_sums'] = EE_INF;
1038
+				} else {
1039
+					$ticket_sums[$ticket->ID()]['datetime_sums'] += $current_total_available
1040
+						? $datetime->spaces_remaining()
1041
+						: $datetime->reg_limit();
1042
+				}
1043
+				$datetime_limits[$datetime->ID()] = $current_total_available
1044
+					? $datetime->spaces_remaining()
1045
+					: $datetime->reg_limit();
1046
+			}
1047
+			$ticket_sums[$ticket->ID()]['ticket'] = $ticket;
1048
+		}
1049
+		//The order is sorted by lowest available first (which is calculated for each ticket by multiplying the normalized
1050
+		//ticket quantity by the number of datetimes on the ticket).  For tie-breakers, then the next sort is based on the
1051
+		//ticket with the greatest sum of all remaining datetime->spaces_remaining() ( or $datetime->reg_limit() if not
1052
+		//$current_total_available ) for the datetimes on the ticket.
1053
+		usort($ticket_sums, function ($a, $b) {
1054
+			if ($a['sum'] === $b['sum']) {
1055
+				if ($a['datetime_sums'] === $b['datetime_sums']) {
1056
+					return 0;
1057
+				}
1058
+				return $a['datetime_sums'] < $b['datetime_sums'] ? 1 : -1;
1059
+			}
1060
+			return ($a['sum'] < $b['sum']) ? -1 : 1;
1061
+		});
1062
+		//now let's loop through the sorted tickets and simulate sellouts
1063
+		foreach ($ticket_sums as $ticket_info) {
1064
+			if ($ticket_info['ticket'] instanceof EE_Ticket) {
1065
+				$datetimes = $ticket_info['ticket']->datetimes(array('order_by' => array('DTT_reg_limit' => 'ASC')));
1066
+				//need to sort these $datetimes by remaining (only if $current_total_available)
1067
+				//setup datetimes for simulation
1068
+				$ticket_datetimes_remaining = array();
1069
+				foreach ($datetimes as $datetime) {
1070
+					$ticket_datetimes_remaining[$datetime->ID()]['rem'] = $datetime_limits[$datetime->ID()];
1071
+					$ticket_datetimes_remaining[$datetime->ID()]['datetime'] = $datetime;
1072
+				}
1073
+				usort($ticket_datetimes_remaining, function ($a, $b) {
1074
+					if ($a['rem'] === $b['rem']) {
1075
+						return 0;
1076
+					}
1077
+					return ($a['rem'] < $b['rem']) ? -1 : 1;
1078
+				});
1079
+				//get the remaining on the first datetime (which should be the one with the least remaining) and that is
1080
+				//what we add to the spaces_available running total.  Then we need to decrease the remaining on our datetime tracker.
1081
+				$lowest_datetime = reset($ticket_datetimes_remaining);
1082
+				//need to get the lower of; what the remaining is on the lowest datetime, and the remaining on the ticket.
1083
+				// If this ends up being 0 (because of previous tickets in our simulation selling out), then it has already
1084
+				// been tracked on $spaces available and this ticket is now sold out for the simulation, so we can continue
1085
+				// to the next ticket.
1086
+				if ($current_total_available) {
1087
+					$remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->remaining());
1088
+				} else {
1089
+					$remaining = min($lowest_datetime['rem'], $ticket_info['ticket']->qty());
1090
+				}
1091
+				//if $remaining is infinite that means that all datetimes on this ticket are infinite but we've made it here because all
1092
+				//tickets have a quantity.  So we don't have to track datetimes, we can just use ticket quantities for total
1093
+				//available.
1094
+				if ($remaining === EE_INF) {
1095
+					$spaces_available += $ticket_info['ticket']->qty();
1096
+					continue;
1097
+				}
1098
+				//if ticket has sold amounts then we also need to add that (but only if doing live counts)
1099
+				if ($current_total_available) {
1100
+					$spaces_available += $ticket_info['ticket']->sold();
1101
+				}
1102
+				if ($remaining <= 0) {
1103
+					continue;
1104
+				} else {
1105
+					$spaces_available += $remaining;
1106
+				}
1107
+				//loop through the datetimes and sell them out!
1108
+				foreach ($ticket_datetimes_remaining as $datetime_info) {
1109
+					if ($datetime_info['datetime'] instanceof EE_Datetime) {
1110
+						$datetime_limits[$datetime_info['datetime']->ID()] += -$remaining;
1111
+					}
1112
+				}
1113
+			}
1114
+		}
1115
+		return apply_filters(
1116
+			'FHEE_EE_Event__total_available_spaces__spaces_available',
1117
+			$spaces_available,
1118
+			$this,
1119
+			$datetimes,
1120
+			$tickets
1121
+		);
1122
+	}
1123
+
1124
+
1125
+
1126
+	/**
1127
+	 * Checks if the event is set to sold out
1128
+	 *
1129
+	 * @param  bool $actual whether or not to perform calculations to not only figure the
1130
+	 *                      actual status but also to flip the status if necessary to sold
1131
+	 *                      out If false, we just check the existing status of the event
1132
+	 * @return boolean
1133
+	 * @throws \EE_Error
1134
+	 */
1135
+	public function is_sold_out($actual = false)
1136
+	{
1137
+		if ( ! $actual) {
1138
+			return $this->status() === EEM_Event::sold_out;
1139
+		} else {
1140
+			return $this->perform_sold_out_status_check();
1141
+		}
1142
+	}
1143
+
1144
+
1145
+
1146
+	/**
1147
+	 * Checks if the event is marked as postponed
1148
+	 *
1149
+	 * @return boolean
1150
+	 */
1151
+	public function is_postponed()
1152
+	{
1153
+		return $this->status() === EEM_Event::postponed;
1154
+	}
1155
+
1156
+
1157
+
1158
+	/**
1159
+	 * Checks if the event is marked as cancelled
1160
+	 *
1161
+	 * @return boolean
1162
+	 */
1163
+	public function is_cancelled()
1164
+	{
1165
+		return $this->status() === EEM_Event::cancelled;
1166
+	}
1167
+
1168
+
1169
+
1170
+	/**
1171
+	 * Get the logical active status in a hierarchical order for all the datetimes.  Note
1172
+	 * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1173
+	 * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1174
+	 * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1175
+	 * the event is considered expired.
1176
+	 * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a status
1177
+	 * set on the EVENT when it is not published and thus is done
1178
+	 *
1179
+	 * @param bool $reset
1180
+	 * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1181
+	 * @throws \EE_Error
1182
+	 */
1183
+	public function get_active_status($reset = false)
1184
+	{
1185
+		// if the active status has already been set, then just use that value (unless we are resetting it)
1186
+		if ( ! empty($this->_active_status) && ! $reset) {
1187
+			return $this->_active_status;
1188
+		}
1189
+		//first check if event id is present on this object
1190
+		if ( ! $this->ID()) {
1191
+			return false;
1192
+		}
1193
+		$where_params_for_event = array(array('EVT_ID' => $this->ID()));
1194
+		//if event is published:
1195
+		if ($this->status() === 'publish') {
1196
+			//active?
1197
+			if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::active, $where_params_for_event) > 0) {
1198
+				$this->_active_status = EE_Datetime::active;
1199
+			} else {
1200
+				//upcoming?
1201
+				if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::upcoming, $where_params_for_event) > 0) {
1202
+					$this->_active_status = EE_Datetime::upcoming;
1203
+				} else {
1204
+					//expired?
1205
+					if (EEM_Datetime::instance()->get_datetime_count_for_status(EE_Datetime::expired, $where_params_for_event) > 0) {
1206
+						$this->_active_status = EE_Datetime::expired;
1207
+					} else {
1208
+						//it would be odd if things make it this far because it basically means there are no datetime's
1209
+						//attached to the event.  So in this case it will just be considered inactive.
1210
+						$this->_active_status = EE_Datetime::inactive;
1211
+					}
1212
+				}
1213
+			}
1214
+		} else {
1215
+			//the event is not published, so let's just set it's active status according to its' post status
1216
+			switch ($this->status()) {
1217
+				case EEM_Event::sold_out :
1218
+					$this->_active_status = EE_Datetime::sold_out;
1219
+					break;
1220
+				case EEM_Event::cancelled :
1221
+					$this->_active_status = EE_Datetime::cancelled;
1222
+					break;
1223
+				case EEM_Event::postponed :
1224
+					$this->_active_status = EE_Datetime::postponed;
1225
+					break;
1226
+				default :
1227
+					$this->_active_status = EE_Datetime::inactive;
1228
+			}
1229
+		}
1230
+		return $this->_active_status;
1231
+	}
1232
+
1233
+
1234
+
1235
+	/**
1236
+	 *    pretty_active_status
1237
+	 *
1238
+	 * @access public
1239
+	 * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1240
+	 * @return mixed void|string
1241
+	 * @throws \EE_Error
1242
+	 */
1243
+	public function pretty_active_status($echo = true)
1244
+	{
1245
+		$active_status = $this->get_active_status();
1246
+		$status = '<span class="ee-status event-active-status-'
1247
+				  . $active_status
1248
+				  . '">'
1249
+				  . EEH_Template::pretty_status($active_status, false, 'sentence')
1250
+				  . '</span>';
1251
+		if ($echo) {
1252
+			echo $status;
1253
+			return '';
1254
+		}
1255
+		return $status;
1256
+	}
1257
+
1258
+
1259
+
1260
+	/**
1261
+	 * @return bool|int
1262
+	 * @throws \EE_Error
1263
+	 */
1264
+	public function get_number_of_tickets_sold()
1265
+	{
1266
+		$tkt_sold = 0;
1267
+		if ( ! $this->ID()) {
1268
+			return 0;
1269
+		}
1270
+		$datetimes = $this->datetimes();
1271
+		foreach ($datetimes as $datetime) {
1272
+			if ($datetime instanceof EE_Datetime) {
1273
+				$tkt_sold += $datetime->sold();
1274
+			}
1275
+		}
1276
+		return $tkt_sold;
1277
+	}
1278
+
1279
+
1280
+
1281
+	/**
1282
+	 * This just returns a count of all the registrations for this event
1283
+	 *
1284
+	 * @access  public
1285
+	 * @return int
1286
+	 * @throws \EE_Error
1287
+	 */
1288
+	public function get_count_of_all_registrations()
1289
+	{
1290
+		return EEM_Event::instance()->count_related($this, 'Registration');
1291
+	}
1292
+
1293
+
1294
+
1295
+	/**
1296
+	 * This returns the ticket with the earliest start time that is
1297
+	 * available for this event (across all datetimes attached to the event)
1298
+	 *
1299
+	 * @return EE_Ticket
1300
+	 * @throws \EE_Error
1301
+	 */
1302
+	public function get_ticket_with_earliest_start_time()
1303
+	{
1304
+		$where['Datetime.EVT_ID'] = $this->ID();
1305
+		$query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1306
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1307
+	}
1308
+
1309
+
1310
+
1311
+	/**
1312
+	 * This returns the ticket with the latest end time that is available
1313
+	 * for this event (across all datetimes attached to the event)
1314
+	 *
1315
+	 * @return EE_Ticket
1316
+	 * @throws \EE_Error
1317
+	 */
1318
+	public function get_ticket_with_latest_end_time()
1319
+	{
1320
+		$where['Datetime.EVT_ID'] = $this->ID();
1321
+		$query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1322
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1323
+	}
1324
+
1325
+
1326
+
1327
+	/**
1328
+	 * This returns whether there are any tickets on sale for this event.
1329
+	 *
1330
+	 * @return bool true = YES tickets on sale.
1331
+	 * @throws \EE_Error
1332
+	 */
1333
+	public function tickets_on_sale()
1334
+	{
1335
+		$earliest_ticket = $this->get_ticket_with_earliest_start_time();
1336
+		$latest_ticket = $this->get_ticket_with_latest_end_time();
1337
+		if ( ! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1338
+			return false;
1339
+		}
1340
+		//check on sale for these two tickets.
1341
+		if ($latest_ticket->is_on_sale() || $earliest_ticket->is_on_sale()) {
1342
+			return true;
1343
+		}
1344
+		return false;
1345
+	}
1346
+
1347
+
1348
+
1349
+	/**
1350
+	 * Gets the URL for viewing this event on the front-end. Overrides parent
1351
+	 * to check for an external URL first
1352
+	 *
1353
+	 * @return string
1354
+	 * @throws \EE_Error
1355
+	 */
1356
+	public function get_permalink()
1357
+	{
1358
+		if ($this->external_url()) {
1359
+			return $this->external_url();
1360
+		} else {
1361
+			return parent::get_permalink();
1362
+		}
1363
+	}
1364
+
1365
+
1366
+
1367
+	/**
1368
+	 * Gets the first term for 'espresso_event_categories' we can find
1369
+	 *
1370
+	 * @param array $query_params like EEM_Base::get_all
1371
+	 * @return EE_Term
1372
+	 * @throws \EE_Error
1373
+	 */
1374
+	public function first_event_category($query_params = array())
1375
+	{
1376
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1377
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1378
+		return EEM_Term::instance()->get_one($query_params);
1379
+	}
1380
+
1381
+
1382
+
1383
+	/**
1384
+	 * Gets all terms for 'espresso_event_categories' we can find
1385
+	 *
1386
+	 * @param array $query_params
1387
+	 * @return EE_Term[]
1388
+	 * @throws \EE_Error
1389
+	 */
1390
+	public function get_all_event_categories($query_params = array())
1391
+	{
1392
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1393
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1394
+		return EEM_Term::instance()->get_all($query_params);
1395
+	}
1396
+
1397
+
1398
+
1399
+	/**
1400
+	 * Gets all the question groups, ordering them by QSG_order ascending
1401
+	 *
1402
+	 * @param array $query_params @see EEM_Base::get_all
1403
+	 * @return EE_Question_Group[]
1404
+	 * @throws \EE_Error
1405
+	 */
1406
+	public function question_groups($query_params = array())
1407
+	{
1408
+		$query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1409
+		return $this->get_many_related('Question_Group', $query_params);
1410
+	}
1411
+
1412
+
1413
+
1414
+	/**
1415
+	 * Implementation for EEI_Has_Icon interface method.
1416
+	 *
1417
+	 * @see EEI_Visual_Representation for comments
1418
+	 * @return string
1419
+	 */
1420
+	public function get_icon()
1421
+	{
1422
+		return '<span class="dashicons dashicons-flag"></span>';
1423
+	}
1424
+
1425
+
1426
+
1427
+	/**
1428
+	 * Implementation for EEI_Admin_Links interface method.
1429
+	 *
1430
+	 * @see EEI_Admin_Links for comments
1431
+	 * @return string
1432
+	 * @throws \EE_Error
1433
+	 */
1434
+	public function get_admin_details_link()
1435
+	{
1436
+		return $this->get_admin_edit_link();
1437
+	}
1438
+
1439
+
1440
+
1441
+	/**
1442
+	 * Implementation for EEI_Admin_Links interface method.
1443
+	 *
1444
+	 * @see EEI_Admin_Links for comments
1445
+	 * @return string
1446
+	 * @throws \EE_Error
1447
+	 */
1448
+	public function get_admin_edit_link()
1449
+	{
1450
+		return EEH_URL::add_query_args_and_nonce(array(
1451
+			'page'   => 'espresso_events',
1452
+			'action' => 'edit',
1453
+			'post'   => $this->ID(),
1454
+		),
1455
+			admin_url('admin.php')
1456
+		);
1457
+	}
1458
+
1459
+
1460
+
1461
+	/**
1462
+	 * Implementation for EEI_Admin_Links interface method.
1463
+	 *
1464
+	 * @see EEI_Admin_Links for comments
1465
+	 * @return string
1466
+	 */
1467
+	public function get_admin_settings_link()
1468
+	{
1469
+		return EEH_URL::add_query_args_and_nonce(array(
1470
+			'page'   => 'espresso_events',
1471
+			'action' => 'default_event_settings',
1472
+		),
1473
+			admin_url('admin.php')
1474
+		);
1475
+	}
1476
+
1477
+
1478
+
1479
+	/**
1480
+	 * Implementation for EEI_Admin_Links interface method.
1481
+	 *
1482
+	 * @see EEI_Admin_Links for comments
1483
+	 * @return string
1484
+	 */
1485
+	public function get_admin_overview_link()
1486
+	{
1487
+		return EEH_URL::add_query_args_and_nonce(array(
1488
+			'page'   => 'espresso_events',
1489
+			'action' => 'default',
1490
+		),
1491
+			admin_url('admin.php')
1492
+		);
1493
+	}
1494 1494
 
1495 1495
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Registration.model.php 1 patch
Indentation   +803 added lines, -803 removed lines patch added patch discarded remove patch
@@ -2,7 +2,7 @@  discard block
 block discarded – undo
2 2
 use EventEspresso\core\services\database\TableAnalysis;
3 3
 
4 4
 if ( ! defined('EVENT_ESPRESSO_VERSION')) {
5
-    exit('No direct script access allowed');
5
+	exit('No direct script access allowed');
6 6
 }
7 7
 
8 8
 
@@ -17,760 +17,760 @@  discard block
 block discarded – undo
17 17
 class EEM_Registration extends EEM_Soft_Delete_Base
18 18
 {
19 19
 
20
-    // private instance of the Registration object
21
-    protected static $_instance = null;
22
-
23
-    /**
24
-     * Keys are the status IDs for registrations (eg, RAP, RCN, etc), and the values
25
-     * are status codes (eg, approved, cancelled, etc)
26
-     *
27
-     * @var array
28
-     */
29
-    private static $_reg_status;
30
-
31
-    /**
32
-     * The value of REG_count for a primary registrant
33
-     */
34
-    const PRIMARY_REGISTRANT_COUNT = 1;
35
-
36
-    /**
37
-     * Status ID (STS_ID on esp_status table) to indicate an INCOMPLETE registration.
38
-     * Initial status for registrations when they are first created
39
-     * Payments are NOT allowed.
40
-     * Automatically toggled to whatever the default Event registration status is upon completion of the attendee
41
-     * information reg step NO space reserved. Registration is NOT active
42
-     */
43
-    const status_id_incomplete = 'RIC';
44
-
45
-    /**
46
-     * Status ID (STS_ID on esp_status table) to indicate an UNAPPROVED registration.
47
-     * Payments are NOT allowed.
48
-     * Event Admin must manually toggle STS_ID for it to change
49
-     * No space reserved.
50
-     * Registration is active
51
-     */
52
-    const status_id_not_approved = 'RNA';
53
-
54
-    /**
55
-     * Status ID (STS_ID on esp_status table) to indicate registration is PENDING_PAYMENT .
56
-     * Payments are allowed.
57
-     * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
58
-     * No space reserved.
59
-     * Registration is active
60
-     */
61
-    const status_id_pending_payment = 'RPP';
62
-
63
-    /**
64
-     * Status ID (STS_ID on esp_status table) to indicate registration is on the WAIT_LIST .
65
-     * Payments are allowed.
66
-     * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
67
-     * No space reserved.
68
-     * Registration is active
69
-     */
70
-    const status_id_wait_list = 'RWL';
71
-
72
-    /**
73
-     * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
74
-     * the TXN may or may not be completed ( paid in full )
75
-     * Payments are allowed.
76
-     * A space IS reserved.
77
-     * Registration is active
78
-     */
79
-    const status_id_approved = 'RAP';
80
-
81
-    /**
82
-     * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
83
-     * Payments are NOT allowed.
84
-     * NO space reserved.
85
-     * Registration is NOT active
86
-     */
87
-    const status_id_cancelled = 'RCN';
88
-
89
-    /**
90
-     * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
91
-     * Payments are NOT allowed.
92
-     * No space reserved.
93
-     * Registration is NOT active
94
-     */
95
-    const status_id_declined = 'RDC';
96
-
97
-    /**
98
-     * @var TableAnalysis $table_analysis
99
-     */
100
-    protected $_table_analysis;
101
-
102
-
103
-
104
-    /**
105
-     *    private constructor to prevent direct creation
106
-     *
107
-     * @Constructor
108
-     * @access protected
109
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
110
-     *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
111
-     *                         date time model field objects.  Default is NULL (and will be assumed using the set
112
-     *                         timezone in the 'timezone_string' wp option)
113
-     */
114
-    protected function __construct($timezone = null)
115
-    {
116
-        $this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true);
117
-        $this->singular_item = __('Registration', 'event_espresso');
118
-        $this->plural_item = __('Registrations', 'event_espresso');
119
-        $this->_tables = array(
120
-            'Registration' => new EE_Primary_Table('esp_registration', 'REG_ID'),
121
-        );
122
-        $this->_fields = array(
123
-            'Registration' => array(
124
-                'REG_ID'           => new EE_Primary_Key_Int_Field('REG_ID', __('Registration ID', 'event_espresso')),
125
-                'EVT_ID'           => new EE_Foreign_Key_Int_Field(
126
-                    'EVT_ID', __('Event ID', 'event_espresso'), false, 0, 'Event'
127
-                ),
128
-                'ATT_ID'           => new EE_Foreign_Key_Int_Field(
129
-                    'ATT_ID', __('Attendee ID', 'event_espresso'), false, 0, 'Attendee'
130
-                ),
131
-                'TXN_ID'           => new EE_Foreign_Key_Int_Field('TXN_ID', __('Transaction ID', 'event_espresso'),
132
-                    false, 0, 'Transaction'
133
-                ),
134
-                'TKT_ID'           => new EE_Foreign_Key_Int_Field('TKT_ID', __('Ticket ID', 'event_espresso'), false,
135
-                    0, 'Ticket'
136
-                ),
137
-                'STS_ID'           => new EE_Foreign_Key_String_Field('STS_ID', __('Status ID', 'event_espresso'),
138
-                    false, EEM_Registration::status_id_incomplete, 'Status'
139
-                ),
140
-                'REG_date'         => new EE_Datetime_Field('REG_date',
141
-                    __('Time registration occurred', 'event_espresso'), false, EE_Datetime_Field::now, $timezone
142
-                ),
143
-                'REG_final_price'  => new EE_Money_Field('REG_final_price',
144
-                    __('Registration\'s share of the transaction total', 'event_espresso'), false, 0
145
-                ),
146
-                'REG_paid'         => new EE_Money_Field('REG_paid',
147
-                    __('Amount paid to date towards registration', 'event_espresso'), false, 0
148
-                ),
149
-                'REG_session'      => new EE_Plain_Text_Field('REG_session',
150
-                    __('Session ID of registration', 'event_espresso'), false, ''
151
-                ),
152
-                'REG_code'         => new EE_Plain_Text_Field('REG_code',
153
-                    __('Unique Code for this registration', 'event_espresso'), false, ''
154
-                ),
155
-                'REG_url_link'     => new EE_Plain_Text_Field('REG_url_link',
156
-                    __('String to be used in URL for identifying registration', 'event_espresso'), false, ''
157
-                ),
158
-                'REG_count'        => new EE_Integer_Field('REG_count',
159
-                    __('Count of this registration in the group registration ', 'event_espresso'), true, 1
160
-                ),
161
-                'REG_group_size'   => new EE_Integer_Field('REG_group_size',
162
-                    __('Number of registrations on this group', 'event_espresso'), false, 1
163
-                ),
164
-                'REG_att_is_going' => new EE_Boolean_Field('REG_att_is_going',
165
-                    __('Flag indicating the registrant plans on attending', 'event_espresso'), false, false
166
-                ),
167
-                'REG_deleted'      => new EE_Trashed_Flag_Field(
168
-                    'REG_deleted', __('Flag indicating if registration has been archived or not.', 'event_espresso'),
169
-                    false, false
170
-                ),
171
-            ),
172
-        );
173
-        $this->_model_relations = array(
174
-            'Event'                => new EE_Belongs_To_Relation(),
175
-            'Attendee'             => new EE_Belongs_To_Relation(),
176
-            'Transaction'          => new EE_Belongs_To_Relation(),
177
-            'Ticket'               => new EE_Belongs_To_Relation(),
178
-            'Status'               => new EE_Belongs_To_Relation(),
179
-            'Answer'               => new EE_Has_Many_Relation(),
180
-            'Checkin'              => new EE_Has_Many_Relation(),
181
-            'Registration_Payment' => new EE_Has_Many_Relation(),
182
-            'Payment'              => new EE_HABTM_Relation('Registration_Payment'),
183
-            'Message'              => new EE_Has_Many_Any_Relation(false)
184
-            //allow deletes even if there are messages in the queue related
185
-        );
186
-        $this->_model_chain_to_wp_user = 'Event';
187
-        parent::__construct($timezone);
188
-    }
189
-
190
-
191
-
192
-    /**
193
-     * a filterable list of registration statuses
194
-     *
195
-     * @return array
196
-     */
197
-    public static function reg_statuses()
198
-    {
199
-        return apply_filters(
200
-            'FHEE__EEM_Registration__reg_statuses',
201
-            array(
202
-                EEM_Registration::status_id_approved,
203
-                EEM_Registration::status_id_pending_payment,
204
-                EEM_Registration::status_id_wait_list,
205
-                EEM_Registration::status_id_not_approved,
206
-                EEM_Registration::status_id_incomplete,
207
-                EEM_Registration::status_id_cancelled,
208
-                EEM_Registration::status_id_declined,
209
-            )
210
-        );
211
-    }
212
-
213
-
214
-
215
-    /**
216
-     * reg_statuses_that_allow_payment
217
-     * a filterable list of registration statuses that allow a registrant to make a payment
218
-     *
219
-     * @access public
220
-     * @return array
221
-     */
222
-    public static function reg_statuses_that_allow_payment()
223
-    {
224
-        return apply_filters(
225
-            'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
226
-            array(
227
-                EEM_Registration::status_id_approved,
228
-                EEM_Registration::status_id_pending_payment,
229
-            )
230
-        );
231
-    }
232
-
233
-
234
-
235
-    /**
236
-     * active_reg_statuses
237
-     * a filterable list of registration statuses that are considered active
238
-     *
239
-     * @access public
240
-     * @return array
241
-     */
242
-    public static function active_reg_statuses()
243
-    {
244
-        return apply_filters(
245
-            'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
246
-            array(
247
-                EEM_Registration::status_id_approved,
248
-                EEM_Registration::status_id_pending_payment,
249
-                EEM_Registration::status_id_wait_list,
250
-                EEM_Registration::status_id_not_approved,
251
-            )
252
-        );
253
-    }
254
-
255
-
256
-
257
-    /**
258
-     * inactive_reg_statuses
259
-     * a filterable list of registration statuses that are not considered active
260
-     *
261
-     * @access public
262
-     * @return array
263
-     */
264
-    public static function inactive_reg_statuses()
265
-    {
266
-        return apply_filters(
267
-            'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
268
-            array(
269
-                EEM_Registration::status_id_incomplete,
270
-                EEM_Registration::status_id_cancelled,
271
-                EEM_Registration::status_id_declined,
272
-            )
273
-        );
274
-    }
275
-
276
-
277
-
278
-    /**
279
-     *    closed_reg_statuses
280
-     *    a filterable list of registration statuses that are considered "closed"
281
-     * meaning they should not be considered in any calculations involving monies owing
282
-     *
283
-     * @access public
284
-     * @return array
285
-     */
286
-    public static function closed_reg_statuses()
287
-    {
288
-        return apply_filters(
289
-            'FHEE__EEM_Registration__closed_reg_statuses',
290
-            array(
291
-                EEM_Registration::status_id_cancelled,
292
-                EEM_Registration::status_id_declined,
293
-                EEM_Registration::status_id_wait_list,
294
-            )
295
-        );
296
-    }
297
-
298
-
299
-
300
-    /**
301
-     *        get list of registration statuses
302
-     *
303
-     * @access public
304
-     * @param array $exclude    The status ids to exclude from the returned results
305
-     * @param bool  $translated If true will return the values as singular localized strings
306
-     * @return array
307
-     */
308
-    public static function reg_status_array($exclude = array(), $translated = false)
309
-    {
310
-        EEM_Registration::instance()->_get_registration_status_array($exclude);
311
-        return $translated ? EEM_Status::instance()->localized_status(self::$_reg_status, false, 'sentence')
312
-            : self::$_reg_status;
313
-    }
314
-
315
-
316
-
317
-    /**
318
-     *    get list of registration statuses
319
-     *
320
-     * @access private
321
-     * @param array $exclude
322
-     * @return array
323
-     */
324
-    private function _get_registration_status_array($exclude = array())
325
-    {
326
-        //in the very rare circumstance that we are deleting a model's table's data
327
-        //and the table hasn't actually been created, this could have an error
328
-        /** @type WPDB $wpdb */
329
-        global $wpdb;
330
-        if ($this->_get_table_analysis()->tableExists($wpdb->prefix . 'esp_status')) {
331
-            $results = $wpdb->get_results(
332
-                "SELECT STS_ID, STS_code FROM {$wpdb->prefix}esp_status WHERE STS_type = 'registration'"
333
-            );
334
-            self::$_reg_status = array();
335
-            foreach ($results as $status) {
336
-                if ( ! in_array($status->STS_ID, $exclude)) {
337
-                    self::$_reg_status[$status->STS_ID] = $status->STS_code;
338
-                }
339
-            }
340
-        }
341
-    }
342
-
343
-
344
-
345
-    /**
346
-     * Gets the injected table analyzer, or throws an exception
347
-     *
348
-     * @return TableAnalysis
349
-     * @throws \EE_Error
350
-     */
351
-    protected function _get_table_analysis()
352
-    {
353
-        if ($this->_table_analysis instanceof TableAnalysis) {
354
-            return $this->_table_analysis;
355
-        } else {
356
-            throw new \EE_Error(
357
-                sprintf(
358
-                    __('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
359
-                    get_class($this)
360
-                )
361
-            );
362
-        }
363
-    }
364
-
365
-
366
-
367
-    /**
368
-     * This returns a wpdb->results array of all registration date month and years matching the incoming query params
369
-     * and grouped by month and year.
370
-     *
371
-     * @param  array $where_params Array of query_params as described in the comments for EEM_Base::get_all()
372
-     * @return array
373
-     * @throws \EE_Error
374
-     */
375
-    public function get_reg_months_and_years($where_params)
376
-    {
377
-        $query_params[0] = $where_params;
378
-        $query_params['group_by'] = array('reg_year', 'reg_month');
379
-        $query_params['order_by'] = array('REG_date' => 'DESC');
380
-        $columns_to_select = array(
381
-            'reg_year'  => array('YEAR(REG_date)', '%s'),
382
-            'reg_month' => array('MONTHNAME(REG_date)', '%s'),
383
-        );
384
-        return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
385
-    }
386
-
387
-
388
-
389
-    /**
390
-     *        retrieve ALL registrations for a particular Attendee from db
391
-     *
392
-     * @access        public
393
-     * @param        int $ATT_ID
394
-     * @return    EE_Registration[]
395
-     */
396
-    public function get_all_registrations_for_attendee($ATT_ID = 0)
397
-    {
398
-        if ( ! $ATT_ID) {
399
-            return false;
400
-        }
401
-        return $this->get_all(array(array('ATT_ID' => $ATT_ID)));
402
-    }
403
-
404
-
405
-
406
-    /**
407
-     * Gets a registration given their REG_url_link. Yes, this should usually
408
-     * be passed via a GET parameter.
409
-     *
410
-     * @param string $REG_url_link
411
-     * @return EE_Registration
412
-     */
413
-    public function get_registration_for_reg_url_link($REG_url_link)
414
-    {
415
-        if ( ! $REG_url_link) {
416
-            return false;
417
-        }
418
-        return $this->get_one(array(array('REG_url_link' => $REG_url_link)));
419
-    }
420
-
421
-
422
-
423
-    /**
424
-     *        retrieve registration for a specific transaction attendee from db
425
-     *
426
-     * @access        public
427
-     * @param    int $TXN_ID
428
-     * @param    int $ATT_ID
429
-     * @param    int $att_nmbr in case the ATT_ID is the same for multiple registrations (same details used) then the
430
-     *                         attendee number is required
431
-     * @return        mixed        array on success, FALSE on fail
432
-     */
433
-    public function get_registration_for_transaction_attendee($TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0)
434
-    {
435
-        return $this->get_one(array(
436
-            array(
437
-                'TXN_ID' => $TXN_ID,
438
-                'ATT_ID' => $ATT_ID,
439
-            ),
440
-            'limit' => array(min(($att_nmbr - 1), 0), 1),
441
-        ));
442
-    }
443
-
444
-
445
-
446
-    /**
447
-     *        get the number of registrations per day  for the Registration Admin page Reports Tab.
448
-     *        (doesn't utilize models because it's a fairly specialized query)
449
-     *
450
-     * @access        public
451
-     * @param $period string which can be passed to php's strtotime function (eg "-1 month")
452
-     * @return stdClass[] with properties regDate and total
453
-     */
454
-    public function get_registrations_per_day_report($period = '-1 month')
455
-    {
456
-        $sql_date = $this->convert_datetime_for_query('REG_date', date("Y-m-d H:i:s", strtotime($period)),
457
-            'Y-m-d H:i:s', 'UTC');
458
-        $where = array(
459
-            'REG_date' => array('>=', $sql_date),
460
-            'STS_ID'   => array('!=', EEM_Registration::status_id_incomplete),
461
-        );
462
-        if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_day_report')) {
463
-            $where['Event.EVT_wp_user'] = get_current_user_id();
464
-        }
465
-        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'REG_date');
466
-        $results = $this->_get_all_wpdb_results(
467
-            array(
468
-                $where,
469
-                'group_by' => 'regDate',
470
-                'order_by' => array('REG_date' => 'ASC'),
471
-            ),
472
-            OBJECT,
473
-            array(
474
-                'regDate' => array('DATE(' . $query_interval . ')', '%s'),
475
-                'total'   => array('count(REG_ID)', '%d'),
476
-            ));
477
-        return $results;
478
-    }
479
-
480
-
481
-
482
-    /**
483
-     * Get the number of registrations per day including the count of registrations for each Registration Status.
484
-     * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
485
-     *
486
-     * @param string $period
487
-     * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
488
-     *                    (i.e. RAP)
489
-     */
490
-    public function get_registrations_per_day_and_per_status_report($period = '-1 month')
491
-    {
492
-        global $wpdb;
493
-        $registration_table = $wpdb->prefix . 'esp_registration';
494
-        $event_table = $wpdb->posts;
495
-        $sql_date = date("Y-m-d H:i:s", strtotime($period));
496
-        //prepare the query interval for displaying offset
497
-        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'dates.REG_date');
498
-        //inner date query
499
-        $inner_date_query = "SELECT DISTINCT REG_date from $registration_table ";
500
-        $inner_where = " WHERE";
501
-        //exclude events not authored by user if permissions in effect
502
-        if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
503
-            $inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
504
-            $inner_where .= " post_author = " . get_current_user_id() . " AND";
505
-        }
506
-        $inner_where .= " REG_date >= '$sql_date'";
507
-        $inner_date_query .= $inner_where;
508
-        //start main query
509
-        $select = "SELECT DATE($query_interval) as Registration_REG_date, ";
510
-        $join = '';
511
-        $join_parts = array();
512
-        $select_parts = array();
513
-        //loop through registration stati to do parts for each status.
514
-        foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
515
-            if ($STS_ID === EEM_Registration::status_id_incomplete) {
516
-                continue;
517
-            }
518
-            $select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
519
-            $join_parts[] = "$registration_table AS $STS_code ON $STS_code.REG_date = dates.REG_date AND $STS_code.STS_ID = '$STS_ID'";
520
-        }
521
-        //setup the selects
522
-        $select .= implode(', ', $select_parts);
523
-        $select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
524
-        //setup the joins
525
-        $join .= implode(" LEFT JOIN ", $join_parts);
526
-        //now let's put it all together
527
-        $query = $select . $join . ' GROUP BY Registration_REG_date';
528
-        //and execute it
529
-        $results = $wpdb->get_results($query, ARRAY_A);
530
-        return $results;
531
-    }
532
-
533
-
534
-
535
-    /**
536
-     *        get the number of registrations per event  for the Registration Admin page Reports Tab
537
-     *
538
-     * @access        public
539
-     * @param $period string which can be passed to php's strtotime function (eg "-1 month")
540
-     * @return stdClass[] each with properties event_name, reg_limit, and total
541
-     */
542
-    public function get_registrations_per_event_report($period = '-1 month')
543
-    {
544
-        $date_sql = $this->convert_datetime_for_query('REG_date', date("Y-m-d H:i:s", strtotime($period)),
545
-            'Y-m-d H:i:s', 'UTC');
546
-        $where = array(
547
-            'REG_date' => array('>=', $date_sql),
548
-            'STS_ID'   => array('!=', EEM_Registration::status_id_incomplete),
549
-        );
550
-        if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
551
-            $where['Event.EVT_wp_user'] = get_current_user_id();
552
-        }
553
-        $results = $this->_get_all_wpdb_results(array(
554
-            $where,
555
-            'group_by' => 'Event.EVT_name',
556
-            'order_by' => 'Event.EVT_name',
557
-            'limit'    => array(0, 24),
558
-        ),
559
-            OBJECT,
560
-            array(
561
-                'event_name' => array('Event_CPT.post_title', '%s'),
562
-                'total'      => array('COUNT(REG_ID)', '%s'),
563
-            )
564
-        );
565
-        return $results;
566
-    }
567
-
568
-
569
-
570
-    /**
571
-     * Get the number of registrations per event grouped by registration status.
572
-     * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
573
-     *
574
-     * @param string $period
575
-     * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
576
-     *                    (i.e. RAP)
577
-     */
578
-    public function get_registrations_per_event_and_per_status_report($period = '-1 month')
579
-    {
580
-        global $wpdb;
581
-        $registration_table = $wpdb->prefix . 'esp_registration';
582
-        $event_table = $wpdb->posts;
583
-        $sql_date = date("Y-m-d H:i:s", strtotime($period));
584
-        //inner date query
585
-        $inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
586
-        $inner_where = " WHERE";
587
-        //exclude events not authored by user if permissions in effect
588
-        if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
589
-            $inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
590
-            $inner_where .= " post_author = " . get_current_user_id() . " AND";
591
-        }
592
-        $inner_where .= " REG_date >= '$sql_date'";
593
-        $inner_date_query .= $inner_where;
594
-        //build main query
595
-        $select = "SELECT Event.post_title as Registration_Event, ";
596
-        $join = '';
597
-        $join_parts = array();
598
-        $select_parts = array();
599
-        //loop through registration stati to do parts for each status.
600
-        foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
601
-            if ($STS_ID === EEM_Registration::status_id_incomplete) {
602
-                continue;
603
-            }
604
-            $select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
605
-            $join_parts[] = "$registration_table AS $STS_code ON $STS_code.EVT_ID = dates.EVT_ID AND $STS_code.STS_ID = '$STS_ID' AND $STS_code.REG_date = dates.REG_date";
606
-        }
607
-        //setup the selects
608
-        $select .= implode(', ', $select_parts);
609
-        $select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
610
-        //setup remaining joins
611
-        $join .= implode(" LEFT JOIN ", $join_parts);
612
-        //now put it all together
613
-        $query = $select . $join . ' GROUP BY Registration_Event';
614
-        //and execute
615
-        $results = $wpdb->get_results($query, ARRAY_A);
616
-        return $results;
617
-    }
618
-
619
-
620
-
621
-    /**
622
-     * Returns the EE_Registration of the primary attendee on the transaction id provided
623
-     *
624
-     * @param int $TXN_ID
625
-     * @return EE_Registration
626
-     */
627
-    public function get_primary_registration_for_transaction_ID($TXN_ID = 0)
628
-    {
629
-        if ( ! $TXN_ID) {
630
-            return false;
631
-        }
632
-        return $this->get_one(array(
633
-            array(
634
-                'TXN_ID'    => $TXN_ID,
635
-                'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT,
636
-            ),
637
-        ));
638
-    }
639
-
640
-
641
-
642
-    /**
643
-     *        get_event_registration_count
644
-     *
645
-     * @access public
646
-     * @param int     $EVT_ID
647
-     * @param boolean $for_incomplete_payments
648
-     * @return int
649
-     */
650
-    public function get_event_registration_count($EVT_ID, $for_incomplete_payments = false)
651
-    {
652
-        // we only count approved registrations towards registration limits
653
-        $query_params = array(array('EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved));
654
-        if ($for_incomplete_payments) {
655
-            $query_params[0]['Transaction.STS_ID'] = array('!=', EEM_Transaction::complete_status_code);
656
-        }
657
-        return $this->count($query_params);
658
-    }
659
-
660
-
661
-
662
-    /**
663
-     * Deletes all registrations with no transactions. Note that this needs to be very efficient
664
-     * and so it uses wpdb directly
665
-     *
666
-     * @global WPDB $wpdb
667
-     * @return int number deleted
668
-     */
669
-    public function delete_registrations_with_no_transaction()
670
-    {
671
-        /** @type WPDB $wpdb */
672
-        global $wpdb;
673
-        return $wpdb->query(
674
-            'DELETE r FROM '
675
-            . $this->table()
676
-            . ' r LEFT JOIN '
677
-            . EEM_Transaction::instance()->table()
678
-            . ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL');
679
-    }
680
-
681
-
682
-
683
-    /**
684
-     *  Count registrations checked into (or out of) a datetime
685
-     *
686
-     * @param int     $DTT_ID     datetime ID
687
-     * @param boolean $checked_in whether to count registrations checked IN or OUT
688
-     * @return int
689
-     */
690
-    public function count_registrations_checked_into_datetime($DTT_ID, $checked_in = true)
691
-    {
692
-        global $wpdb;
693
-        //subquery to get latest checkin
694
-        $query = $wpdb->prepare(
695
-            'SELECT '
696
-            . 'COUNT( DISTINCT checkins.REG_ID ) '
697
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
698
-            . '( SELECT '
699
-            . 'max( CHK_timestamp ) AS latest_checkin, '
700
-            . 'REG_ID AS REG_ID '
701
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' '
702
-            . 'WHERE DTT_ID=%d '
703
-            . 'GROUP BY REG_ID'
704
-            . ') AS most_recent_checkin_per_reg '
705
-            . 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
706
-            . 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
707
-            . 'WHERE '
708
-            . 'checkins.CHK_in=%d',
709
-            $DTT_ID,
710
-            $checked_in
711
-        );
712
-        return (int)$wpdb->get_var($query);
713
-    }
714
-
715
-
716
-
717
-    /**
718
-     *  Count registrations checked into (or out of) an event.
719
-     *
720
-     * @param int     $EVT_ID     event ID
721
-     * @param boolean $checked_in whether to count registrations checked IN or OUT
722
-     * @return int
723
-     */
724
-    public function count_registrations_checked_into_event($EVT_ID, $checked_in = true)
725
-    {
726
-        global $wpdb;
727
-        //subquery to get latest checkin
728
-        $query = $wpdb->prepare(
729
-            'SELECT '
730
-            . 'COUNT( DISTINCT checkins.REG_ID ) '
731
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
732
-            . '( SELECT '
733
-            . 'max( CHK_timestamp ) AS latest_checkin, '
734
-            . 'REG_ID AS REG_ID '
735
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
736
-            . 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
737
-            . 'ON c.DTT_ID=d.DTT_ID '
738
-            . 'WHERE d.EVT_ID=%d '
739
-            . 'GROUP BY REG_ID'
740
-            . ') AS most_recent_checkin_per_reg '
741
-            . 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
742
-            . 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
743
-            . 'WHERE '
744
-            . 'checkins.CHK_in=%d',
745
-            $EVT_ID,
746
-            $checked_in
747
-        );
748
-        return (int)$wpdb->get_var($query);
749
-    }
750
-
751
-
752
-
753
-    /**
754
-     * The purpose of this method is to retrieve an array of
755
-     * EE_Registration objects that represent the latest registration
756
-     * for each ATT_ID given in the function argument.
757
-     *
758
-     * @param array $attendee_ids
759
-     * @return EE_Registration[]
760
-     */
761
-    public function get_latest_registration_for_each_of_given_contacts($attendee_ids = array())
762
-    {
763
-        //first do a native wp_query to get the latest REG_ID's matching these attendees.
764
-        global $wpdb;
765
-        $registration_table = $wpdb->prefix . 'esp_registration';
766
-        $attendee_table = $wpdb->posts;
767
-        $attendee_ids = is_array($attendee_ids)
768
-            ? array_map('absint', $attendee_ids)
769
-            : array((int)$attendee_ids);
770
-        $attendee_ids = implode(',', $attendee_ids);
771
-        //first we do a query to get the registration ids
772
-        // (because a group by before order by causes the order by to be ignored.)
773
-        $registration_id_query = "
20
+	// private instance of the Registration object
21
+	protected static $_instance = null;
22
+
23
+	/**
24
+	 * Keys are the status IDs for registrations (eg, RAP, RCN, etc), and the values
25
+	 * are status codes (eg, approved, cancelled, etc)
26
+	 *
27
+	 * @var array
28
+	 */
29
+	private static $_reg_status;
30
+
31
+	/**
32
+	 * The value of REG_count for a primary registrant
33
+	 */
34
+	const PRIMARY_REGISTRANT_COUNT = 1;
35
+
36
+	/**
37
+	 * Status ID (STS_ID on esp_status table) to indicate an INCOMPLETE registration.
38
+	 * Initial status for registrations when they are first created
39
+	 * Payments are NOT allowed.
40
+	 * Automatically toggled to whatever the default Event registration status is upon completion of the attendee
41
+	 * information reg step NO space reserved. Registration is NOT active
42
+	 */
43
+	const status_id_incomplete = 'RIC';
44
+
45
+	/**
46
+	 * Status ID (STS_ID on esp_status table) to indicate an UNAPPROVED registration.
47
+	 * Payments are NOT allowed.
48
+	 * Event Admin must manually toggle STS_ID for it to change
49
+	 * No space reserved.
50
+	 * Registration is active
51
+	 */
52
+	const status_id_not_approved = 'RNA';
53
+
54
+	/**
55
+	 * Status ID (STS_ID on esp_status table) to indicate registration is PENDING_PAYMENT .
56
+	 * Payments are allowed.
57
+	 * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
58
+	 * No space reserved.
59
+	 * Registration is active
60
+	 */
61
+	const status_id_pending_payment = 'RPP';
62
+
63
+	/**
64
+	 * Status ID (STS_ID on esp_status table) to indicate registration is on the WAIT_LIST .
65
+	 * Payments are allowed.
66
+	 * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
67
+	 * No space reserved.
68
+	 * Registration is active
69
+	 */
70
+	const status_id_wait_list = 'RWL';
71
+
72
+	/**
73
+	 * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
74
+	 * the TXN may or may not be completed ( paid in full )
75
+	 * Payments are allowed.
76
+	 * A space IS reserved.
77
+	 * Registration is active
78
+	 */
79
+	const status_id_approved = 'RAP';
80
+
81
+	/**
82
+	 * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
83
+	 * Payments are NOT allowed.
84
+	 * NO space reserved.
85
+	 * Registration is NOT active
86
+	 */
87
+	const status_id_cancelled = 'RCN';
88
+
89
+	/**
90
+	 * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
91
+	 * Payments are NOT allowed.
92
+	 * No space reserved.
93
+	 * Registration is NOT active
94
+	 */
95
+	const status_id_declined = 'RDC';
96
+
97
+	/**
98
+	 * @var TableAnalysis $table_analysis
99
+	 */
100
+	protected $_table_analysis;
101
+
102
+
103
+
104
+	/**
105
+	 *    private constructor to prevent direct creation
106
+	 *
107
+	 * @Constructor
108
+	 * @access protected
109
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
110
+	 *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
111
+	 *                         date time model field objects.  Default is NULL (and will be assumed using the set
112
+	 *                         timezone in the 'timezone_string' wp option)
113
+	 */
114
+	protected function __construct($timezone = null)
115
+	{
116
+		$this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true);
117
+		$this->singular_item = __('Registration', 'event_espresso');
118
+		$this->plural_item = __('Registrations', 'event_espresso');
119
+		$this->_tables = array(
120
+			'Registration' => new EE_Primary_Table('esp_registration', 'REG_ID'),
121
+		);
122
+		$this->_fields = array(
123
+			'Registration' => array(
124
+				'REG_ID'           => new EE_Primary_Key_Int_Field('REG_ID', __('Registration ID', 'event_espresso')),
125
+				'EVT_ID'           => new EE_Foreign_Key_Int_Field(
126
+					'EVT_ID', __('Event ID', 'event_espresso'), false, 0, 'Event'
127
+				),
128
+				'ATT_ID'           => new EE_Foreign_Key_Int_Field(
129
+					'ATT_ID', __('Attendee ID', 'event_espresso'), false, 0, 'Attendee'
130
+				),
131
+				'TXN_ID'           => new EE_Foreign_Key_Int_Field('TXN_ID', __('Transaction ID', 'event_espresso'),
132
+					false, 0, 'Transaction'
133
+				),
134
+				'TKT_ID'           => new EE_Foreign_Key_Int_Field('TKT_ID', __('Ticket ID', 'event_espresso'), false,
135
+					0, 'Ticket'
136
+				),
137
+				'STS_ID'           => new EE_Foreign_Key_String_Field('STS_ID', __('Status ID', 'event_espresso'),
138
+					false, EEM_Registration::status_id_incomplete, 'Status'
139
+				),
140
+				'REG_date'         => new EE_Datetime_Field('REG_date',
141
+					__('Time registration occurred', 'event_espresso'), false, EE_Datetime_Field::now, $timezone
142
+				),
143
+				'REG_final_price'  => new EE_Money_Field('REG_final_price',
144
+					__('Registration\'s share of the transaction total', 'event_espresso'), false, 0
145
+				),
146
+				'REG_paid'         => new EE_Money_Field('REG_paid',
147
+					__('Amount paid to date towards registration', 'event_espresso'), false, 0
148
+				),
149
+				'REG_session'      => new EE_Plain_Text_Field('REG_session',
150
+					__('Session ID of registration', 'event_espresso'), false, ''
151
+				),
152
+				'REG_code'         => new EE_Plain_Text_Field('REG_code',
153
+					__('Unique Code for this registration', 'event_espresso'), false, ''
154
+				),
155
+				'REG_url_link'     => new EE_Plain_Text_Field('REG_url_link',
156
+					__('String to be used in URL for identifying registration', 'event_espresso'), false, ''
157
+				),
158
+				'REG_count'        => new EE_Integer_Field('REG_count',
159
+					__('Count of this registration in the group registration ', 'event_espresso'), true, 1
160
+				),
161
+				'REG_group_size'   => new EE_Integer_Field('REG_group_size',
162
+					__('Number of registrations on this group', 'event_espresso'), false, 1
163
+				),
164
+				'REG_att_is_going' => new EE_Boolean_Field('REG_att_is_going',
165
+					__('Flag indicating the registrant plans on attending', 'event_espresso'), false, false
166
+				),
167
+				'REG_deleted'      => new EE_Trashed_Flag_Field(
168
+					'REG_deleted', __('Flag indicating if registration has been archived or not.', 'event_espresso'),
169
+					false, false
170
+				),
171
+			),
172
+		);
173
+		$this->_model_relations = array(
174
+			'Event'                => new EE_Belongs_To_Relation(),
175
+			'Attendee'             => new EE_Belongs_To_Relation(),
176
+			'Transaction'          => new EE_Belongs_To_Relation(),
177
+			'Ticket'               => new EE_Belongs_To_Relation(),
178
+			'Status'               => new EE_Belongs_To_Relation(),
179
+			'Answer'               => new EE_Has_Many_Relation(),
180
+			'Checkin'              => new EE_Has_Many_Relation(),
181
+			'Registration_Payment' => new EE_Has_Many_Relation(),
182
+			'Payment'              => new EE_HABTM_Relation('Registration_Payment'),
183
+			'Message'              => new EE_Has_Many_Any_Relation(false)
184
+			//allow deletes even if there are messages in the queue related
185
+		);
186
+		$this->_model_chain_to_wp_user = 'Event';
187
+		parent::__construct($timezone);
188
+	}
189
+
190
+
191
+
192
+	/**
193
+	 * a filterable list of registration statuses
194
+	 *
195
+	 * @return array
196
+	 */
197
+	public static function reg_statuses()
198
+	{
199
+		return apply_filters(
200
+			'FHEE__EEM_Registration__reg_statuses',
201
+			array(
202
+				EEM_Registration::status_id_approved,
203
+				EEM_Registration::status_id_pending_payment,
204
+				EEM_Registration::status_id_wait_list,
205
+				EEM_Registration::status_id_not_approved,
206
+				EEM_Registration::status_id_incomplete,
207
+				EEM_Registration::status_id_cancelled,
208
+				EEM_Registration::status_id_declined,
209
+			)
210
+		);
211
+	}
212
+
213
+
214
+
215
+	/**
216
+	 * reg_statuses_that_allow_payment
217
+	 * a filterable list of registration statuses that allow a registrant to make a payment
218
+	 *
219
+	 * @access public
220
+	 * @return array
221
+	 */
222
+	public static function reg_statuses_that_allow_payment()
223
+	{
224
+		return apply_filters(
225
+			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
226
+			array(
227
+				EEM_Registration::status_id_approved,
228
+				EEM_Registration::status_id_pending_payment,
229
+			)
230
+		);
231
+	}
232
+
233
+
234
+
235
+	/**
236
+	 * active_reg_statuses
237
+	 * a filterable list of registration statuses that are considered active
238
+	 *
239
+	 * @access public
240
+	 * @return array
241
+	 */
242
+	public static function active_reg_statuses()
243
+	{
244
+		return apply_filters(
245
+			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
246
+			array(
247
+				EEM_Registration::status_id_approved,
248
+				EEM_Registration::status_id_pending_payment,
249
+				EEM_Registration::status_id_wait_list,
250
+				EEM_Registration::status_id_not_approved,
251
+			)
252
+		);
253
+	}
254
+
255
+
256
+
257
+	/**
258
+	 * inactive_reg_statuses
259
+	 * a filterable list of registration statuses that are not considered active
260
+	 *
261
+	 * @access public
262
+	 * @return array
263
+	 */
264
+	public static function inactive_reg_statuses()
265
+	{
266
+		return apply_filters(
267
+			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
268
+			array(
269
+				EEM_Registration::status_id_incomplete,
270
+				EEM_Registration::status_id_cancelled,
271
+				EEM_Registration::status_id_declined,
272
+			)
273
+		);
274
+	}
275
+
276
+
277
+
278
+	/**
279
+	 *    closed_reg_statuses
280
+	 *    a filterable list of registration statuses that are considered "closed"
281
+	 * meaning they should not be considered in any calculations involving monies owing
282
+	 *
283
+	 * @access public
284
+	 * @return array
285
+	 */
286
+	public static function closed_reg_statuses()
287
+	{
288
+		return apply_filters(
289
+			'FHEE__EEM_Registration__closed_reg_statuses',
290
+			array(
291
+				EEM_Registration::status_id_cancelled,
292
+				EEM_Registration::status_id_declined,
293
+				EEM_Registration::status_id_wait_list,
294
+			)
295
+		);
296
+	}
297
+
298
+
299
+
300
+	/**
301
+	 *        get list of registration statuses
302
+	 *
303
+	 * @access public
304
+	 * @param array $exclude    The status ids to exclude from the returned results
305
+	 * @param bool  $translated If true will return the values as singular localized strings
306
+	 * @return array
307
+	 */
308
+	public static function reg_status_array($exclude = array(), $translated = false)
309
+	{
310
+		EEM_Registration::instance()->_get_registration_status_array($exclude);
311
+		return $translated ? EEM_Status::instance()->localized_status(self::$_reg_status, false, 'sentence')
312
+			: self::$_reg_status;
313
+	}
314
+
315
+
316
+
317
+	/**
318
+	 *    get list of registration statuses
319
+	 *
320
+	 * @access private
321
+	 * @param array $exclude
322
+	 * @return array
323
+	 */
324
+	private function _get_registration_status_array($exclude = array())
325
+	{
326
+		//in the very rare circumstance that we are deleting a model's table's data
327
+		//and the table hasn't actually been created, this could have an error
328
+		/** @type WPDB $wpdb */
329
+		global $wpdb;
330
+		if ($this->_get_table_analysis()->tableExists($wpdb->prefix . 'esp_status')) {
331
+			$results = $wpdb->get_results(
332
+				"SELECT STS_ID, STS_code FROM {$wpdb->prefix}esp_status WHERE STS_type = 'registration'"
333
+			);
334
+			self::$_reg_status = array();
335
+			foreach ($results as $status) {
336
+				if ( ! in_array($status->STS_ID, $exclude)) {
337
+					self::$_reg_status[$status->STS_ID] = $status->STS_code;
338
+				}
339
+			}
340
+		}
341
+	}
342
+
343
+
344
+
345
+	/**
346
+	 * Gets the injected table analyzer, or throws an exception
347
+	 *
348
+	 * @return TableAnalysis
349
+	 * @throws \EE_Error
350
+	 */
351
+	protected function _get_table_analysis()
352
+	{
353
+		if ($this->_table_analysis instanceof TableAnalysis) {
354
+			return $this->_table_analysis;
355
+		} else {
356
+			throw new \EE_Error(
357
+				sprintf(
358
+					__('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
359
+					get_class($this)
360
+				)
361
+			);
362
+		}
363
+	}
364
+
365
+
366
+
367
+	/**
368
+	 * This returns a wpdb->results array of all registration date month and years matching the incoming query params
369
+	 * and grouped by month and year.
370
+	 *
371
+	 * @param  array $where_params Array of query_params as described in the comments for EEM_Base::get_all()
372
+	 * @return array
373
+	 * @throws \EE_Error
374
+	 */
375
+	public function get_reg_months_and_years($where_params)
376
+	{
377
+		$query_params[0] = $where_params;
378
+		$query_params['group_by'] = array('reg_year', 'reg_month');
379
+		$query_params['order_by'] = array('REG_date' => 'DESC');
380
+		$columns_to_select = array(
381
+			'reg_year'  => array('YEAR(REG_date)', '%s'),
382
+			'reg_month' => array('MONTHNAME(REG_date)', '%s'),
383
+		);
384
+		return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
385
+	}
386
+
387
+
388
+
389
+	/**
390
+	 *        retrieve ALL registrations for a particular Attendee from db
391
+	 *
392
+	 * @access        public
393
+	 * @param        int $ATT_ID
394
+	 * @return    EE_Registration[]
395
+	 */
396
+	public function get_all_registrations_for_attendee($ATT_ID = 0)
397
+	{
398
+		if ( ! $ATT_ID) {
399
+			return false;
400
+		}
401
+		return $this->get_all(array(array('ATT_ID' => $ATT_ID)));
402
+	}
403
+
404
+
405
+
406
+	/**
407
+	 * Gets a registration given their REG_url_link. Yes, this should usually
408
+	 * be passed via a GET parameter.
409
+	 *
410
+	 * @param string $REG_url_link
411
+	 * @return EE_Registration
412
+	 */
413
+	public function get_registration_for_reg_url_link($REG_url_link)
414
+	{
415
+		if ( ! $REG_url_link) {
416
+			return false;
417
+		}
418
+		return $this->get_one(array(array('REG_url_link' => $REG_url_link)));
419
+	}
420
+
421
+
422
+
423
+	/**
424
+	 *        retrieve registration for a specific transaction attendee from db
425
+	 *
426
+	 * @access        public
427
+	 * @param    int $TXN_ID
428
+	 * @param    int $ATT_ID
429
+	 * @param    int $att_nmbr in case the ATT_ID is the same for multiple registrations (same details used) then the
430
+	 *                         attendee number is required
431
+	 * @return        mixed        array on success, FALSE on fail
432
+	 */
433
+	public function get_registration_for_transaction_attendee($TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0)
434
+	{
435
+		return $this->get_one(array(
436
+			array(
437
+				'TXN_ID' => $TXN_ID,
438
+				'ATT_ID' => $ATT_ID,
439
+			),
440
+			'limit' => array(min(($att_nmbr - 1), 0), 1),
441
+		));
442
+	}
443
+
444
+
445
+
446
+	/**
447
+	 *        get the number of registrations per day  for the Registration Admin page Reports Tab.
448
+	 *        (doesn't utilize models because it's a fairly specialized query)
449
+	 *
450
+	 * @access        public
451
+	 * @param $period string which can be passed to php's strtotime function (eg "-1 month")
452
+	 * @return stdClass[] with properties regDate and total
453
+	 */
454
+	public function get_registrations_per_day_report($period = '-1 month')
455
+	{
456
+		$sql_date = $this->convert_datetime_for_query('REG_date', date("Y-m-d H:i:s", strtotime($period)),
457
+			'Y-m-d H:i:s', 'UTC');
458
+		$where = array(
459
+			'REG_date' => array('>=', $sql_date),
460
+			'STS_ID'   => array('!=', EEM_Registration::status_id_incomplete),
461
+		);
462
+		if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_day_report')) {
463
+			$where['Event.EVT_wp_user'] = get_current_user_id();
464
+		}
465
+		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'REG_date');
466
+		$results = $this->_get_all_wpdb_results(
467
+			array(
468
+				$where,
469
+				'group_by' => 'regDate',
470
+				'order_by' => array('REG_date' => 'ASC'),
471
+			),
472
+			OBJECT,
473
+			array(
474
+				'regDate' => array('DATE(' . $query_interval . ')', '%s'),
475
+				'total'   => array('count(REG_ID)', '%d'),
476
+			));
477
+		return $results;
478
+	}
479
+
480
+
481
+
482
+	/**
483
+	 * Get the number of registrations per day including the count of registrations for each Registration Status.
484
+	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
485
+	 *
486
+	 * @param string $period
487
+	 * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
488
+	 *                    (i.e. RAP)
489
+	 */
490
+	public function get_registrations_per_day_and_per_status_report($period = '-1 month')
491
+	{
492
+		global $wpdb;
493
+		$registration_table = $wpdb->prefix . 'esp_registration';
494
+		$event_table = $wpdb->posts;
495
+		$sql_date = date("Y-m-d H:i:s", strtotime($period));
496
+		//prepare the query interval for displaying offset
497
+		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'dates.REG_date');
498
+		//inner date query
499
+		$inner_date_query = "SELECT DISTINCT REG_date from $registration_table ";
500
+		$inner_where = " WHERE";
501
+		//exclude events not authored by user if permissions in effect
502
+		if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
503
+			$inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
504
+			$inner_where .= " post_author = " . get_current_user_id() . " AND";
505
+		}
506
+		$inner_where .= " REG_date >= '$sql_date'";
507
+		$inner_date_query .= $inner_where;
508
+		//start main query
509
+		$select = "SELECT DATE($query_interval) as Registration_REG_date, ";
510
+		$join = '';
511
+		$join_parts = array();
512
+		$select_parts = array();
513
+		//loop through registration stati to do parts for each status.
514
+		foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
515
+			if ($STS_ID === EEM_Registration::status_id_incomplete) {
516
+				continue;
517
+			}
518
+			$select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
519
+			$join_parts[] = "$registration_table AS $STS_code ON $STS_code.REG_date = dates.REG_date AND $STS_code.STS_ID = '$STS_ID'";
520
+		}
521
+		//setup the selects
522
+		$select .= implode(', ', $select_parts);
523
+		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
524
+		//setup the joins
525
+		$join .= implode(" LEFT JOIN ", $join_parts);
526
+		//now let's put it all together
527
+		$query = $select . $join . ' GROUP BY Registration_REG_date';
528
+		//and execute it
529
+		$results = $wpdb->get_results($query, ARRAY_A);
530
+		return $results;
531
+	}
532
+
533
+
534
+
535
+	/**
536
+	 *        get the number of registrations per event  for the Registration Admin page Reports Tab
537
+	 *
538
+	 * @access        public
539
+	 * @param $period string which can be passed to php's strtotime function (eg "-1 month")
540
+	 * @return stdClass[] each with properties event_name, reg_limit, and total
541
+	 */
542
+	public function get_registrations_per_event_report($period = '-1 month')
543
+	{
544
+		$date_sql = $this->convert_datetime_for_query('REG_date', date("Y-m-d H:i:s", strtotime($period)),
545
+			'Y-m-d H:i:s', 'UTC');
546
+		$where = array(
547
+			'REG_date' => array('>=', $date_sql),
548
+			'STS_ID'   => array('!=', EEM_Registration::status_id_incomplete),
549
+		);
550
+		if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
551
+			$where['Event.EVT_wp_user'] = get_current_user_id();
552
+		}
553
+		$results = $this->_get_all_wpdb_results(array(
554
+			$where,
555
+			'group_by' => 'Event.EVT_name',
556
+			'order_by' => 'Event.EVT_name',
557
+			'limit'    => array(0, 24),
558
+		),
559
+			OBJECT,
560
+			array(
561
+				'event_name' => array('Event_CPT.post_title', '%s'),
562
+				'total'      => array('COUNT(REG_ID)', '%s'),
563
+			)
564
+		);
565
+		return $results;
566
+	}
567
+
568
+
569
+
570
+	/**
571
+	 * Get the number of registrations per event grouped by registration status.
572
+	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
573
+	 *
574
+	 * @param string $period
575
+	 * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
576
+	 *                    (i.e. RAP)
577
+	 */
578
+	public function get_registrations_per_event_and_per_status_report($period = '-1 month')
579
+	{
580
+		global $wpdb;
581
+		$registration_table = $wpdb->prefix . 'esp_registration';
582
+		$event_table = $wpdb->posts;
583
+		$sql_date = date("Y-m-d H:i:s", strtotime($period));
584
+		//inner date query
585
+		$inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
586
+		$inner_where = " WHERE";
587
+		//exclude events not authored by user if permissions in effect
588
+		if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
589
+			$inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
590
+			$inner_where .= " post_author = " . get_current_user_id() . " AND";
591
+		}
592
+		$inner_where .= " REG_date >= '$sql_date'";
593
+		$inner_date_query .= $inner_where;
594
+		//build main query
595
+		$select = "SELECT Event.post_title as Registration_Event, ";
596
+		$join = '';
597
+		$join_parts = array();
598
+		$select_parts = array();
599
+		//loop through registration stati to do parts for each status.
600
+		foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
601
+			if ($STS_ID === EEM_Registration::status_id_incomplete) {
602
+				continue;
603
+			}
604
+			$select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
605
+			$join_parts[] = "$registration_table AS $STS_code ON $STS_code.EVT_ID = dates.EVT_ID AND $STS_code.STS_ID = '$STS_ID' AND $STS_code.REG_date = dates.REG_date";
606
+		}
607
+		//setup the selects
608
+		$select .= implode(', ', $select_parts);
609
+		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
610
+		//setup remaining joins
611
+		$join .= implode(" LEFT JOIN ", $join_parts);
612
+		//now put it all together
613
+		$query = $select . $join . ' GROUP BY Registration_Event';
614
+		//and execute
615
+		$results = $wpdb->get_results($query, ARRAY_A);
616
+		return $results;
617
+	}
618
+
619
+
620
+
621
+	/**
622
+	 * Returns the EE_Registration of the primary attendee on the transaction id provided
623
+	 *
624
+	 * @param int $TXN_ID
625
+	 * @return EE_Registration
626
+	 */
627
+	public function get_primary_registration_for_transaction_ID($TXN_ID = 0)
628
+	{
629
+		if ( ! $TXN_ID) {
630
+			return false;
631
+		}
632
+		return $this->get_one(array(
633
+			array(
634
+				'TXN_ID'    => $TXN_ID,
635
+				'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT,
636
+			),
637
+		));
638
+	}
639
+
640
+
641
+
642
+	/**
643
+	 *        get_event_registration_count
644
+	 *
645
+	 * @access public
646
+	 * @param int     $EVT_ID
647
+	 * @param boolean $for_incomplete_payments
648
+	 * @return int
649
+	 */
650
+	public function get_event_registration_count($EVT_ID, $for_incomplete_payments = false)
651
+	{
652
+		// we only count approved registrations towards registration limits
653
+		$query_params = array(array('EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved));
654
+		if ($for_incomplete_payments) {
655
+			$query_params[0]['Transaction.STS_ID'] = array('!=', EEM_Transaction::complete_status_code);
656
+		}
657
+		return $this->count($query_params);
658
+	}
659
+
660
+
661
+
662
+	/**
663
+	 * Deletes all registrations with no transactions. Note that this needs to be very efficient
664
+	 * and so it uses wpdb directly
665
+	 *
666
+	 * @global WPDB $wpdb
667
+	 * @return int number deleted
668
+	 */
669
+	public function delete_registrations_with_no_transaction()
670
+	{
671
+		/** @type WPDB $wpdb */
672
+		global $wpdb;
673
+		return $wpdb->query(
674
+			'DELETE r FROM '
675
+			. $this->table()
676
+			. ' r LEFT JOIN '
677
+			. EEM_Transaction::instance()->table()
678
+			. ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL');
679
+	}
680
+
681
+
682
+
683
+	/**
684
+	 *  Count registrations checked into (or out of) a datetime
685
+	 *
686
+	 * @param int     $DTT_ID     datetime ID
687
+	 * @param boolean $checked_in whether to count registrations checked IN or OUT
688
+	 * @return int
689
+	 */
690
+	public function count_registrations_checked_into_datetime($DTT_ID, $checked_in = true)
691
+	{
692
+		global $wpdb;
693
+		//subquery to get latest checkin
694
+		$query = $wpdb->prepare(
695
+			'SELECT '
696
+			. 'COUNT( DISTINCT checkins.REG_ID ) '
697
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
698
+			. '( SELECT '
699
+			. 'max( CHK_timestamp ) AS latest_checkin, '
700
+			. 'REG_ID AS REG_ID '
701
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' '
702
+			. 'WHERE DTT_ID=%d '
703
+			. 'GROUP BY REG_ID'
704
+			. ') AS most_recent_checkin_per_reg '
705
+			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
706
+			. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
707
+			. 'WHERE '
708
+			. 'checkins.CHK_in=%d',
709
+			$DTT_ID,
710
+			$checked_in
711
+		);
712
+		return (int)$wpdb->get_var($query);
713
+	}
714
+
715
+
716
+
717
+	/**
718
+	 *  Count registrations checked into (or out of) an event.
719
+	 *
720
+	 * @param int     $EVT_ID     event ID
721
+	 * @param boolean $checked_in whether to count registrations checked IN or OUT
722
+	 * @return int
723
+	 */
724
+	public function count_registrations_checked_into_event($EVT_ID, $checked_in = true)
725
+	{
726
+		global $wpdb;
727
+		//subquery to get latest checkin
728
+		$query = $wpdb->prepare(
729
+			'SELECT '
730
+			. 'COUNT( DISTINCT checkins.REG_ID ) '
731
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
732
+			. '( SELECT '
733
+			. 'max( CHK_timestamp ) AS latest_checkin, '
734
+			. 'REG_ID AS REG_ID '
735
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
736
+			. 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
737
+			. 'ON c.DTT_ID=d.DTT_ID '
738
+			. 'WHERE d.EVT_ID=%d '
739
+			. 'GROUP BY REG_ID'
740
+			. ') AS most_recent_checkin_per_reg '
741
+			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
742
+			. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
743
+			. 'WHERE '
744
+			. 'checkins.CHK_in=%d',
745
+			$EVT_ID,
746
+			$checked_in
747
+		);
748
+		return (int)$wpdb->get_var($query);
749
+	}
750
+
751
+
752
+
753
+	/**
754
+	 * The purpose of this method is to retrieve an array of
755
+	 * EE_Registration objects that represent the latest registration
756
+	 * for each ATT_ID given in the function argument.
757
+	 *
758
+	 * @param array $attendee_ids
759
+	 * @return EE_Registration[]
760
+	 */
761
+	public function get_latest_registration_for_each_of_given_contacts($attendee_ids = array())
762
+	{
763
+		//first do a native wp_query to get the latest REG_ID's matching these attendees.
764
+		global $wpdb;
765
+		$registration_table = $wpdb->prefix . 'esp_registration';
766
+		$attendee_table = $wpdb->posts;
767
+		$attendee_ids = is_array($attendee_ids)
768
+			? array_map('absint', $attendee_ids)
769
+			: array((int)$attendee_ids);
770
+		$attendee_ids = implode(',', $attendee_ids);
771
+		//first we do a query to get the registration ids
772
+		// (because a group by before order by causes the order by to be ignored.)
773
+		$registration_id_query = "
774 774
 			SELECT registrations.registration_ids as registration_id
775 775
 			FROM (
776 776
 				SELECT
@@ -784,54 +784,54 @@  discard block
 block discarded – undo
784 784
 			  ) AS registrations
785 785
 			  GROUP BY registrations.attendee_ids
786 786
 		";
787
-        $registration_ids = $wpdb->get_results($registration_id_query, ARRAY_A);
788
-        if (empty($registration_ids)) {
789
-            return array();
790
-        }
791
-        $ids_for_model_query = array();
792
-        //let's flatten the ids so they can be used in the model query.
793
-        foreach ($registration_ids as $registration_id) {
794
-            if (isset($registration_id['registration_id'])) {
795
-                $ids_for_model_query[] = $registration_id['registration_id'];
796
-            }
797
-        }
798
-        //construct query
799
-        $_where = array(
800
-            'REG_ID' => array('IN', $ids_for_model_query),
801
-        );
802
-        return $this->get_all(array($_where));
803
-    }
804
-
805
-
806
-
807
-    /**
808
-     * returns a count of registrations for the supplied event having the status as specified
809
-     *
810
-     * @param EE_Event $event
811
-     * @param string   $status
812
-     * @return int
813
-     * @throws EE_Error
814
-     */
815
-    public function event_reg_count_for_status(EE_Event $event, $status = EEM_Registration::status_id_approved)
816
-    {
817
-        $status = in_array(
818
-            $status,
819
-            EEM_Registration::reg_statuses(),
820
-            true
821
-        )
822
-            ? $status
823
-            : EEM_Registration::status_id_approved;
824
-        return $this->count(
825
-            array(
826
-                array(
827
-                    'EVT_ID' => $event->ID(),
828
-                    'STS_ID' => $status,
829
-                ),
830
-            ),
831
-            'REG_ID',
832
-            true
833
-        );
834
-    }
787
+		$registration_ids = $wpdb->get_results($registration_id_query, ARRAY_A);
788
+		if (empty($registration_ids)) {
789
+			return array();
790
+		}
791
+		$ids_for_model_query = array();
792
+		//let's flatten the ids so they can be used in the model query.
793
+		foreach ($registration_ids as $registration_id) {
794
+			if (isset($registration_id['registration_id'])) {
795
+				$ids_for_model_query[] = $registration_id['registration_id'];
796
+			}
797
+		}
798
+		//construct query
799
+		$_where = array(
800
+			'REG_ID' => array('IN', $ids_for_model_query),
801
+		);
802
+		return $this->get_all(array($_where));
803
+	}
804
+
805
+
806
+
807
+	/**
808
+	 * returns a count of registrations for the supplied event having the status as specified
809
+	 *
810
+	 * @param EE_Event $event
811
+	 * @param string   $status
812
+	 * @return int
813
+	 * @throws EE_Error
814
+	 */
815
+	public function event_reg_count_for_status(EE_Event $event, $status = EEM_Registration::status_id_approved)
816
+	{
817
+		$status = in_array(
818
+			$status,
819
+			EEM_Registration::reg_statuses(),
820
+			true
821
+		)
822
+			? $status
823
+			: EEM_Registration::status_id_approved;
824
+		return $this->count(
825
+			array(
826
+				array(
827
+					'EVT_ID' => $event->ID(),
828
+					'STS_ID' => $status,
829
+				),
830
+			),
831
+			'REG_ID',
832
+			true
833
+		);
834
+	}
835 835
 
836 836
 }
837 837
 // End of file EEM_Registration.model.php
Please login to merge, or discard this patch.