Completed
Branch BUG-10381-asset-loading (f91422)
by
unknown
170:16 queued 157:41
created
core/db_models/fields/EE_Post_Content_Field.php 1 patch
Indentation   +47 added lines, -47 removed lines patch added patch discarded remove patch
@@ -8,56 +8,56 @@
 block discarded – undo
8 8
 class EE_Post_Content_Field extends EE_Text_Field_Base
9 9
 {
10 10
 
11
-    /**
12
-     * @param string $table_column
13
-     * @param string $nicename
14
-     * @param bool   $nullable
15
-     * @param null   $default_value
16
-     */
17
-    public function __construct($table_column, $nicename, $nullable, $default_value = null)
18
-    {
19
-        parent::__construct($table_column, $nicename, $nullable, $default_value);
20
-        $this->setSchemaType('object');
21
-    }
11
+	/**
12
+	 * @param string $table_column
13
+	 * @param string $nicename
14
+	 * @param bool   $nullable
15
+	 * @param null   $default_value
16
+	 */
17
+	public function __construct($table_column, $nicename, $nullable, $default_value = null)
18
+	{
19
+		parent::__construct($table_column, $nicename, $nullable, $default_value);
20
+		$this->setSchemaType('object');
21
+	}
22 22
 
23 23
 
24
-    /**
25
-     * removes all tags which a WP Post wouldn't allow in its content normally
26
-     *
27
-     * @param string $value
28
-     * @return string
29
-     */
30
-    function prepare_for_set($value)
31
-    {
32
-        if (! current_user_can('unfiltered_html')) {
33
-            $value = wp_kses("$value", wp_kses_allowed_html('post'));
34
-        }
35
-        return parent::prepare_for_set($value);
36
-    }
24
+	/**
25
+	 * removes all tags which a WP Post wouldn't allow in its content normally
26
+	 *
27
+	 * @param string $value
28
+	 * @return string
29
+	 */
30
+	function prepare_for_set($value)
31
+	{
32
+		if (! current_user_can('unfiltered_html')) {
33
+			$value = wp_kses("$value", wp_kses_allowed_html('post'));
34
+		}
35
+		return parent::prepare_for_set($value);
36
+	}
37 37
 
38
-    function prepare_for_set_from_db($value_found_in_db_for_model_object)
39
-    {
40
-        return $value_found_in_db_for_model_object;
41
-    }
38
+	function prepare_for_set_from_db($value_found_in_db_for_model_object)
39
+	{
40
+		return $value_found_in_db_for_model_object;
41
+	}
42 42
 
43 43
 
44
-    public function getSchemaProperties()
45
-    {
46
-        return array(
47
-            'raw' => array(
48
-                'description' =>  sprintf(
49
-                    __('%s - the content as it exists in the database.', 'event_espresso'),
50
-                    $this->get_nicename()
51
-                ),
52
-                'type' => 'string'
53
-            ),
54
-            'rendered' => array(
55
-                'description' =>  sprintf(
56
-                    __('%s - the content rendered for display.', 'event_espresso'),
57
-                    $this->get_nicename()
58
-                ),
59
-                'type' => 'string'
60
-            )
61
-        );
62
-    }
44
+	public function getSchemaProperties()
45
+	{
46
+		return array(
47
+			'raw' => array(
48
+				'description' =>  sprintf(
49
+					__('%s - the content as it exists in the database.', 'event_espresso'),
50
+					$this->get_nicename()
51
+				),
52
+				'type' => 'string'
53
+			),
54
+			'rendered' => array(
55
+				'description' =>  sprintf(
56
+					__('%s - the content rendered for display.', 'event_espresso'),
57
+					$this->get_nicename()
58
+				),
59
+				'type' => 'string'
60
+			)
61
+		);
62
+	}
63 63
 }
64 64
\ No newline at end of file
Please login to merge, or discard this patch.
core/db_models/fields/EE_Datetime_Field.php 2 patches
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -210,8 +210,8 @@  discard block
 block discarded – undo
210 210
 
211 211
             default :
212 212
                 return $pretty
213
-                    ? $this->_pretty_date_format . ' ' . $this->_pretty_time_format
214
-                    : $this->_date_format . ' ' . $this->_time_format;
213
+                    ? $this->_pretty_date_format.' '.$this->_pretty_time_format
214
+                    : $this->_date_format.' '.$this->_time_format;
215 215
         }
216 216
     }
217 217
 
@@ -467,7 +467,7 @@  discard block
 block discarded – undo
467 467
      */
468 468
     protected function _prepare_for_display($DateTime, $schema = false)
469 469
     {
470
-        if (! $DateTime instanceof DateTime) {
470
+        if ( ! $DateTime instanceof DateTime) {
471 471
             if ($this->_nullable) {
472 472
                 return '';
473 473
             } else {
@@ -502,15 +502,15 @@  discard block
 block discarded – undo
502 502
             if ($this->_display_timezone()) {
503 503
                 //must be explicit because schema could equal true.
504 504
                 if ($schema === 'no_html') {
505
-                    $timezone_string = ' (' . $DateTime->format('T') . ')';
505
+                    $timezone_string = ' ('.$DateTime->format('T').')';
506 506
                 } else {
507
-                    $timezone_string = ' <span class="ee_dtt_timezone_string">(' . $DateTime->format('T') . ')</span>';
507
+                    $timezone_string = ' <span class="ee_dtt_timezone_string">('.$DateTime->format('T').')</span>';
508 508
                 }
509 509
             } else {
510 510
                 $timezone_string = '';
511 511
             }
512 512
 
513
-            return $DateTime->format($format_string) . $timezone_string;
513
+            return $DateTime->format($format_string).$timezone_string;
514 514
         } else {
515 515
             return $DateTime->format($format_string);
516 516
         }
@@ -528,7 +528,7 @@  discard block
 block discarded – undo
528 528
     public function prepare_for_use_in_db($datetime_value)
529 529
     {
530 530
         //we allow an empty value or DateTime object, but nothing else.
531
-        if (! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
531
+        if ( ! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
532 532
             throw new EE_Error(
533 533
                 __(
534 534
                     'The incoming value being prepared for setting in the database must either be empty or a php DateTime object',
@@ -583,7 +583,7 @@  discard block
 block discarded – undo
583 583
             }
584 584
         }
585 585
 
586
-        if (! $DateTime instanceof DbSafeDateTime) {
586
+        if ( ! $DateTime instanceof DbSafeDateTime) {
587 587
             // if still no datetime object, then let's just use now
588 588
             $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
589 589
         }
@@ -662,7 +662,7 @@  discard block
 block discarded – undo
662 662
         }
663 663
         //not a unix timestamp.  So we will use the set format on this object and set timezone to
664 664
         //create the DateTime object.
665
-        $format = $this->_date_format . ' ' . $this->_time_format;
665
+        $format = $this->_date_format.' '.$this->_time_format;
666 666
         try {
667 667
             $DateTime = DateTime::createFromFormat($format, $date_string, $this->_DateTimeZone);
668 668
             if ($DateTime instanceof DateTime) {
@@ -671,7 +671,7 @@  discard block
 block discarded – undo
671 671
                     $this->_DateTimeZone
672 672
                 );
673 673
             }
674
-            if (! $DateTime instanceof DbSafeDateTime) {
674
+            if ( ! $DateTime instanceof DbSafeDateTime) {
675 675
                 throw new EE_Error(
676 676
                     sprintf(
677 677
                         __('"%1$s" does not represent a valid Date Time in the format "%2$s".', 'event_espresso'),
Please login to merge, or discard this patch.
Indentation   +749 added lines, -749 removed lines patch added patch discarded remove patch
@@ -15,753 +15,753 @@
 block discarded – undo
15 15
 class EE_Datetime_Field extends EE_Model_Field_Base
16 16
 {
17 17
 
18
-    /**
19
-     * The pattern we're looking for is if only the characters 0-9 are found and there are only
20
-     * 10 or more numbers (because 9 numbers even with all 9's would be sometime in 2001 )
21
-     *
22
-     * @type string unix_timestamp_regex
23
-     */
24
-    const unix_timestamp_regex = '/[0-9]{10,}/';
25
-
26
-    /**
27
-     * @type string mysql_timestamp_format
28
-     */
29
-    const mysql_timestamp_format = 'Y-m-d H:i:s';
30
-
31
-    /**
32
-     * @type string mysql_date_format
33
-     */
34
-    const mysql_date_format = 'Y-m-d';
35
-
36
-    /**
37
-     * @type string mysql_time_format
38
-     */
39
-    const mysql_time_format = 'H:i:s';
40
-
41
-    /**
42
-     * Const for using in the default value. If the field's default is set to this,
43
-     * then we will return the time of calling `get_default_value()`, not
44
-     * just the current time at construction
45
-     */
46
-    const now = 'now';
47
-
48
-    /**
49
-     * The following properties hold the default formats for date and time.
50
-     * Defaults are set via the constructor and can be overridden on class instantiation.
51
-     * However they can also be overridden later by the set_format() method
52
-     * (and corresponding set_date_format, set_time_format methods);
53
-     */
54
-    /**
55
-     * @type string $_date_format
56
-     */
57
-    protected $_date_format = '';
58
-
59
-    /**
60
-     * @type string $_time_format
61
-     */
62
-    protected $_time_format = '';
63
-
64
-    /**
65
-     * @type string $_pretty_date_format
66
-     */
67
-    protected $_pretty_date_format = '';
68
-
69
-    /**
70
-     * @type string $_pretty_time_format
71
-     */
72
-    protected $_pretty_time_format = '';
73
-
74
-    /**
75
-     * @type DateTimeZone $_DateTimeZone
76
-     */
77
-    protected $_DateTimeZone;
78
-
79
-    /**
80
-     * @type DateTimeZone $_UTC_DateTimeZone
81
-     */
82
-    protected $_UTC_DateTimeZone;
83
-
84
-    /**
85
-     * @type DateTimeZone $_blog_DateTimeZone
86
-     */
87
-    protected $_blog_DateTimeZone;
88
-
89
-
90
-    /**
91
-     * This property holds how we want the output returned when getting a datetime string.  It is set for the
92
-     * set_date_time_output() method.  By default this is empty.  When empty, we are assuming that we want both date
93
-     * and time returned via getters.
94
-     *
95
-     * @var mixed (null|string)
96
-     */
97
-    protected $_date_time_output;
98
-
99
-
100
-    /**
101
-     * timezone string
102
-     * This gets set by the constructor and can be changed by the "set_timezone()" method so that we know what timezone
103
-     * incoming strings|timestamps are in.  This can also be used before a get to set what timezone you want strings
104
-     * coming out of the object to be in.  Default timezone is the current WP timezone option setting
105
-     *
106
-     * @var string
107
-     */
108
-    protected $_timezone_string;
109
-
110
-
111
-    /**
112
-     * This holds whatever UTC offset for the blog (we automatically convert timezone strings into their related
113
-     * offsets for comparison purposes).
114
-     *
115
-     * @var int
116
-     */
117
-    protected $_blog_offset;
118
-
119
-
120
-    /**
121
-     * @param string $table_column
122
-     * @param string $nice_name
123
-     * @param bool   $nullable
124
-     * @param string $default_value
125
-     * @param string $timezone_string
126
-     * @param string $date_format
127
-     * @param string $time_format
128
-     * @param string $pretty_date_format
129
-     * @param string $pretty_time_format
130
-     * @throws \EE_Error
131
-     */
132
-    public function __construct(
133
-        $table_column,
134
-        $nice_name,
135
-        $nullable,
136
-        $default_value,
137
-        $timezone_string = '',
138
-        $date_format = '',
139
-        $time_format = '',
140
-        $pretty_date_format = '',
141
-        $pretty_time_format = ''
142
-    ) {
143
-
144
-        $this->_date_format        = ! empty($date_format) ? $date_format : get_option('date_format');
145
-        $this->_time_format        = ! empty($time_format) ? $time_format : get_option('time_format');
146
-        $this->_pretty_date_format = ! empty($pretty_date_format) ? $pretty_date_format : get_option('date_format');
147
-        $this->_pretty_time_format = ! empty($pretty_time_format) ? $pretty_time_format : get_option('time_format');
148
-
149
-        parent::__construct($table_column, $nice_name, $nullable, $default_value);
150
-        $this->set_timezone($timezone_string);
151
-        $this->setSchemaFormat('date-time');
152
-    }
153
-
154
-
155
-    /**
156
-     * @return DateTimeZone
157
-     * @throws \EE_Error
158
-     */
159
-    public function get_UTC_DateTimeZone()
160
-    {
161
-        return $this->_UTC_DateTimeZone instanceof DateTimeZone
162
-            ? $this->_UTC_DateTimeZone
163
-            : $this->_create_timezone_object_from_timezone_string('UTC');
164
-    }
165
-
166
-
167
-    /**
168
-     * @return DateTimeZone
169
-     * @throws \EE_Error
170
-     */
171
-    public function get_blog_DateTimeZone()
172
-    {
173
-        return $this->_blog_DateTimeZone instanceof DateTimeZone
174
-            ? $this->_blog_DateTimeZone
175
-            : $this->_create_timezone_object_from_timezone_string('');
176
-    }
177
-
178
-
179
-    /**
180
-     * this prepares any incoming date data and make sure its converted to a utc unix timestamp
181
-     *
182
-     * @param  string|int $value_inputted_for_field_on_model_object could be a string formatted date time or int unix
183
-     *                                                              timestamp
184
-     * @return DateTime
185
-     */
186
-    public function prepare_for_set($value_inputted_for_field_on_model_object)
187
-    {
188
-        return $this->_get_date_object($value_inputted_for_field_on_model_object);
189
-    }
190
-
191
-
192
-    /**
193
-     * This returns the format string to be used by getters depending on what the $_date_time_output property is set at.
194
-     * getters need to know whether we're just returning the date or the time or both.  By default we return both.
195
-     *
196
-     * @param bool $pretty If we're returning the pretty formats or standard format string.
197
-     * @return string    The final assembled format string.
198
-     */
199
-    protected function _get_date_time_output($pretty = false)
200
-    {
201
-
202
-        switch ($this->_date_time_output) {
203
-            case 'time' :
204
-                return $pretty ? $this->_pretty_time_format : $this->_time_format;
205
-                break;
206
-
207
-            case 'date' :
208
-                return $pretty ? $this->_pretty_date_format : $this->_date_format;
209
-                break;
210
-
211
-            default :
212
-                return $pretty
213
-                    ? $this->_pretty_date_format . ' ' . $this->_pretty_time_format
214
-                    : $this->_date_format . ' ' . $this->_time_format;
215
-        }
216
-    }
217
-
218
-
219
-    /**
220
-     * This just sets the $_date_time_output property so we can flag how date and times are formatted before being
221
-     * returned (using the format properties)
222
-     *
223
-     * @param string $what acceptable values are 'time' or 'date'.
224
-     *                     Any other value will be set but will always result
225
-     *                     in both 'date' and 'time' being returned.
226
-     * @return void
227
-     */
228
-    public function set_date_time_output($what = null)
229
-    {
230
-        $this->_date_time_output = $what;
231
-    }
232
-
233
-
234
-    /**
235
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
236
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
237
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp).
238
-     * We also set some other properties in this method.
239
-     *
240
-     * @param string $timezone_string A valid timezone string as described by @link
241
-     *                                http://www.php.net/manual/en/timezones.php
242
-     * @return void
243
-     * @throws \EE_Error
244
-     */
245
-    public function set_timezone($timezone_string)
246
-    {
247
-        if (empty($timezone_string) && $this->_timezone_string !== null) {
248
-            // leave the timezone AS-IS if we already have one and
249
-            // the function arg didn't provide one
250
-            return;
251
-        }
252
-        $timezone_string        = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
253
-        $this->_timezone_string = ! empty($timezone_string) ? $timezone_string : 'UTC';
254
-        $this->_DateTimeZone    = $this->_create_timezone_object_from_timezone_string($this->_timezone_string);
255
-    }
256
-
257
-
258
-    /**
259
-     * _create_timezone_object_from_timezone_name
260
-     *
261
-     * @access protected
262
-     * @param string $timezone_string
263
-     * @return \DateTimeZone
264
-     * @throws \EE_Error
265
-     */
266
-    protected function _create_timezone_object_from_timezone_string($timezone_string = '')
267
-    {
268
-        return new DateTimeZone(EEH_DTT_Helper::get_valid_timezone_string($timezone_string));
269
-    }
270
-
271
-
272
-    /**
273
-     * This just returns whatever is set for the current timezone.
274
-     *
275
-     * @access public
276
-     * @return string timezone string
277
-     */
278
-    public function get_timezone()
279
-    {
280
-        return $this->_timezone_string;
281
-    }
282
-
283
-
284
-    /**
285
-     * set the $_date_format property
286
-     *
287
-     * @access public
288
-     * @param string $format a new date format (corresponding to formats accepted by PHP date() function)
289
-     * @param bool   $pretty Whether to set pretty format or not.
290
-     * @return void
291
-     */
292
-    public function set_date_format($format, $pretty = false)
293
-    {
294
-        if ($pretty) {
295
-            $this->_pretty_date_format = $format;
296
-        } else {
297
-            $this->_date_format = $format;
298
-        }
299
-    }
300
-
301
-
302
-    /**
303
-     * return the $_date_format property value.
304
-     *
305
-     * @param bool $pretty Whether to get pretty format or not.
306
-     * @return string
307
-     */
308
-    public function get_date_format($pretty = false)
309
-    {
310
-        return $pretty ? $this->_pretty_date_format : $this->_date_format;
311
-    }
312
-
313
-
314
-    /**
315
-     * set the $_time_format property
316
-     *
317
-     * @access public
318
-     * @param string $format a new time format (corresponding to formats accepted by PHP date() function)
319
-     * @param bool   $pretty Whether to set pretty format or not.
320
-     * @return void
321
-     */
322
-    public function set_time_format($format, $pretty = false)
323
-    {
324
-        if ($pretty) {
325
-            $this->_pretty_time_format = $format;
326
-        } else {
327
-            $this->_time_format = $format;
328
-        }
329
-    }
330
-
331
-
332
-    /**
333
-     * return the $_time_format property value.
334
-     *
335
-     * @param bool $pretty Whether to get pretty format or not.
336
-     * @return string
337
-     */
338
-    public function get_time_format($pretty = false)
339
-    {
340
-        return $pretty ? $this->_pretty_time_format : $this->_time_format;
341
-    }
342
-
343
-
344
-    /**
345
-     * set the $_pretty_date_format property
346
-     *
347
-     * @access public
348
-     * @param string $format a new pretty date format (corresponding to formats accepted by PHP date() function)
349
-     * @return void
350
-     */
351
-    public function set_pretty_date_format($format)
352
-    {
353
-        $this->_pretty_date_format = $format;
354
-    }
355
-
356
-
357
-    /**
358
-     * set the $_pretty_time_format property
359
-     *
360
-     * @access public
361
-     * @param string $format a new pretty time format (corresponding to formats accepted by PHP date() function)
362
-     * @return void
363
-     */
364
-    public function set_pretty_time_format($format)
365
-    {
366
-        $this->_pretty_time_format = $format;
367
-    }
368
-
369
-
370
-    /**
371
-     * Only sets the time portion of the datetime.
372
-     *
373
-     * @param string|DateTime $time_to_set_string like 8am OR a DateTime object.
374
-     * @param DateTime        $current            current DateTime object for the datetime field
375
-     * @return DateTime
376
-     */
377
-    public function prepare_for_set_with_new_time($time_to_set_string, DateTime $current)
378
-    {
379
-        // if $time_to_set_string is datetime object, then let's use it to set the parse array.
380
-        // Otherwise parse the string.
381
-        if ($time_to_set_string instanceof DateTime) {
382
-            $parsed = array(
383
-                'hour'   => $time_to_set_string->format('H'),
384
-                'minute' => $time_to_set_string->format('i'),
385
-                'second' => $time_to_set_string->format('s'),
386
-            );
387
-        } else {
388
-            //parse incoming string
389
-            $parsed = date_parse_from_format($this->_time_format, $time_to_set_string);
390
-        }
391
-
392
-        //make sure $current is in the correct timezone.
393
-        $current->setTimezone($this->_DateTimeZone);
394
-
395
-        return $current->setTime($parsed['hour'], $parsed['minute'], $parsed['second']);
396
-    }
397
-
398
-
399
-    /**
400
-     * Only sets the date portion of the datetime.
401
-     *
402
-     * @param string|DateTime $date_to_set_string like Friday, January 8th or a DateTime object.
403
-     * @param DateTime        $current            current DateTime object for the datetime field
404
-     * @return DateTime
405
-     */
406
-    public function prepare_for_set_with_new_date($date_to_set_string, DateTime $current)
407
-    {
408
-        // if $time_to_set_string is datetime object, then let's use it to set the parse array.
409
-        // Otherwise parse the string.
410
-        if ($date_to_set_string instanceof DateTime) {
411
-            $parsed = array(
412
-                'year'  => $date_to_set_string->format('Y'),
413
-                'month' => $date_to_set_string->format('m'),
414
-                'day'   => $date_to_set_string->format('d'),
415
-            );
416
-        } else {
417
-            //parse incoming string
418
-            $parsed = date_parse_from_format($this->_date_format, $date_to_set_string);
419
-        }
420
-
421
-        //make sure $current is in the correct timezone
422
-        $current->setTimezone($this->_DateTimeZone);
423
-
424
-        return $current->setDate($parsed['year'], $parsed['month'], $parsed['day']);
425
-    }
426
-
427
-
428
-    /**
429
-     * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0 timezone).  When the
430
-     * datetime gets to this stage it should ALREADY be in UTC time
431
-     *
432
-     * @param  DateTime $DateTime
433
-     * @return string formatted date time for given timezone
434
-     * @throws \EE_Error
435
-     */
436
-    public function prepare_for_get($DateTime)
437
-    {
438
-        return $this->_prepare_for_display($DateTime);
439
-    }
440
-
441
-
442
-    /**
443
-     * This differs from prepare_for_get in that it considers whether the internal $_timezone differs
444
-     * from the set wp timezone.  If so, then it returns the datetime string formatted via
445
-     * _pretty_date_format, and _pretty_time_format.  However, it also appends a timezone
446
-     * abbreviation to the date_string.
447
-     *
448
-     * @param mixed $DateTime
449
-     * @param null  $schema
450
-     * @return string
451
-     * @throws \EE_Error
452
-     */
453
-    public function prepare_for_pretty_echoing($DateTime, $schema = null)
454
-    {
455
-        return $this->_prepare_for_display($DateTime, $schema ? $schema : true);
456
-    }
457
-
458
-
459
-    /**
460
-     * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
461
-     * timezone).
462
-     *
463
-     * @param DateTime    $DateTime
464
-     * @param bool|string $schema
465
-     * @return string
466
-     * @throws \EE_Error
467
-     */
468
-    protected function _prepare_for_display($DateTime, $schema = false)
469
-    {
470
-        if (! $DateTime instanceof DateTime) {
471
-            if ($this->_nullable) {
472
-                return '';
473
-            } else {
474
-                if (WP_DEBUG) {
475
-                    throw new EE_Error(
476
-                        sprintf(
477
-                            __(
478
-                                'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.',
479
-                                'event_espresso'
480
-                            ),
481
-                            $this->_nicename
482
-                        )
483
-                    );
484
-                } else {
485
-                    $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now);
486
-                    EE_Error::add_error(
487
-                        sprintf(
488
-                            __(
489
-                                'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.  When WP_DEBUG is false, the value is set to "now" instead of throwing an exception.',
490
-                                'event_espresso'
491
-                            ),
492
-                            $this->_nicename
493
-                        )
494
-                    );
495
-                }
496
-            }
497
-        }
498
-        $format_string = $this->_get_date_time_output($schema);
499
-        //make sure datetime_value is in the correct timezone (in case that's been updated).
500
-        $DateTime->setTimezone($this->_DateTimeZone);
501
-        if ($schema) {
502
-            if ($this->_display_timezone()) {
503
-                //must be explicit because schema could equal true.
504
-                if ($schema === 'no_html') {
505
-                    $timezone_string = ' (' . $DateTime->format('T') . ')';
506
-                } else {
507
-                    $timezone_string = ' <span class="ee_dtt_timezone_string">(' . $DateTime->format('T') . ')</span>';
508
-                }
509
-            } else {
510
-                $timezone_string = '';
511
-            }
512
-
513
-            return $DateTime->format($format_string) . $timezone_string;
514
-        } else {
515
-            return $DateTime->format($format_string);
516
-        }
517
-    }
518
-
519
-
520
-    /**
521
-     * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
522
-     * timezone).
523
-     *
524
-     * @param  mixed $datetime_value u
525
-     * @return string mysql timestamp in UTC
526
-     * @throws \EE_Error
527
-     */
528
-    public function prepare_for_use_in_db($datetime_value)
529
-    {
530
-        //we allow an empty value or DateTime object, but nothing else.
531
-        if (! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
532
-            throw new EE_Error(
533
-                __(
534
-                    'The incoming value being prepared for setting in the database must either be empty or a php DateTime object',
535
-                    'event_espresso'
536
-                )
537
-            );
538
-        }
539
-
540
-        if ($datetime_value instanceof DateTime) {
541
-            if ( ! $datetime_value instanceof DbSafeDateTime) {
542
-                $datetime_value = DbSafeDateTime::createFromDateTime($datetime_value);
543
-            }
544
-
545
-            return $datetime_value->setTimezone($this->get_UTC_DateTimeZone())->format(
546
-                EE_Datetime_Field::mysql_timestamp_format
547
-            );
548
-        }
549
-
550
-        // if $datetime_value is empty, and ! $this->_nullable, use current_time() but set the GMT flag to true
551
-        return ! $this->_nullable && empty($datetime_value) ? current_time('mysql', true) : null;
552
-    }
553
-
554
-
555
-    /**
556
-     * This prepares the datetime for internal usage as a PHP DateTime object OR null (if nullable is
557
-     * allowed)
558
-     *
559
-     * @param string $datetime_string mysql timestamp in UTC
560
-     * @return  mixed null | DateTime
561
-     * @throws \EE_Error
562
-     */
563
-    public function prepare_for_set_from_db($datetime_string)
564
-    {
565
-        //if $datetime_value is empty, and ! $this->_nullable, just use time()
566
-        if (empty($datetime_string) && $this->_nullable) {
567
-            return null;
568
-        }
569
-        // datetime strings from the db should ALWAYS be in UTC+0, so use UTC_DateTimeZone when creating
570
-        if (empty($datetime_string)) {
571
-            $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
572
-        } else {
573
-            $DateTime = DateTime::createFromFormat(
574
-                EE_Datetime_Field::mysql_timestamp_format,
575
-                $datetime_string,
576
-                $this->get_UTC_DateTimeZone()
577
-            );
578
-            if ($DateTime instanceof \DateTime) {
579
-                $DateTime = new DbSafeDateTime(
580
-                    $DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
581
-                    $this->get_UTC_DateTimeZone()
582
-                );
583
-            }
584
-        }
585
-
586
-        if (! $DateTime instanceof DbSafeDateTime) {
587
-            // if still no datetime object, then let's just use now
588
-            $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
589
-        }
590
-        // THEN apply the field's set DateTimeZone
591
-        $DateTime->setTimezone($this->_DateTimeZone);
592
-
593
-        return $DateTime;
594
-    }
595
-
596
-
597
-    /**
598
-     * All this method does is determine if we're going to display the timezone string or not on any output.
599
-     * To determine this we check if the set timezone offset is different than the blog's set timezone offset.
600
-     * If so, then true.
601
-     *
602
-     * @return bool true for yes false for no
603
-     * @throws \EE_Error
604
-     */
605
-    protected function _display_timezone()
606
-    {
607
-
608
-        // first let's do a comparison of timezone strings.
609
-        // If they match then we can get out without any further calculations
610
-        $blog_string = get_option('timezone_string');
611
-        if ($blog_string === $this->_timezone_string) {
612
-            return false;
613
-        }
614
-        // now we need to calc the offset for the timezone string so we can compare with the blog offset.
615
-        $this_offset = $this->get_timezone_offset($this->_DateTimeZone);
616
-        $blog_offset = $this->get_timezone_offset($this->get_blog_DateTimeZone());
617
-        // now compare
618
-        return $blog_offset !== $this_offset;
619
-    }
620
-
621
-
622
-    /**
623
-     * This method returns a php DateTime object for setting on the EE_Base_Class model.
624
-     * EE passes around DateTime objects because they are MUCH easier to manipulate and deal
625
-     * with.
626
-     *
627
-     * @param int|string|DateTime $date_string            This should be the incoming date string.  It's assumed to be
628
-     *                                                    in the format that is set on the date_field (or DateTime
629
-     *                                                    object)!
630
-     * @return DateTime
631
-     */
632
-    protected function _get_date_object($date_string)
633
-    {
634
-        //first if this is an empty date_string and nullable is allowed, just return null.
635
-        if ($this->_nullable && empty($date_string)) {
636
-            return null;
637
-        }
638
-
639
-        // if incoming date
640
-        if ($date_string instanceof DateTime) {
641
-            $date_string->setTimezone($this->_DateTimeZone);
642
-
643
-            return $date_string;
644
-        }
645
-        // if empty date_string and made it here.
646
-        // Return a datetime object for now in the given timezone.
647
-        if (empty($date_string)) {
648
-            return new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
649
-        }
650
-        // if $date_string is matches something that looks like a Unix timestamp let's just use it.
651
-        if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $date_string)) {
652
-            try {
653
-                // This is operating under the assumption that the incoming Unix timestamp
654
-                // is an ACTUAL Unix timestamp and not the calculated one output by current_time('timestamp');
655
-                $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
656
-                $DateTime->setTimestamp($date_string);
657
-
658
-                return $DateTime;
659
-            } catch (Exception $e) {
660
-                // should be rare, but if things got fooled then let's just continue
661
-            }
662
-        }
663
-        //not a unix timestamp.  So we will use the set format on this object and set timezone to
664
-        //create the DateTime object.
665
-        $format = $this->_date_format . ' ' . $this->_time_format;
666
-        try {
667
-            $DateTime = DateTime::createFromFormat($format, $date_string, $this->_DateTimeZone);
668
-            if ($DateTime instanceof DateTime) {
669
-                $DateTime = new DbSafeDateTime(
670
-                    $DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
671
-                    $this->_DateTimeZone
672
-                );
673
-            }
674
-            if (! $DateTime instanceof DbSafeDateTime) {
675
-                throw new EE_Error(
676
-                    sprintf(
677
-                        __('"%1$s" does not represent a valid Date Time in the format "%2$s".', 'event_espresso'),
678
-                        $date_string,
679
-                        $format
680
-                    )
681
-                );
682
-            }
683
-        } catch (Exception $e) {
684
-            // if we made it here then likely then something went really wrong.
685
-            // Instead of throwing an exception, let's just return a DateTime object for now, in the set timezone.
686
-            $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
687
-        }
688
-
689
-        return $DateTime;
690
-    }
691
-
692
-
693
-
694
-    /**
695
-     * get_timezone_transitions
696
-     *
697
-     * @param \DateTimeZone $DateTimeZone
698
-     * @param int           $time
699
-     * @param bool          $first_only
700
-     * @return mixed
701
-     */
702
-    public function get_timezone_transitions(DateTimeZone $DateTimeZone, $time = null, $first_only = true)
703
-    {
704
-        $time = is_int($time) || $time === null ? $time : strtotime($time);
705
-        $time = preg_match(EE_Datetime_Field::unix_timestamp_regex, $time) ? $time : time();
706
-        $transitions = $DateTimeZone->getTransitions($time);
707
-        return $first_only && ! isset($transitions['ts']) ? reset($transitions) : $transitions;
708
-    }
709
-
710
-
711
-
712
-    /**
713
-     * get_timezone_offset
714
-     *
715
-     * @param \DateTimeZone $DateTimeZone
716
-     * @param int           $time
717
-     * @return mixed
718
-     * @throws \DomainException
719
-     */
720
-    public function get_timezone_offset(DateTimeZone $DateTimeZone, $time = null)
721
-    {
722
-        $transitions = $this->get_timezone_transitions($DateTimeZone, $time);
723
-        if ( ! isset($transitions['offset'])) {
724
-            throw new DomainException();
725
-        }
726
-        return $transitions['offset'];
727
-    }
728
-
729
-
730
-    /**
731
-     * This will take an incoming timezone string and return the abbreviation for that timezone
732
-     *
733
-     * @param  string $timezone_string
734
-     * @return string           abbreviation
735
-     * @throws \EE_Error
736
-     */
737
-    public function get_timezone_abbrev($timezone_string)
738
-    {
739
-        $timezone_string = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
740
-        $dateTime        = new DateTime(\EE_Datetime_Field::now, new DateTimeZone($timezone_string));
741
-
742
-        return $dateTime->format('T');
743
-    }
744
-
745
-    /**
746
-     * Overrides the parent to allow for having a dynamic "now" value
747
-     *
748
-     * @return mixed
749
-     */
750
-    public function get_default_value()
751
-    {
752
-        if ($this->_default_value === EE_Datetime_Field::now) {
753
-            return time();
754
-        } else {
755
-            return parent::get_default_value();
756
-        }
757
-    }
758
-
759
-
760
-    public function getSchemaDescription()
761
-    {
762
-        return sprintf(
763
-            esc_html__('%s - the value for this field is in the timezone of the site.', 'event_espresso'),
764
-            $this->get_nicename()
765
-        );
766
-    }
18
+	/**
19
+	 * The pattern we're looking for is if only the characters 0-9 are found and there are only
20
+	 * 10 or more numbers (because 9 numbers even with all 9's would be sometime in 2001 )
21
+	 *
22
+	 * @type string unix_timestamp_regex
23
+	 */
24
+	const unix_timestamp_regex = '/[0-9]{10,}/';
25
+
26
+	/**
27
+	 * @type string mysql_timestamp_format
28
+	 */
29
+	const mysql_timestamp_format = 'Y-m-d H:i:s';
30
+
31
+	/**
32
+	 * @type string mysql_date_format
33
+	 */
34
+	const mysql_date_format = 'Y-m-d';
35
+
36
+	/**
37
+	 * @type string mysql_time_format
38
+	 */
39
+	const mysql_time_format = 'H:i:s';
40
+
41
+	/**
42
+	 * Const for using in the default value. If the field's default is set to this,
43
+	 * then we will return the time of calling `get_default_value()`, not
44
+	 * just the current time at construction
45
+	 */
46
+	const now = 'now';
47
+
48
+	/**
49
+	 * The following properties hold the default formats for date and time.
50
+	 * Defaults are set via the constructor and can be overridden on class instantiation.
51
+	 * However they can also be overridden later by the set_format() method
52
+	 * (and corresponding set_date_format, set_time_format methods);
53
+	 */
54
+	/**
55
+	 * @type string $_date_format
56
+	 */
57
+	protected $_date_format = '';
58
+
59
+	/**
60
+	 * @type string $_time_format
61
+	 */
62
+	protected $_time_format = '';
63
+
64
+	/**
65
+	 * @type string $_pretty_date_format
66
+	 */
67
+	protected $_pretty_date_format = '';
68
+
69
+	/**
70
+	 * @type string $_pretty_time_format
71
+	 */
72
+	protected $_pretty_time_format = '';
73
+
74
+	/**
75
+	 * @type DateTimeZone $_DateTimeZone
76
+	 */
77
+	protected $_DateTimeZone;
78
+
79
+	/**
80
+	 * @type DateTimeZone $_UTC_DateTimeZone
81
+	 */
82
+	protected $_UTC_DateTimeZone;
83
+
84
+	/**
85
+	 * @type DateTimeZone $_blog_DateTimeZone
86
+	 */
87
+	protected $_blog_DateTimeZone;
88
+
89
+
90
+	/**
91
+	 * This property holds how we want the output returned when getting a datetime string.  It is set for the
92
+	 * set_date_time_output() method.  By default this is empty.  When empty, we are assuming that we want both date
93
+	 * and time returned via getters.
94
+	 *
95
+	 * @var mixed (null|string)
96
+	 */
97
+	protected $_date_time_output;
98
+
99
+
100
+	/**
101
+	 * timezone string
102
+	 * This gets set by the constructor and can be changed by the "set_timezone()" method so that we know what timezone
103
+	 * incoming strings|timestamps are in.  This can also be used before a get to set what timezone you want strings
104
+	 * coming out of the object to be in.  Default timezone is the current WP timezone option setting
105
+	 *
106
+	 * @var string
107
+	 */
108
+	protected $_timezone_string;
109
+
110
+
111
+	/**
112
+	 * This holds whatever UTC offset for the blog (we automatically convert timezone strings into their related
113
+	 * offsets for comparison purposes).
114
+	 *
115
+	 * @var int
116
+	 */
117
+	protected $_blog_offset;
118
+
119
+
120
+	/**
121
+	 * @param string $table_column
122
+	 * @param string $nice_name
123
+	 * @param bool   $nullable
124
+	 * @param string $default_value
125
+	 * @param string $timezone_string
126
+	 * @param string $date_format
127
+	 * @param string $time_format
128
+	 * @param string $pretty_date_format
129
+	 * @param string $pretty_time_format
130
+	 * @throws \EE_Error
131
+	 */
132
+	public function __construct(
133
+		$table_column,
134
+		$nice_name,
135
+		$nullable,
136
+		$default_value,
137
+		$timezone_string = '',
138
+		$date_format = '',
139
+		$time_format = '',
140
+		$pretty_date_format = '',
141
+		$pretty_time_format = ''
142
+	) {
143
+
144
+		$this->_date_format        = ! empty($date_format) ? $date_format : get_option('date_format');
145
+		$this->_time_format        = ! empty($time_format) ? $time_format : get_option('time_format');
146
+		$this->_pretty_date_format = ! empty($pretty_date_format) ? $pretty_date_format : get_option('date_format');
147
+		$this->_pretty_time_format = ! empty($pretty_time_format) ? $pretty_time_format : get_option('time_format');
148
+
149
+		parent::__construct($table_column, $nice_name, $nullable, $default_value);
150
+		$this->set_timezone($timezone_string);
151
+		$this->setSchemaFormat('date-time');
152
+	}
153
+
154
+
155
+	/**
156
+	 * @return DateTimeZone
157
+	 * @throws \EE_Error
158
+	 */
159
+	public function get_UTC_DateTimeZone()
160
+	{
161
+		return $this->_UTC_DateTimeZone instanceof DateTimeZone
162
+			? $this->_UTC_DateTimeZone
163
+			: $this->_create_timezone_object_from_timezone_string('UTC');
164
+	}
165
+
166
+
167
+	/**
168
+	 * @return DateTimeZone
169
+	 * @throws \EE_Error
170
+	 */
171
+	public function get_blog_DateTimeZone()
172
+	{
173
+		return $this->_blog_DateTimeZone instanceof DateTimeZone
174
+			? $this->_blog_DateTimeZone
175
+			: $this->_create_timezone_object_from_timezone_string('');
176
+	}
177
+
178
+
179
+	/**
180
+	 * this prepares any incoming date data and make sure its converted to a utc unix timestamp
181
+	 *
182
+	 * @param  string|int $value_inputted_for_field_on_model_object could be a string formatted date time or int unix
183
+	 *                                                              timestamp
184
+	 * @return DateTime
185
+	 */
186
+	public function prepare_for_set($value_inputted_for_field_on_model_object)
187
+	{
188
+		return $this->_get_date_object($value_inputted_for_field_on_model_object);
189
+	}
190
+
191
+
192
+	/**
193
+	 * This returns the format string to be used by getters depending on what the $_date_time_output property is set at.
194
+	 * getters need to know whether we're just returning the date or the time or both.  By default we return both.
195
+	 *
196
+	 * @param bool $pretty If we're returning the pretty formats or standard format string.
197
+	 * @return string    The final assembled format string.
198
+	 */
199
+	protected function _get_date_time_output($pretty = false)
200
+	{
201
+
202
+		switch ($this->_date_time_output) {
203
+			case 'time' :
204
+				return $pretty ? $this->_pretty_time_format : $this->_time_format;
205
+				break;
206
+
207
+			case 'date' :
208
+				return $pretty ? $this->_pretty_date_format : $this->_date_format;
209
+				break;
210
+
211
+			default :
212
+				return $pretty
213
+					? $this->_pretty_date_format . ' ' . $this->_pretty_time_format
214
+					: $this->_date_format . ' ' . $this->_time_format;
215
+		}
216
+	}
217
+
218
+
219
+	/**
220
+	 * This just sets the $_date_time_output property so we can flag how date and times are formatted before being
221
+	 * returned (using the format properties)
222
+	 *
223
+	 * @param string $what acceptable values are 'time' or 'date'.
224
+	 *                     Any other value will be set but will always result
225
+	 *                     in both 'date' and 'time' being returned.
226
+	 * @return void
227
+	 */
228
+	public function set_date_time_output($what = null)
229
+	{
230
+		$this->_date_time_output = $what;
231
+	}
232
+
233
+
234
+	/**
235
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
236
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
237
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp).
238
+	 * We also set some other properties in this method.
239
+	 *
240
+	 * @param string $timezone_string A valid timezone string as described by @link
241
+	 *                                http://www.php.net/manual/en/timezones.php
242
+	 * @return void
243
+	 * @throws \EE_Error
244
+	 */
245
+	public function set_timezone($timezone_string)
246
+	{
247
+		if (empty($timezone_string) && $this->_timezone_string !== null) {
248
+			// leave the timezone AS-IS if we already have one and
249
+			// the function arg didn't provide one
250
+			return;
251
+		}
252
+		$timezone_string        = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
253
+		$this->_timezone_string = ! empty($timezone_string) ? $timezone_string : 'UTC';
254
+		$this->_DateTimeZone    = $this->_create_timezone_object_from_timezone_string($this->_timezone_string);
255
+	}
256
+
257
+
258
+	/**
259
+	 * _create_timezone_object_from_timezone_name
260
+	 *
261
+	 * @access protected
262
+	 * @param string $timezone_string
263
+	 * @return \DateTimeZone
264
+	 * @throws \EE_Error
265
+	 */
266
+	protected function _create_timezone_object_from_timezone_string($timezone_string = '')
267
+	{
268
+		return new DateTimeZone(EEH_DTT_Helper::get_valid_timezone_string($timezone_string));
269
+	}
270
+
271
+
272
+	/**
273
+	 * This just returns whatever is set for the current timezone.
274
+	 *
275
+	 * @access public
276
+	 * @return string timezone string
277
+	 */
278
+	public function get_timezone()
279
+	{
280
+		return $this->_timezone_string;
281
+	}
282
+
283
+
284
+	/**
285
+	 * set the $_date_format property
286
+	 *
287
+	 * @access public
288
+	 * @param string $format a new date format (corresponding to formats accepted by PHP date() function)
289
+	 * @param bool   $pretty Whether to set pretty format or not.
290
+	 * @return void
291
+	 */
292
+	public function set_date_format($format, $pretty = false)
293
+	{
294
+		if ($pretty) {
295
+			$this->_pretty_date_format = $format;
296
+		} else {
297
+			$this->_date_format = $format;
298
+		}
299
+	}
300
+
301
+
302
+	/**
303
+	 * return the $_date_format property value.
304
+	 *
305
+	 * @param bool $pretty Whether to get pretty format or not.
306
+	 * @return string
307
+	 */
308
+	public function get_date_format($pretty = false)
309
+	{
310
+		return $pretty ? $this->_pretty_date_format : $this->_date_format;
311
+	}
312
+
313
+
314
+	/**
315
+	 * set the $_time_format property
316
+	 *
317
+	 * @access public
318
+	 * @param string $format a new time format (corresponding to formats accepted by PHP date() function)
319
+	 * @param bool   $pretty Whether to set pretty format or not.
320
+	 * @return void
321
+	 */
322
+	public function set_time_format($format, $pretty = false)
323
+	{
324
+		if ($pretty) {
325
+			$this->_pretty_time_format = $format;
326
+		} else {
327
+			$this->_time_format = $format;
328
+		}
329
+	}
330
+
331
+
332
+	/**
333
+	 * return the $_time_format property value.
334
+	 *
335
+	 * @param bool $pretty Whether to get pretty format or not.
336
+	 * @return string
337
+	 */
338
+	public function get_time_format($pretty = false)
339
+	{
340
+		return $pretty ? $this->_pretty_time_format : $this->_time_format;
341
+	}
342
+
343
+
344
+	/**
345
+	 * set the $_pretty_date_format property
346
+	 *
347
+	 * @access public
348
+	 * @param string $format a new pretty date format (corresponding to formats accepted by PHP date() function)
349
+	 * @return void
350
+	 */
351
+	public function set_pretty_date_format($format)
352
+	{
353
+		$this->_pretty_date_format = $format;
354
+	}
355
+
356
+
357
+	/**
358
+	 * set the $_pretty_time_format property
359
+	 *
360
+	 * @access public
361
+	 * @param string $format a new pretty time format (corresponding to formats accepted by PHP date() function)
362
+	 * @return void
363
+	 */
364
+	public function set_pretty_time_format($format)
365
+	{
366
+		$this->_pretty_time_format = $format;
367
+	}
368
+
369
+
370
+	/**
371
+	 * Only sets the time portion of the datetime.
372
+	 *
373
+	 * @param string|DateTime $time_to_set_string like 8am OR a DateTime object.
374
+	 * @param DateTime        $current            current DateTime object for the datetime field
375
+	 * @return DateTime
376
+	 */
377
+	public function prepare_for_set_with_new_time($time_to_set_string, DateTime $current)
378
+	{
379
+		// if $time_to_set_string is datetime object, then let's use it to set the parse array.
380
+		// Otherwise parse the string.
381
+		if ($time_to_set_string instanceof DateTime) {
382
+			$parsed = array(
383
+				'hour'   => $time_to_set_string->format('H'),
384
+				'minute' => $time_to_set_string->format('i'),
385
+				'second' => $time_to_set_string->format('s'),
386
+			);
387
+		} else {
388
+			//parse incoming string
389
+			$parsed = date_parse_from_format($this->_time_format, $time_to_set_string);
390
+		}
391
+
392
+		//make sure $current is in the correct timezone.
393
+		$current->setTimezone($this->_DateTimeZone);
394
+
395
+		return $current->setTime($parsed['hour'], $parsed['minute'], $parsed['second']);
396
+	}
397
+
398
+
399
+	/**
400
+	 * Only sets the date portion of the datetime.
401
+	 *
402
+	 * @param string|DateTime $date_to_set_string like Friday, January 8th or a DateTime object.
403
+	 * @param DateTime        $current            current DateTime object for the datetime field
404
+	 * @return DateTime
405
+	 */
406
+	public function prepare_for_set_with_new_date($date_to_set_string, DateTime $current)
407
+	{
408
+		// if $time_to_set_string is datetime object, then let's use it to set the parse array.
409
+		// Otherwise parse the string.
410
+		if ($date_to_set_string instanceof DateTime) {
411
+			$parsed = array(
412
+				'year'  => $date_to_set_string->format('Y'),
413
+				'month' => $date_to_set_string->format('m'),
414
+				'day'   => $date_to_set_string->format('d'),
415
+			);
416
+		} else {
417
+			//parse incoming string
418
+			$parsed = date_parse_from_format($this->_date_format, $date_to_set_string);
419
+		}
420
+
421
+		//make sure $current is in the correct timezone
422
+		$current->setTimezone($this->_DateTimeZone);
423
+
424
+		return $current->setDate($parsed['year'], $parsed['month'], $parsed['day']);
425
+	}
426
+
427
+
428
+	/**
429
+	 * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0 timezone).  When the
430
+	 * datetime gets to this stage it should ALREADY be in UTC time
431
+	 *
432
+	 * @param  DateTime $DateTime
433
+	 * @return string formatted date time for given timezone
434
+	 * @throws \EE_Error
435
+	 */
436
+	public function prepare_for_get($DateTime)
437
+	{
438
+		return $this->_prepare_for_display($DateTime);
439
+	}
440
+
441
+
442
+	/**
443
+	 * This differs from prepare_for_get in that it considers whether the internal $_timezone differs
444
+	 * from the set wp timezone.  If so, then it returns the datetime string formatted via
445
+	 * _pretty_date_format, and _pretty_time_format.  However, it also appends a timezone
446
+	 * abbreviation to the date_string.
447
+	 *
448
+	 * @param mixed $DateTime
449
+	 * @param null  $schema
450
+	 * @return string
451
+	 * @throws \EE_Error
452
+	 */
453
+	public function prepare_for_pretty_echoing($DateTime, $schema = null)
454
+	{
455
+		return $this->_prepare_for_display($DateTime, $schema ? $schema : true);
456
+	}
457
+
458
+
459
+	/**
460
+	 * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
461
+	 * timezone).
462
+	 *
463
+	 * @param DateTime    $DateTime
464
+	 * @param bool|string $schema
465
+	 * @return string
466
+	 * @throws \EE_Error
467
+	 */
468
+	protected function _prepare_for_display($DateTime, $schema = false)
469
+	{
470
+		if (! $DateTime instanceof DateTime) {
471
+			if ($this->_nullable) {
472
+				return '';
473
+			} else {
474
+				if (WP_DEBUG) {
475
+					throw new EE_Error(
476
+						sprintf(
477
+							__(
478
+								'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.',
479
+								'event_espresso'
480
+							),
481
+							$this->_nicename
482
+						)
483
+					);
484
+				} else {
485
+					$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now);
486
+					EE_Error::add_error(
487
+						sprintf(
488
+							__(
489
+								'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.  When WP_DEBUG is false, the value is set to "now" instead of throwing an exception.',
490
+								'event_espresso'
491
+							),
492
+							$this->_nicename
493
+						)
494
+					);
495
+				}
496
+			}
497
+		}
498
+		$format_string = $this->_get_date_time_output($schema);
499
+		//make sure datetime_value is in the correct timezone (in case that's been updated).
500
+		$DateTime->setTimezone($this->_DateTimeZone);
501
+		if ($schema) {
502
+			if ($this->_display_timezone()) {
503
+				//must be explicit because schema could equal true.
504
+				if ($schema === 'no_html') {
505
+					$timezone_string = ' (' . $DateTime->format('T') . ')';
506
+				} else {
507
+					$timezone_string = ' <span class="ee_dtt_timezone_string">(' . $DateTime->format('T') . ')</span>';
508
+				}
509
+			} else {
510
+				$timezone_string = '';
511
+			}
512
+
513
+			return $DateTime->format($format_string) . $timezone_string;
514
+		} else {
515
+			return $DateTime->format($format_string);
516
+		}
517
+	}
518
+
519
+
520
+	/**
521
+	 * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
522
+	 * timezone).
523
+	 *
524
+	 * @param  mixed $datetime_value u
525
+	 * @return string mysql timestamp in UTC
526
+	 * @throws \EE_Error
527
+	 */
528
+	public function prepare_for_use_in_db($datetime_value)
529
+	{
530
+		//we allow an empty value or DateTime object, but nothing else.
531
+		if (! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
532
+			throw new EE_Error(
533
+				__(
534
+					'The incoming value being prepared for setting in the database must either be empty or a php DateTime object',
535
+					'event_espresso'
536
+				)
537
+			);
538
+		}
539
+
540
+		if ($datetime_value instanceof DateTime) {
541
+			if ( ! $datetime_value instanceof DbSafeDateTime) {
542
+				$datetime_value = DbSafeDateTime::createFromDateTime($datetime_value);
543
+			}
544
+
545
+			return $datetime_value->setTimezone($this->get_UTC_DateTimeZone())->format(
546
+				EE_Datetime_Field::mysql_timestamp_format
547
+			);
548
+		}
549
+
550
+		// if $datetime_value is empty, and ! $this->_nullable, use current_time() but set the GMT flag to true
551
+		return ! $this->_nullable && empty($datetime_value) ? current_time('mysql', true) : null;
552
+	}
553
+
554
+
555
+	/**
556
+	 * This prepares the datetime for internal usage as a PHP DateTime object OR null (if nullable is
557
+	 * allowed)
558
+	 *
559
+	 * @param string $datetime_string mysql timestamp in UTC
560
+	 * @return  mixed null | DateTime
561
+	 * @throws \EE_Error
562
+	 */
563
+	public function prepare_for_set_from_db($datetime_string)
564
+	{
565
+		//if $datetime_value is empty, and ! $this->_nullable, just use time()
566
+		if (empty($datetime_string) && $this->_nullable) {
567
+			return null;
568
+		}
569
+		// datetime strings from the db should ALWAYS be in UTC+0, so use UTC_DateTimeZone when creating
570
+		if (empty($datetime_string)) {
571
+			$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
572
+		} else {
573
+			$DateTime = DateTime::createFromFormat(
574
+				EE_Datetime_Field::mysql_timestamp_format,
575
+				$datetime_string,
576
+				$this->get_UTC_DateTimeZone()
577
+			);
578
+			if ($DateTime instanceof \DateTime) {
579
+				$DateTime = new DbSafeDateTime(
580
+					$DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
581
+					$this->get_UTC_DateTimeZone()
582
+				);
583
+			}
584
+		}
585
+
586
+		if (! $DateTime instanceof DbSafeDateTime) {
587
+			// if still no datetime object, then let's just use now
588
+			$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
589
+		}
590
+		// THEN apply the field's set DateTimeZone
591
+		$DateTime->setTimezone($this->_DateTimeZone);
592
+
593
+		return $DateTime;
594
+	}
595
+
596
+
597
+	/**
598
+	 * All this method does is determine if we're going to display the timezone string or not on any output.
599
+	 * To determine this we check if the set timezone offset is different than the blog's set timezone offset.
600
+	 * If so, then true.
601
+	 *
602
+	 * @return bool true for yes false for no
603
+	 * @throws \EE_Error
604
+	 */
605
+	protected function _display_timezone()
606
+	{
607
+
608
+		// first let's do a comparison of timezone strings.
609
+		// If they match then we can get out without any further calculations
610
+		$blog_string = get_option('timezone_string');
611
+		if ($blog_string === $this->_timezone_string) {
612
+			return false;
613
+		}
614
+		// now we need to calc the offset for the timezone string so we can compare with the blog offset.
615
+		$this_offset = $this->get_timezone_offset($this->_DateTimeZone);
616
+		$blog_offset = $this->get_timezone_offset($this->get_blog_DateTimeZone());
617
+		// now compare
618
+		return $blog_offset !== $this_offset;
619
+	}
620
+
621
+
622
+	/**
623
+	 * This method returns a php DateTime object for setting on the EE_Base_Class model.
624
+	 * EE passes around DateTime objects because they are MUCH easier to manipulate and deal
625
+	 * with.
626
+	 *
627
+	 * @param int|string|DateTime $date_string            This should be the incoming date string.  It's assumed to be
628
+	 *                                                    in the format that is set on the date_field (or DateTime
629
+	 *                                                    object)!
630
+	 * @return DateTime
631
+	 */
632
+	protected function _get_date_object($date_string)
633
+	{
634
+		//first if this is an empty date_string and nullable is allowed, just return null.
635
+		if ($this->_nullable && empty($date_string)) {
636
+			return null;
637
+		}
638
+
639
+		// if incoming date
640
+		if ($date_string instanceof DateTime) {
641
+			$date_string->setTimezone($this->_DateTimeZone);
642
+
643
+			return $date_string;
644
+		}
645
+		// if empty date_string and made it here.
646
+		// Return a datetime object for now in the given timezone.
647
+		if (empty($date_string)) {
648
+			return new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
649
+		}
650
+		// if $date_string is matches something that looks like a Unix timestamp let's just use it.
651
+		if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $date_string)) {
652
+			try {
653
+				// This is operating under the assumption that the incoming Unix timestamp
654
+				// is an ACTUAL Unix timestamp and not the calculated one output by current_time('timestamp');
655
+				$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
656
+				$DateTime->setTimestamp($date_string);
657
+
658
+				return $DateTime;
659
+			} catch (Exception $e) {
660
+				// should be rare, but if things got fooled then let's just continue
661
+			}
662
+		}
663
+		//not a unix timestamp.  So we will use the set format on this object and set timezone to
664
+		//create the DateTime object.
665
+		$format = $this->_date_format . ' ' . $this->_time_format;
666
+		try {
667
+			$DateTime = DateTime::createFromFormat($format, $date_string, $this->_DateTimeZone);
668
+			if ($DateTime instanceof DateTime) {
669
+				$DateTime = new DbSafeDateTime(
670
+					$DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
671
+					$this->_DateTimeZone
672
+				);
673
+			}
674
+			if (! $DateTime instanceof DbSafeDateTime) {
675
+				throw new EE_Error(
676
+					sprintf(
677
+						__('"%1$s" does not represent a valid Date Time in the format "%2$s".', 'event_espresso'),
678
+						$date_string,
679
+						$format
680
+					)
681
+				);
682
+			}
683
+		} catch (Exception $e) {
684
+			// if we made it here then likely then something went really wrong.
685
+			// Instead of throwing an exception, let's just return a DateTime object for now, in the set timezone.
686
+			$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
687
+		}
688
+
689
+		return $DateTime;
690
+	}
691
+
692
+
693
+
694
+	/**
695
+	 * get_timezone_transitions
696
+	 *
697
+	 * @param \DateTimeZone $DateTimeZone
698
+	 * @param int           $time
699
+	 * @param bool          $first_only
700
+	 * @return mixed
701
+	 */
702
+	public function get_timezone_transitions(DateTimeZone $DateTimeZone, $time = null, $first_only = true)
703
+	{
704
+		$time = is_int($time) || $time === null ? $time : strtotime($time);
705
+		$time = preg_match(EE_Datetime_Field::unix_timestamp_regex, $time) ? $time : time();
706
+		$transitions = $DateTimeZone->getTransitions($time);
707
+		return $first_only && ! isset($transitions['ts']) ? reset($transitions) : $transitions;
708
+	}
709
+
710
+
711
+
712
+	/**
713
+	 * get_timezone_offset
714
+	 *
715
+	 * @param \DateTimeZone $DateTimeZone
716
+	 * @param int           $time
717
+	 * @return mixed
718
+	 * @throws \DomainException
719
+	 */
720
+	public function get_timezone_offset(DateTimeZone $DateTimeZone, $time = null)
721
+	{
722
+		$transitions = $this->get_timezone_transitions($DateTimeZone, $time);
723
+		if ( ! isset($transitions['offset'])) {
724
+			throw new DomainException();
725
+		}
726
+		return $transitions['offset'];
727
+	}
728
+
729
+
730
+	/**
731
+	 * This will take an incoming timezone string and return the abbreviation for that timezone
732
+	 *
733
+	 * @param  string $timezone_string
734
+	 * @return string           abbreviation
735
+	 * @throws \EE_Error
736
+	 */
737
+	public function get_timezone_abbrev($timezone_string)
738
+	{
739
+		$timezone_string = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
740
+		$dateTime        = new DateTime(\EE_Datetime_Field::now, new DateTimeZone($timezone_string));
741
+
742
+		return $dateTime->format('T');
743
+	}
744
+
745
+	/**
746
+	 * Overrides the parent to allow for having a dynamic "now" value
747
+	 *
748
+	 * @return mixed
749
+	 */
750
+	public function get_default_value()
751
+	{
752
+		if ($this->_default_value === EE_Datetime_Field::now) {
753
+			return time();
754
+		} else {
755
+			return parent::get_default_value();
756
+		}
757
+	}
758
+
759
+
760
+	public function getSchemaDescription()
761
+	{
762
+		return sprintf(
763
+			esc_html__('%s - the value for this field is in the timezone of the site.', 'event_espresso'),
764
+			$this->get_nicename()
765
+		);
766
+	}
767 767
 }
Please login to merge, or discard this patch.
core/db_models/fields/EE_Email_Field.php 2 patches
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -9,48 +9,48 @@
 block discarded – undo
9 9
  */
10 10
 class EE_Email_Field extends EE_Text_Field_Base
11 11
 {
12
-    /**
13
-     * @param string $table_column
14
-     * @param string $nicename
15
-     * @param bool   $nullable
16
-     * @param null   $default_value
17
-     */
18
-    public function __construct($table_column, $nicename, $nullable, $default_value = null)
19
-    {
20
-        parent::__construct($table_column, $nicename, $nullable, $default_value);
21
-        $this->setSchemaFormat('email');
22
-    }
12
+	/**
13
+	 * @param string $table_column
14
+	 * @param string $nicename
15
+	 * @param bool   $nullable
16
+	 * @param null   $default_value
17
+	 */
18
+	public function __construct($table_column, $nicename, $nullable, $default_value = null)
19
+	{
20
+		parent::__construct($table_column, $nicename, $nullable, $default_value);
21
+		$this->setSchemaFormat('email');
22
+	}
23 23
 
24 24
 
25
-    /**
26
-     * In form inputs, we should have called htmlentities and addslashes() on form inputs,
27
-     * so we need to undo that on setting of these fields
28
-     *
29
-     * @param string $email_address
30
-     * @return string
31
-     */
32
-    function prepare_for_set($email_address)
33
-    {
34
-        $validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
35
-            ? EE_Registry::instance()->CFG->registration->email_validation_level
36
-            : 'wp_default';
37
-        if ($validation_level === 'basic' && ! preg_match('/^.+\@\S+$/', $email_address)) {
38
-            // email not even in correct {string}@{string} format
39
-            return '';
40
-        } else if ($validation_level === 'wp_default' && ! is_email($email_address)) {
41
-            //not a valid email.
42
-            return '';
43
-        } else if (
44
-            ($validation_level === 'i18n' || $validation_level === 'i18n_dns')
45
-            // plz see http://stackoverflow.com/a/24817336 re: the following regex
46
-            && ! preg_match(
47
-                '/^(?!\.)((?!.*\.{2})[a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\.!#$%&\'*+-\/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\-\.\d]+)((\.([a-zA-Z\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}]){2,63})+)$/u',
48
-                $email_address
49
-            )
50
-        ) {
51
-            //not a valid email.
52
-            return '';
53
-        }
54
-        return $email_address;
55
-    }
25
+	/**
26
+	 * In form inputs, we should have called htmlentities and addslashes() on form inputs,
27
+	 * so we need to undo that on setting of these fields
28
+	 *
29
+	 * @param string $email_address
30
+	 * @return string
31
+	 */
32
+	function prepare_for_set($email_address)
33
+	{
34
+		$validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
35
+			? EE_Registry::instance()->CFG->registration->email_validation_level
36
+			: 'wp_default';
37
+		if ($validation_level === 'basic' && ! preg_match('/^.+\@\S+$/', $email_address)) {
38
+			// email not even in correct {string}@{string} format
39
+			return '';
40
+		} else if ($validation_level === 'wp_default' && ! is_email($email_address)) {
41
+			//not a valid email.
42
+			return '';
43
+		} else if (
44
+			($validation_level === 'i18n' || $validation_level === 'i18n_dns')
45
+			// plz see http://stackoverflow.com/a/24817336 re: the following regex
46
+			&& ! preg_match(
47
+				'/^(?!\.)((?!.*\.{2})[a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\.!#$%&\'*+-\/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\-\.\d]+)((\.([a-zA-Z\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}]){2,63})+)$/u',
48
+				$email_address
49
+			)
50
+		) {
51
+			//not a valid email.
52
+			return '';
53
+		}
54
+		return $email_address;
55
+	}
56 56
 }
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@
 block discarded – undo
19 19
      * @param string $table_column
20 20
      * @param string $nicename
21 21
      * @param bool   $nullable
22
-     * @param null   $default_value
22
+     * @param string   $default_value
23 23
      */
24 24
     public function __construct($table_column, $nicename, $nullable, $default_value = null)
25 25
     {
Please login to merge, or discard this patch.
core/db_models/fields/EE_DB_Only_Float_Field.php 1 patch
Indentation   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -3,16 +3,16 @@
 block discarded – undo
3 3
 
4 4
 class EE_DB_Only_Float_Field extends EE_DB_Only_Field_Base
5 5
 {
6
-    /**
7
-     * @param string $table_column
8
-     * @param string $nicename
9
-     * @param bool   $nullable
10
-     * @param null   $default_value
11
-     */
12
-    public function __construct($table_column, $nicename, $nullable, $default_value = null)
13
-    {
14
-        parent::__construct($table_column, $nicename, $nullable, $default_value);
15
-        $this->setSchemaType('number');
16
-    }
6
+	/**
7
+	 * @param string $table_column
8
+	 * @param string $nicename
9
+	 * @param bool   $nullable
10
+	 * @param null   $default_value
11
+	 */
12
+	public function __construct($table_column, $nicename, $nullable, $default_value = null)
13
+	{
14
+		parent::__construct($table_column, $nicename, $nullable, $default_value);
15
+		$this->setSchemaType('number');
16
+	}
17 17
 
18 18
 }
Please login to merge, or discard this patch.
core/db_models/fields/EE_Primary_Key_Int_Field.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -4,27 +4,27 @@
 block discarded – undo
4 4
 class EE_Primary_Key_Int_Field extends EE_Primary_Key_Field_Base
5 5
 {
6 6
 
7
-    public function __construct($table_column, $nicename)
8
-    {
9
-        parent::__construct($table_column, $nicename, 0);
10
-        $this->setSchemaType('integer');
11
-    }
7
+	public function __construct($table_column, $nicename)
8
+	{
9
+		parent::__construct($table_column, $nicename, 0);
10
+		$this->setSchemaType('integer');
11
+	}
12 12
 
13
-    function prepare_for_set($value_inputted_for_field_on_model_object)
14
-    {
15
-        if ($this->is_model_obj_of_type_pointed_to($value_inputted_for_field_on_model_object)) {
16
-            $value_inputted_for_field_on_model_object = $value_inputted_for_field_on_model_object->ID();
17
-        }
18
-        return absint($value_inputted_for_field_on_model_object);
19
-    }
13
+	function prepare_for_set($value_inputted_for_field_on_model_object)
14
+	{
15
+		if ($this->is_model_obj_of_type_pointed_to($value_inputted_for_field_on_model_object)) {
16
+			$value_inputted_for_field_on_model_object = $value_inputted_for_field_on_model_object->ID();
17
+		}
18
+		return absint($value_inputted_for_field_on_model_object);
19
+	}
20 20
 
21
-    function prepare_for_set_from_db($value_found_in_db_for_model_object)
22
-    {
23
-        return intval($value_found_in_db_for_model_object);
24
-    }
21
+	function prepare_for_set_from_db($value_found_in_db_for_model_object)
22
+	{
23
+		return intval($value_found_in_db_for_model_object);
24
+	}
25 25
 
26
-    function is_auto_increment()
27
-    {
28
-        return true;
29
-    }
26
+	function is_auto_increment()
27
+	{
28
+		return true;
29
+	}
30 30
 }
31 31
\ No newline at end of file
Please login to merge, or discard this patch.
core/db_models/fields/EE_Enum_Text_Field.php 1 patch
Indentation   +127 added lines, -127 removed lines patch added patch discarded remove patch
@@ -12,131 +12,131 @@
 block discarded – undo
12 12
 class EE_Enum_Text_Field extends EE_Text_Field_Base
13 13
 {
14 14
 
15
-    /**
16
-     * @var array $_allowed_enum_values
17
-     */
18
-    public $_allowed_enum_values;
19
-
20
-    /**
21
-     * @param string  $table_column
22
-     * @param string  $nice_name
23
-     * @param boolean $nullable
24
-     * @param mixed   $default_value
25
-     * @param array   $allowed_enum_values keys are values to be used in the DB, values are how they should be displayed
26
-     */
27
-    function __construct($table_column, $nice_name, $nullable, $default_value, $allowed_enum_values)
28
-    {
29
-        $this->_allowed_enum_values = $allowed_enum_values;
30
-        parent::__construct($table_column, $nice_name, $nullable, $default_value);
31
-        $this->setSchemaType('object');
32
-    }
33
-
34
-
35
-
36
-    /**
37
-     * Returns the list of allowed enum options, but filterable.
38
-     * This is used internally
39
-     *
40
-     * @return array
41
-     */
42
-    protected function _allowed_enum_values()
43
-    {
44
-        return apply_filters(
45
-            'FHEE__EE_Enum_Text_Field___allowed_enum_options',
46
-            $this->_allowed_enum_values,
47
-            $this
48
-        );
49
-    }
50
-
51
-
52
-
53
-    /**
54
-     * When setting, just verify that the value being used matches what we've defined as allowable enum values.
55
-     * If not, throw an error (but if WP_DEBUG is false, just set the value to default).
56
-     *
57
-     * @param string $value_inputted_for_field_on_model_object
58
-     * @return string
59
-     * @throws EE_Error
60
-     */
61
-    public function prepare_for_set($value_inputted_for_field_on_model_object)
62
-    {
63
-        if (
64
-            $value_inputted_for_field_on_model_object !== null
65
-            && ! array_key_exists($value_inputted_for_field_on_model_object, $this->_allowed_enum_values())
66
-        ) {
67
-            if (defined('WP_DEBUG') && WP_DEBUG) {
68
-                $msg = sprintf(
69
-                    __('System is assigning incompatible value "%1$s" to field "%2$s"', 'event_espresso'),
70
-                    $value_inputted_for_field_on_model_object,
71
-                    $this->_name
72
-                );
73
-                $msg2 = sprintf(
74
-                    __('Allowed values for "%1$s" are "%2$s". You provided: "%3$s"', 'event_espresso'),
75
-                    $this->_name,
76
-                    implode(', ', array_keys($this->_allowed_enum_values())),
77
-                    $value_inputted_for_field_on_model_object
78
-                );
79
-                EE_Error::add_error("{$msg}||{$msg2}", __FILE__, __FUNCTION__, __LINE__);
80
-            }
81
-            return $this->get_default_value();
82
-        }
83
-        return $value_inputted_for_field_on_model_object;
84
-    }
85
-
86
-
87
-    /**
88
-     * Gets the pretty version of the enum's value.
89
-     *
90
-     * @param     int |string $value_on_field_to_be_outputted
91
-     * @param    null         $schema
92
-     * @return    string
93
-     */
94
-    public function prepare_for_pretty_echoing($value_on_field_to_be_outputted, $schema = null)
95
-    {
96
-        $options = $this->_allowed_enum_values();
97
-        if (isset($options[$value_on_field_to_be_outputted])) {
98
-            return $options[$value_on_field_to_be_outputted];
99
-        } else {
100
-            return $value_on_field_to_be_outputted;
101
-        }
102
-    }
103
-
104
-
105
-
106
-    /**
107
-     * When retrieving something from the DB, don't enforce the enum's options. If it's in the DB, we just have to live
108
-     * with that. Note also: when we're saving to the DB again, we also don't enforce the enum options. It's ONLY
109
-     * when we're receiving USER input from prepare_for_set() that we enforce the enum options.
110
-     *
111
-     * @param mixed $value_in_db
112
-     * @return mixed
113
-     */
114
-    public function prepare_for_set_from_db($value_in_db)
115
-    {
116
-        return $value_in_db;
117
-    }
118
-
119
-
120
-    public function getSchemaProperties()
121
-    {
122
-        return array(
123
-            'raw' => array(
124
-                'description' =>  sprintf(
125
-                    __('%s - the value in the database.', 'event_espresso'),
126
-                    $this->get_nicename()
127
-                ),
128
-                'type' => 'string',
129
-                'enum' => array_keys($this->_allowed_enum_values)
130
-            ),
131
-            'pretty' => array(
132
-                'description' =>  sprintf(
133
-                    __('%s - the value for display.', 'event_espresso'),
134
-                    $this->get_nicename()
135
-                ),
136
-                'type' => 'string',
137
-                'enum' => array_values($this->_allowed_enum_values),
138
-                'read_only' => true
139
-            )
140
-        );
141
-    }
15
+	/**
16
+	 * @var array $_allowed_enum_values
17
+	 */
18
+	public $_allowed_enum_values;
19
+
20
+	/**
21
+	 * @param string  $table_column
22
+	 * @param string  $nice_name
23
+	 * @param boolean $nullable
24
+	 * @param mixed   $default_value
25
+	 * @param array   $allowed_enum_values keys are values to be used in the DB, values are how they should be displayed
26
+	 */
27
+	function __construct($table_column, $nice_name, $nullable, $default_value, $allowed_enum_values)
28
+	{
29
+		$this->_allowed_enum_values = $allowed_enum_values;
30
+		parent::__construct($table_column, $nice_name, $nullable, $default_value);
31
+		$this->setSchemaType('object');
32
+	}
33
+
34
+
35
+
36
+	/**
37
+	 * Returns the list of allowed enum options, but filterable.
38
+	 * This is used internally
39
+	 *
40
+	 * @return array
41
+	 */
42
+	protected function _allowed_enum_values()
43
+	{
44
+		return apply_filters(
45
+			'FHEE__EE_Enum_Text_Field___allowed_enum_options',
46
+			$this->_allowed_enum_values,
47
+			$this
48
+		);
49
+	}
50
+
51
+
52
+
53
+	/**
54
+	 * When setting, just verify that the value being used matches what we've defined as allowable enum values.
55
+	 * If not, throw an error (but if WP_DEBUG is false, just set the value to default).
56
+	 *
57
+	 * @param string $value_inputted_for_field_on_model_object
58
+	 * @return string
59
+	 * @throws EE_Error
60
+	 */
61
+	public function prepare_for_set($value_inputted_for_field_on_model_object)
62
+	{
63
+		if (
64
+			$value_inputted_for_field_on_model_object !== null
65
+			&& ! array_key_exists($value_inputted_for_field_on_model_object, $this->_allowed_enum_values())
66
+		) {
67
+			if (defined('WP_DEBUG') && WP_DEBUG) {
68
+				$msg = sprintf(
69
+					__('System is assigning incompatible value "%1$s" to field "%2$s"', 'event_espresso'),
70
+					$value_inputted_for_field_on_model_object,
71
+					$this->_name
72
+				);
73
+				$msg2 = sprintf(
74
+					__('Allowed values for "%1$s" are "%2$s". You provided: "%3$s"', 'event_espresso'),
75
+					$this->_name,
76
+					implode(', ', array_keys($this->_allowed_enum_values())),
77
+					$value_inputted_for_field_on_model_object
78
+				);
79
+				EE_Error::add_error("{$msg}||{$msg2}", __FILE__, __FUNCTION__, __LINE__);
80
+			}
81
+			return $this->get_default_value();
82
+		}
83
+		return $value_inputted_for_field_on_model_object;
84
+	}
85
+
86
+
87
+	/**
88
+	 * Gets the pretty version of the enum's value.
89
+	 *
90
+	 * @param     int |string $value_on_field_to_be_outputted
91
+	 * @param    null         $schema
92
+	 * @return    string
93
+	 */
94
+	public function prepare_for_pretty_echoing($value_on_field_to_be_outputted, $schema = null)
95
+	{
96
+		$options = $this->_allowed_enum_values();
97
+		if (isset($options[$value_on_field_to_be_outputted])) {
98
+			return $options[$value_on_field_to_be_outputted];
99
+		} else {
100
+			return $value_on_field_to_be_outputted;
101
+		}
102
+	}
103
+
104
+
105
+
106
+	/**
107
+	 * When retrieving something from the DB, don't enforce the enum's options. If it's in the DB, we just have to live
108
+	 * with that. Note also: when we're saving to the DB again, we also don't enforce the enum options. It's ONLY
109
+	 * when we're receiving USER input from prepare_for_set() that we enforce the enum options.
110
+	 *
111
+	 * @param mixed $value_in_db
112
+	 * @return mixed
113
+	 */
114
+	public function prepare_for_set_from_db($value_in_db)
115
+	{
116
+		return $value_in_db;
117
+	}
118
+
119
+
120
+	public function getSchemaProperties()
121
+	{
122
+		return array(
123
+			'raw' => array(
124
+				'description' =>  sprintf(
125
+					__('%s - the value in the database.', 'event_espresso'),
126
+					$this->get_nicename()
127
+				),
128
+				'type' => 'string',
129
+				'enum' => array_keys($this->_allowed_enum_values)
130
+			),
131
+			'pretty' => array(
132
+				'description' =>  sprintf(
133
+					__('%s - the value for display.', 'event_espresso'),
134
+					$this->get_nicename()
135
+				),
136
+				'type' => 'string',
137
+				'enum' => array_values($this->_allowed_enum_values),
138
+				'read_only' => true
139
+			)
140
+		);
141
+	}
142 142
 }
Please login to merge, or discard this patch.
modules/ticket_selector/ProcessTicketSelector.php 3 patches
Doc Comments   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -32,7 +32,7 @@  discard block
 block discarded – undo
32 32
     /**
33 33
      * cancelTicketSelections
34 34
      *
35
-     * @return        string
35
+     * @return        false|null
36 36
      */
37 37
     public function cancelTicketSelections()
38 38
     {
@@ -99,7 +99,7 @@  discard block
 block discarded – undo
99 99
     /**
100 100
      * process_ticket_selections
101 101
      *
102
-     * @return array|bool
102
+     * @return boolean|null
103 103
      * @throws \EE_Error
104 104
      */
105 105
     public function processTicketSelections()
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
      * validate_post_data
274 274
      *
275 275
      * @param int $id
276
-     * @return array|FALSE
276
+     * @return string
277 277
      */
278 278
     private function validatePostData($id = 0)
279 279
     {
@@ -390,7 +390,7 @@  discard block
 block discarded – undo
390 390
      *
391 391
      * @param \EE_Ticket $ticket
392 392
      * @param int        $qty
393
-     * @return TRUE on success, FALSE on fail
393
+     * @return boolean on success, FALSE on fail
394 394
      * @throws \EE_Error
395 395
      */
396 396
     private function addTicketToCart(\EE_Ticket $ticket = null, $qty = 1)
Please login to merge, or discard this patch.
Indentation   +568 added lines, -568 removed lines patch added patch discarded remove patch
@@ -2,7 +2,7 @@  discard block
 block discarded – undo
2 2
 namespace EventEspresso\modules\ticket_selector;
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
 
@@ -19,573 +19,573 @@  discard block
 block discarded – undo
19 19
 class ProcessTicketSelector
20 20
 {
21 21
 
22
-    /**
23
-     * array of datetimes and the spaces available for them
24
-     *
25
-     * @access private
26
-     * @var array
27
-     */
28
-    private static $_available_spaces = array();
29
-
30
-
31
-
32
-    /**
33
-     * cancelTicketSelections
34
-     *
35
-     * @return        string
36
-     */
37
-    public function cancelTicketSelections()
38
-    {
39
-        // check nonce
40
-        if ( ! $this->processTicketSelectorNonce('cancel_ticket_selections')) {
41
-            return false;
42
-        }
43
-        \EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
44
-        if (\EE_Registry::instance()->REQ->is_set('event_id')) {
45
-            wp_safe_redirect(
46
-                \EEH_Event_View::event_link_url(
47
-                    \EE_Registry::instance()->REQ->get('event_id')
48
-                )
49
-            );
50
-        } else {
51
-            wp_safe_redirect(
52
-                site_url('/' . \EE_Registry::instance()->CFG->core->event_cpt_slug . '/')
53
-            );
54
-        }
55
-        exit();
56
-    }
57
-
58
-
59
-    /**
60
-     * processTicketSelectorNonce
61
-     *
62
-     * @param  string $nonce_name
63
-     * @param string  $id
64
-     * @return bool
65
-     */
66
-    private function processTicketSelectorNonce($nonce_name, $id = '')
67
-    {
68
-        $nonce_name_with_id = ! empty($id) ? "{$nonce_name}_nonce_{$id}" : "{$nonce_name}_nonce";
69
-        if (
70
-            ! is_admin()
71
-            && (
72
-                ! \EE_Registry::instance()->REQ->is_set($nonce_name_with_id)
73
-                || ! wp_verify_nonce(
74
-                    \EE_Registry::instance()->REQ->get($nonce_name_with_id),
75
-                    $nonce_name
76
-                )
77
-            )
78
-        ) {
79
-            \EE_Error::add_error(
80
-                sprintf(
81
-                    __(
82
-                        'We\'re sorry but your request failed to pass a security check.%sPlease click the back button on your browser and try again.',
83
-                        'event_espresso'
84
-                    ),
85
-                    '<br/>'
86
-                ),
87
-                __FILE__,
88
-                __FUNCTION__,
89
-                __LINE__
90
-            );
91
-            return false;
92
-        }
93
-        return true;
94
-    }
95
-
96
-
97
-
98
-    /**
99
-     * process_ticket_selections
100
-     *
101
-     * @return array|bool
102
-     * @throws \EE_Error
103
-     */
104
-    public function processTicketSelections()
105
-    {
106
-        do_action( 'EED_Ticket_Selector__process_ticket_selections__before' );
107
-        // do we have an event id?
108
-        if ( ! \EE_Registry::instance()->REQ->is_set( 'tkt-slctr-event-id' ) ) {
109
-            // $_POST['tkt-slctr-event-id'] was not set ?!?!?!?
110
-            \EE_Error::add_error(
111
-                sprintf(
112
-                    __(
113
-                        'An event id was not provided or was not received.%sPlease click the back button on your browser and try again.',
114
-                        'event_espresso'
115
-                    ),
116
-                    '<br/>'
117
-                ),
118
-                __FILE__,
119
-                __FUNCTION__,
120
-                __LINE__
121
-            );
122
-        }
123
-        //if event id is valid
124
-        $id = absint( \EE_Registry::instance()->REQ->get( 'tkt-slctr-event-id' ) );
125
-        // check nonce
126
-        if ( ! $this->processTicketSelectorNonce('process_ticket_selections', $id)) {
127
-            return false;
128
-        }
129
-        //		d( \EE_Registry::instance()->REQ );
130
-        self::$_available_spaces = array(
131
-            'tickets'   => array(),
132
-            'datetimes' => array(),
133
-        );
134
-        //we should really only have 1 registration in the works now (ie, no MER) so clear any previous items in the cart.
135
-        // When MER happens this will probably need to be tweaked, possibly wrapped in a conditional checking for some constant defined in MER etc.
136
-        \EE_Registry::instance()->load_core( 'Session' );
137
-        // unless otherwise requested, clear the session
138
-        if ( apply_filters( 'FHEE__EE_Ticket_Selector__process_ticket_selections__clear_session', true ) ) {
139
-            \EE_Registry::instance()->SSN->clear_session( __CLASS__, __FUNCTION__ );
140
-        }
141
-        //d( \EE_Registry::instance()->SSN );
142
-        do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
143
-        // validate/sanitize data
144
-        $valid = $this->validatePostData( $id );
145
-        //EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
146
-        //EEH_Debug_Tools::printr( $valid, '$valid', __FILE__, __LINE__ );
147
-        //EEH_Debug_Tools::printr( $valid[ 'total_tickets' ], 'total_tickets', __FILE__, __LINE__ );
148
-        //EEH_Debug_Tools::printr( $valid[ 'max_atndz' ], 'max_atndz', __FILE__, __LINE__ );
149
-        //check total tickets ordered vs max number of attendees that can register
150
-        if ( $valid[ 'total_tickets' ] > $valid[ 'max_atndz' ] ) {
151
-            // ordering too many tickets !!!
152
-            $total_tickets_string = _n(
153
-                'You have attempted to purchase %s ticket.',
154
-                'You have attempted to purchase %s tickets.',
155
-                $valid[ 'total_tickets' ],
156
-                'event_espresso'
157
-            );
158
-            $limit_error_1 = sprintf( $total_tickets_string, $valid[ 'total_tickets' ] );
159
-            // dev only message
160
-            $max_atndz_string = _n(
161
-                'The registration limit for this event is %s ticket per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
162
-                'The registration limit for this event is %s tickets per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
163
-                $valid[ 'max_atndz' ],
164
-                'event_espresso'
165
-            );
166
-            $limit_error_2 = sprintf( $max_atndz_string, $valid[ 'max_atndz' ], $valid[ 'max_atndz' ] );
167
-            \EE_Error::add_error( $limit_error_1 . '<br/>' . $limit_error_2, __FILE__, __FUNCTION__, __LINE__ );
168
-        } else {
169
-            // all data appears to be valid
170
-            $tckts_slctd = false;
171
-            $tickets_added = 0;
172
-            $valid = apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data', $valid);
173
-            if ($valid['total_tickets'] > 0) {
174
-                // load cart
175
-               \EE_Registry::instance()->load_core( 'Cart' );
176
-                // cycle thru the number of data rows sent from the event listing
177
-                for ( $x = 0; $x < $valid[ 'rows' ]; $x++ ) {
178
-                    // does this row actually contain a ticket quantity?
179
-                    if ( isset( $valid['qty'][$x] ) && $valid['qty'][$x] > 0 ) {
180
-                        // YES we have a ticket quantity
181
-                        $tckts_slctd = true;
182
-                        //						d( $valid['ticket_obj'][$x] );
183
-                        if ( $valid['ticket_obj'][$x] instanceof \EE_Ticket ) {
184
-                            // then add ticket to cart
185
-                            $tickets_added += $this->addTicketToCart(
186
-                                $valid[ 'ticket_obj' ][ $x ],
187
-                                $valid[ 'qty' ][ $x ]
188
-                            );
189
-                            if ( \EE_Error::has_error() ) {
190
-                                break;
191
-                            }
192
-                        } else {
193
-                            // nothing added to cart retrieved
194
-                            \EE_Error::add_error(
195
-                                sprintf(
196
-                                    __(
197
-                                        'A valid ticket could not be retrieved for the event.%sPlease click the back button on your browser and try again.',
198
-                                        'event_espresso'
199
-                                    ),
200
-                                    '<br/>'
201
-                                ),
202
-                                __FILE__, __FUNCTION__, __LINE__
203
-                            );
204
-                        }
205
-                    }
206
-                }
207
-            }
208
-            do_action(
209
-                'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
210
-                \EE_Registry::instance()->CART,
211
-                $this
212
-            );
213
-            //d( \EE_Registry::instance()->CART );
214
-            //die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL REDIRECT HERE BEFORE CART UPDATE
215
-            if (apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', $tckts_slctd)) {
216
-                if (apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__success', $tickets_added)) {
217
-                    do_action(
218
-                        'FHEE__EE_Ticket_Selector__process_ticket_selections__before_redirecting_to_checkout',
219
-                        \EE_Registry::instance()->CART,
220
-                        $this
221
-                    );
222
-                    \EE_Registry::instance()->CART->recalculate_all_cart_totals();
223
-                    \EE_Registry::instance()->CART->save_cart( false );
224
-                    // exit('KILL REDIRECT AFTER CART UPDATE'); // <<<<<<<<  OR HERE TO KILL REDIRECT AFTER CART UPDATE
225
-                    // just return TRUE for registrations being made from admin
226
-                    if ( is_admin() ) {
227
-                        return true;
228
-                    }
229
-                    \EE_Error::get_notices(false, true);
230
-                    wp_safe_redirect(
231
-                        apply_filters(
232
-                            'FHEE__EE_Ticket_Selector__process_ticket_selections__success_redirect_url',
233
-                            \EE_Registry::instance()->CFG->core->reg_page_url()
234
-                        )
235
-                    );
236
-                    exit();
237
-                } else {
238
-                    if ( ! \EE_Error::has_error() && ! \EE_Error::has_error(true, 'attention')) {
239
-                        // nothing added to cart
240
-                        \EE_Error::add_attention( __( 'No tickets were added for the event', 'event_espresso' ),
241
-                                                  __FILE__, __FUNCTION__, __LINE__ );
242
-                    }
243
-                }
244
-            } else {
245
-                // no ticket quantities were selected
246
-                \EE_Error::add_error( __( 'You need to select a ticket quantity before you can proceed.',
247
-                                          'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
248
-            }
249
-        }
250
-        //die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL BEFORE REDIRECT
251
-        // at this point, just return if registration is being made from admin
252
-        if ( is_admin() ) {
253
-            return false;
254
-        }
255
-        if ( $valid[ 'return_url' ] ) {
256
-            \EE_Error::get_notices( false, true );
257
-            wp_safe_redirect( $valid[ 'return_url' ] );
258
-            exit();
259
-        } elseif ( isset( $event_to_add[ 'id' ] ) ) {
260
-            \EE_Error::get_notices( false, true );
261
-            wp_safe_redirect( get_permalink( $event_to_add[ 'id' ] ) );
262
-            exit();
263
-        } else {
264
-            echo \EE_Error::get_notices();
265
-        }
266
-        return false;
267
-    }
268
-
269
-
270
-
271
-    /**
272
-     * validate_post_data
273
-     *
274
-     * @param int $id
275
-     * @return array|FALSE
276
-     */
277
-    private function validatePostData( $id = 0 )
278
-    {
279
-        do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
280
-        if ( ! $id ) {
281
-            \EE_Error::add_error(
282
-                __( 'The event id provided was not valid.', 'event_espresso' ),
283
-                __FILE__,
284
-                __FUNCTION__,
285
-                __LINE__
286
-            );
287
-            return false;
288
-        }
289
-        // start with an empty array()
290
-        $valid_data = array();
291
-        // grab valid id
292
-        $valid_data[ 'id' ] = $id;
293
-        // array of other form names
294
-        $inputs_to_clean = array(
295
-            'event_id'   => 'tkt-slctr-event-id',
296
-            'max_atndz'  => 'tkt-slctr-max-atndz-',
297
-            'rows'       => 'tkt-slctr-rows-',
298
-            'qty'        => 'tkt-slctr-qty-',
299
-            'ticket_id'  => 'tkt-slctr-ticket-id-',
300
-            'return_url' => 'tkt-slctr-return-url-',
301
-        );
302
-        // let's track the total number of tickets ordered.'
303
-        $valid_data[ 'total_tickets' ] = 0;
304
-        // cycle through $inputs_to_clean array
305
-        foreach ( $inputs_to_clean as $what => $input_to_clean ) {
306
-            // check for POST data
307
-            if ( \EE_Registry::instance()->REQ->is_set( $input_to_clean . $id ) ) {
308
-                // grab value
309
-                $input_value = \EE_Registry::instance()->REQ->get( $input_to_clean . $id );
310
-                switch ( $what ) {
311
-                    // integers
312
-                    case 'event_id':
313
-                        $valid_data[ $what ] = absint( $input_value );
314
-                        // get event via the event id we put in the form
315
-                        $valid_data[ 'event' ] = \EE_Registry::instance()
316
-                                                             ->load_model( 'Event' )
317
-                                                             ->get_one_by_ID( $valid_data[ 'event_id' ] );
318
-                        break;
319
-                    case 'rows':
320
-                    case 'max_atndz':
321
-                        $valid_data[ $what ] = absint( $input_value );
322
-                        break;
323
-                    // arrays of integers
324
-                    case 'qty':
325
-                        /** @var array $row_qty */
326
-                        $row_qty = $input_value;
327
-                        // if qty is coming from a radio button input, then we need to assemble an array of rows
328
-                        if ( ! is_array( $row_qty ) ) {
329
-                            // get number of rows
330
-                            $rows = \EE_Registry::instance()->REQ->is_set( 'tkt-slctr-rows-' . $id )
331
-                                ? absint( \EE_Registry::instance()->REQ->get( 'tkt-slctr-rows-' . $id ) )
332
-                                : 1;
333
-                            // explode ints by the dash
334
-                            $row_qty = explode( '-', $row_qty );
335
-                            $row = isset( $row_qty[ 0 ] ) ? absint( $row_qty[ 0 ] ) : 1;
336
-                            $qty = isset( $row_qty[ 1 ] ) ? absint( $row_qty[ 1 ] ) : 0;
337
-                            $row_qty = array( $row => $qty );
338
-                            for ( $x = 1; $x <= $rows; $x++ ) {
339
-                                if ( ! isset( $row_qty[ $x ] ) ) {
340
-                                    $row_qty[ $x ] = 0;
341
-                                }
342
-                            }
343
-                        }
344
-                        ksort( $row_qty );
345
-                        // cycle thru values
346
-                        foreach ( $row_qty as $qty ) {
347
-                            $qty = absint( $qty );
348
-                            // sanitize as integers
349
-                            $valid_data[ $what ][] = $qty;
350
-                            $valid_data[ 'total_tickets' ] += $qty;
351
-                        }
352
-                        break;
353
-                    // array of integers
354
-                    case 'ticket_id':
355
-                        $value_array = array();
356
-                        // cycle thru values
357
-                        foreach ( (array)$input_value as $key => $value ) {
358
-                            // allow only numbers, letters,  spaces, commas and dashes
359
-                            $value_array[ $key ] = wp_strip_all_tags( $value );
360
-                            // get ticket via the ticket id we put in the form
361
-                            $ticket_obj = \EE_Registry::instance()->load_model( 'Ticket' )->get_one_by_ID( $value );
362
-                            $valid_data[ 'ticket_obj' ][ $key ] = $ticket_obj;
363
-                        }
364
-                        $valid_data[ $what ] = $value_array;
365
-                        break;
366
-                    case 'return_url' :
367
-                        // grab and sanitize return-url
368
-                        $input_value = esc_url_raw( $input_value );
369
-                        // was the request coming from an iframe ? if so, then:
370
-                        if ( strpos($input_value, 'event_list=iframe')) {
371
-                            // get anchor fragment
372
-                            $input_value = explode('#', $input_value);
373
-                            $input_value = end($input_value);
374
-                            // use event list url instead, but append anchor
375
-                            $input_value = \EEH_Event_View::event_archive_url() . '#' . $input_value;
376
-                        }
377
-                        $valid_data[$what] = $input_value;
378
-                        break;
379
-                }    // end switch $what
380
-            }
381
-        }    // end foreach $inputs_to_clean
382
-        return $valid_data;
383
-    }
384
-
385
-
386
-
387
-    /**
388
-     * adds a ticket to the cart
389
-     *
390
-     * @param \EE_Ticket $ticket
391
-     * @param int        $qty
392
-     * @return TRUE on success, FALSE on fail
393
-     * @throws \EE_Error
394
-     */
395
-    private function addTicketToCart( \EE_Ticket $ticket = null, $qty = 1 )
396
-    {
397
-        do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
398
-        // get the number of spaces left for this datetime ticket
399
-        $available_spaces = $this->ticketDatetimeAvailability( $ticket );
400
-        // compare available spaces against the number of tickets being purchased
401
-        if ( $available_spaces >= $qty ) {
402
-            // allow addons to prevent a ticket from being added to cart
403
-            if (
404
-                ! apply_filters(
405
-                    'FHEE__EE_Ticket_Selector___add_ticket_to_cart__allow_add_to_cart',
406
-                    true,
407
-                    $ticket,
408
-                    $qty,
409
-                    $available_spaces
410
-                )
411
-            ) {
412
-                return false;
413
-            }
414
-            $qty = absint(apply_filters('FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty', $qty, $ticket));
415
-            // add event to cart
416
-            if ( \EE_Registry::instance()->CART->add_ticket_to_cart( $ticket, $qty ) ) {
417
-                $this->recalculateTicketDatetimeAvailability( $ticket, $qty );
418
-                return true;
419
-            }
420
-            return false;
421
-        }
422
-        // tickets can not be purchased but let's find the exact number left
423
-        // for the last ticket selected PRIOR to subtracting tickets
424
-        $available_spaces = $this->ticketDatetimeAvailability( $ticket, true );
425
-        // greedy greedy greedy eh?
426
-        if ( $available_spaces > 0 ) {
427
-            if (
428
-            apply_filters(
429
-                'FHEE__EE_Ticket_Selector___add_ticket_to_cart__allow_display_availability_error',
430
-                true,
431
-                $ticket,
432
-                $qty,
433
-                $available_spaces
434
-            )
435
-            ) {
436
-                $this->displayAvailabilityError($available_spaces);
437
-            }
438
-        } else {
439
-            \EE_Error::add_error(
440
-                __(
441
-                    'We\'re sorry, but there are no available spaces left for this event at this particular date and time.',
442
-                    'event_espresso'
443
-                ),
444
-                __FILE__, __FUNCTION__, __LINE__
445
-            );
446
-        }
447
-        return false;
448
-    }
449
-
450
-
451
-
452
-    /**
453
-     * @param int $available_spaces
454
-     * @throws \EE_Error
455
-     */
456
-    private function displayAvailabilityError($available_spaces = 1)
457
-    {
458
-        // add error messaging - we're using the _n function that will generate
459
-        // the appropriate singular or plural message based on the number of $available_spaces
460
-        if (\EE_Registry::instance()->CART->all_ticket_quantity_count()) {
461
-            $msg = sprintf(
462
-                _n(
463
-                    'We\'re sorry, but there is only %1$s available space left for this event at this particular date and time. Please select a different number (or different combination) of tickets by cancelling the current selection and choosing again, or proceed to registration.',
464
-                    'We\'re sorry, but there are only %1$s available spaces left for this event at this particular date and time. Please select a different number (or different combination) of tickets by cancelling the current selection and choosing again, or proceed to registration.',
465
-                    $available_spaces,
466
-                    'event_espresso'
467
-                ),
468
-                $available_spaces,
469
-                '<br />'
470
-            );
471
-        } else {
472
-            $msg = sprintf(
473
-                _n(
474
-                    'We\'re sorry, but there is only %1$s available space left for this event at this particular date and time. Please select a different number (or different combination) of tickets.',
475
-                    'We\'re sorry, but there are only %1$s available spaces left for this event at this particular date and time. Please select a different number (or different combination) of tickets.',
476
-                    $available_spaces,
477
-                    'event_espresso'
478
-                ),
479
-                $available_spaces,
480
-                '<br />'
481
-            );
482
-        }
483
-        \EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
484
-    }
485
-
486
-
487
-
488
-    /**
489
-     * ticketDatetimeAvailability
490
-     * creates an array of tickets plus all of the datetimes available to each ticket
491
-     * and tracks the spaces remaining for each of those datetimes
492
-     *
493
-     * @param \EE_Ticket $ticket - selected ticket
494
-     * @param bool       $get_original_ticket_spaces
495
-     * @return int
496
-     * @throws \EE_Error
497
-     */
498
-    private function ticketDatetimeAvailability( \EE_Ticket $ticket, $get_original_ticket_spaces = false )
499
-    {
500
-        // if the $_available_spaces array has not been set up yet...
501
-        if ( ! isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
502
-            $this->setInitialTicketDatetimeAvailability( $ticket );
503
-        }
504
-        $available_spaces = $ticket->qty() - $ticket->sold();
505
-        if ( isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
506
-            // loop thru tickets, which will ALSO include individual ticket records AND a total
507
-            foreach ( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] as $DTD_ID => $spaces ) {
508
-                // if we want the original datetime availability BEFORE we started subtracting tickets ?
509
-                if ( $get_original_ticket_spaces ) {
510
-                    // then grab the available spaces from the "tickets" array
511
-                    // and compare with the above to get the lowest number
512
-                    $available_spaces = min(
513
-                        $available_spaces,
514
-                        self::$_available_spaces[ 'tickets' ][ $ticket->ID() ][ $DTD_ID ]
515
-                    );
516
-                } else {
517
-                    // we want the updated ticket availability as stored in the "datetimes" array
518
-                    $available_spaces = min( $available_spaces, self::$_available_spaces[ 'datetimes' ][ $DTD_ID ] );
519
-                }
520
-            }
521
-        }
522
-        return $available_spaces;
523
-    }
524
-
525
-
526
-
527
-    /**
528
-     * @param \EE_Ticket $ticket
529
-     * @return void
530
-     * @throws \EE_Error
531
-     */
532
-    private function setInitialTicketDatetimeAvailability( \EE_Ticket $ticket )
533
-    {
534
-        // first, get all of the datetimes that are available to this ticket
535
-        $datetimes = $ticket->get_many_related(
536
-            'Datetime',
537
-            array(
538
-                array(
539
-                    'DTT_EVT_end' => array(
540
-                        '>=',
541
-                        \EEM_Datetime::instance()->current_time_for_query( 'DTT_EVT_end' ),
542
-                    ),
543
-                ),
544
-                'order_by' => array( 'DTT_EVT_start' => 'ASC' ),
545
-            )
546
-        );
547
-        if ( ! empty( $datetimes ) ) {
548
-            // now loop thru all of the datetimes
549
-            foreach ( $datetimes as $datetime ) {
550
-                if ( $datetime instanceof \EE_Datetime ) {
551
-                    // the number of spaces available for the datetime without considering individual ticket quantities
552
-                    $spaces_remaining = $datetime->spaces_remaining();
553
-                    // save the total available spaces ( the lesser of the ticket qty minus the number of tickets sold
554
-                    // or the datetime spaces remaining) to this ticket using the datetime ID as the key
555
-                    self::$_available_spaces[ 'tickets' ][ $ticket->ID() ][ $datetime->ID() ] = min(
556
-                        $ticket->qty() - $ticket->sold(),
557
-                        $spaces_remaining
558
-                    );
559
-                    // if the remaining spaces for this datetime is already set,
560
-                    // then compare that against the datetime spaces remaining, and take the lowest number,
561
-                    // else just take the datetime spaces remaining, and assign to the datetimes array
562
-                    self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ] = isset(
563
-                        self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ]
564
-                    )
565
-                        ? min( self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ], $spaces_remaining )
566
-                        : $spaces_remaining;
567
-                }
568
-            }
569
-        }
570
-    }
571
-
572
-
573
-
574
-    /**
575
-     * @param    \EE_Ticket $ticket
576
-     * @param    int        $qty
577
-     * @return    void
578
-     */
579
-    private function recalculateTicketDatetimeAvailability( \EE_Ticket $ticket, $qty = 0 )
580
-    {
581
-        if ( isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
582
-            // loop thru tickets, which will ALSO include individual ticket records AND a total
583
-            foreach ( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] as $DTD_ID => $spaces ) {
584
-                // subtract the qty of selected tickets from each datetime's available spaces this ticket has access to,
585
-                self::$_available_spaces[ 'datetimes' ][ $DTD_ID ] -= $qty;
586
-            }
587
-        }
588
-    }
22
+	/**
23
+	 * array of datetimes and the spaces available for them
24
+	 *
25
+	 * @access private
26
+	 * @var array
27
+	 */
28
+	private static $_available_spaces = array();
29
+
30
+
31
+
32
+	/**
33
+	 * cancelTicketSelections
34
+	 *
35
+	 * @return        string
36
+	 */
37
+	public function cancelTicketSelections()
38
+	{
39
+		// check nonce
40
+		if ( ! $this->processTicketSelectorNonce('cancel_ticket_selections')) {
41
+			return false;
42
+		}
43
+		\EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
44
+		if (\EE_Registry::instance()->REQ->is_set('event_id')) {
45
+			wp_safe_redirect(
46
+				\EEH_Event_View::event_link_url(
47
+					\EE_Registry::instance()->REQ->get('event_id')
48
+				)
49
+			);
50
+		} else {
51
+			wp_safe_redirect(
52
+				site_url('/' . \EE_Registry::instance()->CFG->core->event_cpt_slug . '/')
53
+			);
54
+		}
55
+		exit();
56
+	}
57
+
58
+
59
+	/**
60
+	 * processTicketSelectorNonce
61
+	 *
62
+	 * @param  string $nonce_name
63
+	 * @param string  $id
64
+	 * @return bool
65
+	 */
66
+	private function processTicketSelectorNonce($nonce_name, $id = '')
67
+	{
68
+		$nonce_name_with_id = ! empty($id) ? "{$nonce_name}_nonce_{$id}" : "{$nonce_name}_nonce";
69
+		if (
70
+			! is_admin()
71
+			&& (
72
+				! \EE_Registry::instance()->REQ->is_set($nonce_name_with_id)
73
+				|| ! wp_verify_nonce(
74
+					\EE_Registry::instance()->REQ->get($nonce_name_with_id),
75
+					$nonce_name
76
+				)
77
+			)
78
+		) {
79
+			\EE_Error::add_error(
80
+				sprintf(
81
+					__(
82
+						'We\'re sorry but your request failed to pass a security check.%sPlease click the back button on your browser and try again.',
83
+						'event_espresso'
84
+					),
85
+					'<br/>'
86
+				),
87
+				__FILE__,
88
+				__FUNCTION__,
89
+				__LINE__
90
+			);
91
+			return false;
92
+		}
93
+		return true;
94
+	}
95
+
96
+
97
+
98
+	/**
99
+	 * process_ticket_selections
100
+	 *
101
+	 * @return array|bool
102
+	 * @throws \EE_Error
103
+	 */
104
+	public function processTicketSelections()
105
+	{
106
+		do_action( 'EED_Ticket_Selector__process_ticket_selections__before' );
107
+		// do we have an event id?
108
+		if ( ! \EE_Registry::instance()->REQ->is_set( 'tkt-slctr-event-id' ) ) {
109
+			// $_POST['tkt-slctr-event-id'] was not set ?!?!?!?
110
+			\EE_Error::add_error(
111
+				sprintf(
112
+					__(
113
+						'An event id was not provided or was not received.%sPlease click the back button on your browser and try again.',
114
+						'event_espresso'
115
+					),
116
+					'<br/>'
117
+				),
118
+				__FILE__,
119
+				__FUNCTION__,
120
+				__LINE__
121
+			);
122
+		}
123
+		//if event id is valid
124
+		$id = absint( \EE_Registry::instance()->REQ->get( 'tkt-slctr-event-id' ) );
125
+		// check nonce
126
+		if ( ! $this->processTicketSelectorNonce('process_ticket_selections', $id)) {
127
+			return false;
128
+		}
129
+		//		d( \EE_Registry::instance()->REQ );
130
+		self::$_available_spaces = array(
131
+			'tickets'   => array(),
132
+			'datetimes' => array(),
133
+		);
134
+		//we should really only have 1 registration in the works now (ie, no MER) so clear any previous items in the cart.
135
+		// When MER happens this will probably need to be tweaked, possibly wrapped in a conditional checking for some constant defined in MER etc.
136
+		\EE_Registry::instance()->load_core( 'Session' );
137
+		// unless otherwise requested, clear the session
138
+		if ( apply_filters( 'FHEE__EE_Ticket_Selector__process_ticket_selections__clear_session', true ) ) {
139
+			\EE_Registry::instance()->SSN->clear_session( __CLASS__, __FUNCTION__ );
140
+		}
141
+		//d( \EE_Registry::instance()->SSN );
142
+		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
143
+		// validate/sanitize data
144
+		$valid = $this->validatePostData( $id );
145
+		//EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
146
+		//EEH_Debug_Tools::printr( $valid, '$valid', __FILE__, __LINE__ );
147
+		//EEH_Debug_Tools::printr( $valid[ 'total_tickets' ], 'total_tickets', __FILE__, __LINE__ );
148
+		//EEH_Debug_Tools::printr( $valid[ 'max_atndz' ], 'max_atndz', __FILE__, __LINE__ );
149
+		//check total tickets ordered vs max number of attendees that can register
150
+		if ( $valid[ 'total_tickets' ] > $valid[ 'max_atndz' ] ) {
151
+			// ordering too many tickets !!!
152
+			$total_tickets_string = _n(
153
+				'You have attempted to purchase %s ticket.',
154
+				'You have attempted to purchase %s tickets.',
155
+				$valid[ 'total_tickets' ],
156
+				'event_espresso'
157
+			);
158
+			$limit_error_1 = sprintf( $total_tickets_string, $valid[ 'total_tickets' ] );
159
+			// dev only message
160
+			$max_atndz_string = _n(
161
+				'The registration limit for this event is %s ticket per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
162
+				'The registration limit for this event is %s tickets per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
163
+				$valid[ 'max_atndz' ],
164
+				'event_espresso'
165
+			);
166
+			$limit_error_2 = sprintf( $max_atndz_string, $valid[ 'max_atndz' ], $valid[ 'max_atndz' ] );
167
+			\EE_Error::add_error( $limit_error_1 . '<br/>' . $limit_error_2, __FILE__, __FUNCTION__, __LINE__ );
168
+		} else {
169
+			// all data appears to be valid
170
+			$tckts_slctd = false;
171
+			$tickets_added = 0;
172
+			$valid = apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data', $valid);
173
+			if ($valid['total_tickets'] > 0) {
174
+				// load cart
175
+			   \EE_Registry::instance()->load_core( 'Cart' );
176
+				// cycle thru the number of data rows sent from the event listing
177
+				for ( $x = 0; $x < $valid[ 'rows' ]; $x++ ) {
178
+					// does this row actually contain a ticket quantity?
179
+					if ( isset( $valid['qty'][$x] ) && $valid['qty'][$x] > 0 ) {
180
+						// YES we have a ticket quantity
181
+						$tckts_slctd = true;
182
+						//						d( $valid['ticket_obj'][$x] );
183
+						if ( $valid['ticket_obj'][$x] instanceof \EE_Ticket ) {
184
+							// then add ticket to cart
185
+							$tickets_added += $this->addTicketToCart(
186
+								$valid[ 'ticket_obj' ][ $x ],
187
+								$valid[ 'qty' ][ $x ]
188
+							);
189
+							if ( \EE_Error::has_error() ) {
190
+								break;
191
+							}
192
+						} else {
193
+							// nothing added to cart retrieved
194
+							\EE_Error::add_error(
195
+								sprintf(
196
+									__(
197
+										'A valid ticket could not be retrieved for the event.%sPlease click the back button on your browser and try again.',
198
+										'event_espresso'
199
+									),
200
+									'<br/>'
201
+								),
202
+								__FILE__, __FUNCTION__, __LINE__
203
+							);
204
+						}
205
+					}
206
+				}
207
+			}
208
+			do_action(
209
+				'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
210
+				\EE_Registry::instance()->CART,
211
+				$this
212
+			);
213
+			//d( \EE_Registry::instance()->CART );
214
+			//die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL REDIRECT HERE BEFORE CART UPDATE
215
+			if (apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', $tckts_slctd)) {
216
+				if (apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__success', $tickets_added)) {
217
+					do_action(
218
+						'FHEE__EE_Ticket_Selector__process_ticket_selections__before_redirecting_to_checkout',
219
+						\EE_Registry::instance()->CART,
220
+						$this
221
+					);
222
+					\EE_Registry::instance()->CART->recalculate_all_cart_totals();
223
+					\EE_Registry::instance()->CART->save_cart( false );
224
+					// exit('KILL REDIRECT AFTER CART UPDATE'); // <<<<<<<<  OR HERE TO KILL REDIRECT AFTER CART UPDATE
225
+					// just return TRUE for registrations being made from admin
226
+					if ( is_admin() ) {
227
+						return true;
228
+					}
229
+					\EE_Error::get_notices(false, true);
230
+					wp_safe_redirect(
231
+						apply_filters(
232
+							'FHEE__EE_Ticket_Selector__process_ticket_selections__success_redirect_url',
233
+							\EE_Registry::instance()->CFG->core->reg_page_url()
234
+						)
235
+					);
236
+					exit();
237
+				} else {
238
+					if ( ! \EE_Error::has_error() && ! \EE_Error::has_error(true, 'attention')) {
239
+						// nothing added to cart
240
+						\EE_Error::add_attention( __( 'No tickets were added for the event', 'event_espresso' ),
241
+												  __FILE__, __FUNCTION__, __LINE__ );
242
+					}
243
+				}
244
+			} else {
245
+				// no ticket quantities were selected
246
+				\EE_Error::add_error( __( 'You need to select a ticket quantity before you can proceed.',
247
+										  'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
248
+			}
249
+		}
250
+		//die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL BEFORE REDIRECT
251
+		// at this point, just return if registration is being made from admin
252
+		if ( is_admin() ) {
253
+			return false;
254
+		}
255
+		if ( $valid[ 'return_url' ] ) {
256
+			\EE_Error::get_notices( false, true );
257
+			wp_safe_redirect( $valid[ 'return_url' ] );
258
+			exit();
259
+		} elseif ( isset( $event_to_add[ 'id' ] ) ) {
260
+			\EE_Error::get_notices( false, true );
261
+			wp_safe_redirect( get_permalink( $event_to_add[ 'id' ] ) );
262
+			exit();
263
+		} else {
264
+			echo \EE_Error::get_notices();
265
+		}
266
+		return false;
267
+	}
268
+
269
+
270
+
271
+	/**
272
+	 * validate_post_data
273
+	 *
274
+	 * @param int $id
275
+	 * @return array|FALSE
276
+	 */
277
+	private function validatePostData( $id = 0 )
278
+	{
279
+		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
280
+		if ( ! $id ) {
281
+			\EE_Error::add_error(
282
+				__( 'The event id provided was not valid.', 'event_espresso' ),
283
+				__FILE__,
284
+				__FUNCTION__,
285
+				__LINE__
286
+			);
287
+			return false;
288
+		}
289
+		// start with an empty array()
290
+		$valid_data = array();
291
+		// grab valid id
292
+		$valid_data[ 'id' ] = $id;
293
+		// array of other form names
294
+		$inputs_to_clean = array(
295
+			'event_id'   => 'tkt-slctr-event-id',
296
+			'max_atndz'  => 'tkt-slctr-max-atndz-',
297
+			'rows'       => 'tkt-slctr-rows-',
298
+			'qty'        => 'tkt-slctr-qty-',
299
+			'ticket_id'  => 'tkt-slctr-ticket-id-',
300
+			'return_url' => 'tkt-slctr-return-url-',
301
+		);
302
+		// let's track the total number of tickets ordered.'
303
+		$valid_data[ 'total_tickets' ] = 0;
304
+		// cycle through $inputs_to_clean array
305
+		foreach ( $inputs_to_clean as $what => $input_to_clean ) {
306
+			// check for POST data
307
+			if ( \EE_Registry::instance()->REQ->is_set( $input_to_clean . $id ) ) {
308
+				// grab value
309
+				$input_value = \EE_Registry::instance()->REQ->get( $input_to_clean . $id );
310
+				switch ( $what ) {
311
+					// integers
312
+					case 'event_id':
313
+						$valid_data[ $what ] = absint( $input_value );
314
+						// get event via the event id we put in the form
315
+						$valid_data[ 'event' ] = \EE_Registry::instance()
316
+															 ->load_model( 'Event' )
317
+															 ->get_one_by_ID( $valid_data[ 'event_id' ] );
318
+						break;
319
+					case 'rows':
320
+					case 'max_atndz':
321
+						$valid_data[ $what ] = absint( $input_value );
322
+						break;
323
+					// arrays of integers
324
+					case 'qty':
325
+						/** @var array $row_qty */
326
+						$row_qty = $input_value;
327
+						// if qty is coming from a radio button input, then we need to assemble an array of rows
328
+						if ( ! is_array( $row_qty ) ) {
329
+							// get number of rows
330
+							$rows = \EE_Registry::instance()->REQ->is_set( 'tkt-slctr-rows-' . $id )
331
+								? absint( \EE_Registry::instance()->REQ->get( 'tkt-slctr-rows-' . $id ) )
332
+								: 1;
333
+							// explode ints by the dash
334
+							$row_qty = explode( '-', $row_qty );
335
+							$row = isset( $row_qty[ 0 ] ) ? absint( $row_qty[ 0 ] ) : 1;
336
+							$qty = isset( $row_qty[ 1 ] ) ? absint( $row_qty[ 1 ] ) : 0;
337
+							$row_qty = array( $row => $qty );
338
+							for ( $x = 1; $x <= $rows; $x++ ) {
339
+								if ( ! isset( $row_qty[ $x ] ) ) {
340
+									$row_qty[ $x ] = 0;
341
+								}
342
+							}
343
+						}
344
+						ksort( $row_qty );
345
+						// cycle thru values
346
+						foreach ( $row_qty as $qty ) {
347
+							$qty = absint( $qty );
348
+							// sanitize as integers
349
+							$valid_data[ $what ][] = $qty;
350
+							$valid_data[ 'total_tickets' ] += $qty;
351
+						}
352
+						break;
353
+					// array of integers
354
+					case 'ticket_id':
355
+						$value_array = array();
356
+						// cycle thru values
357
+						foreach ( (array)$input_value as $key => $value ) {
358
+							// allow only numbers, letters,  spaces, commas and dashes
359
+							$value_array[ $key ] = wp_strip_all_tags( $value );
360
+							// get ticket via the ticket id we put in the form
361
+							$ticket_obj = \EE_Registry::instance()->load_model( 'Ticket' )->get_one_by_ID( $value );
362
+							$valid_data[ 'ticket_obj' ][ $key ] = $ticket_obj;
363
+						}
364
+						$valid_data[ $what ] = $value_array;
365
+						break;
366
+					case 'return_url' :
367
+						// grab and sanitize return-url
368
+						$input_value = esc_url_raw( $input_value );
369
+						// was the request coming from an iframe ? if so, then:
370
+						if ( strpos($input_value, 'event_list=iframe')) {
371
+							// get anchor fragment
372
+							$input_value = explode('#', $input_value);
373
+							$input_value = end($input_value);
374
+							// use event list url instead, but append anchor
375
+							$input_value = \EEH_Event_View::event_archive_url() . '#' . $input_value;
376
+						}
377
+						$valid_data[$what] = $input_value;
378
+						break;
379
+				}    // end switch $what
380
+			}
381
+		}    // end foreach $inputs_to_clean
382
+		return $valid_data;
383
+	}
384
+
385
+
386
+
387
+	/**
388
+	 * adds a ticket to the cart
389
+	 *
390
+	 * @param \EE_Ticket $ticket
391
+	 * @param int        $qty
392
+	 * @return TRUE on success, FALSE on fail
393
+	 * @throws \EE_Error
394
+	 */
395
+	private function addTicketToCart( \EE_Ticket $ticket = null, $qty = 1 )
396
+	{
397
+		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
398
+		// get the number of spaces left for this datetime ticket
399
+		$available_spaces = $this->ticketDatetimeAvailability( $ticket );
400
+		// compare available spaces against the number of tickets being purchased
401
+		if ( $available_spaces >= $qty ) {
402
+			// allow addons to prevent a ticket from being added to cart
403
+			if (
404
+				! apply_filters(
405
+					'FHEE__EE_Ticket_Selector___add_ticket_to_cart__allow_add_to_cart',
406
+					true,
407
+					$ticket,
408
+					$qty,
409
+					$available_spaces
410
+				)
411
+			) {
412
+				return false;
413
+			}
414
+			$qty = absint(apply_filters('FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty', $qty, $ticket));
415
+			// add event to cart
416
+			if ( \EE_Registry::instance()->CART->add_ticket_to_cart( $ticket, $qty ) ) {
417
+				$this->recalculateTicketDatetimeAvailability( $ticket, $qty );
418
+				return true;
419
+			}
420
+			return false;
421
+		}
422
+		// tickets can not be purchased but let's find the exact number left
423
+		// for the last ticket selected PRIOR to subtracting tickets
424
+		$available_spaces = $this->ticketDatetimeAvailability( $ticket, true );
425
+		// greedy greedy greedy eh?
426
+		if ( $available_spaces > 0 ) {
427
+			if (
428
+			apply_filters(
429
+				'FHEE__EE_Ticket_Selector___add_ticket_to_cart__allow_display_availability_error',
430
+				true,
431
+				$ticket,
432
+				$qty,
433
+				$available_spaces
434
+			)
435
+			) {
436
+				$this->displayAvailabilityError($available_spaces);
437
+			}
438
+		} else {
439
+			\EE_Error::add_error(
440
+				__(
441
+					'We\'re sorry, but there are no available spaces left for this event at this particular date and time.',
442
+					'event_espresso'
443
+				),
444
+				__FILE__, __FUNCTION__, __LINE__
445
+			);
446
+		}
447
+		return false;
448
+	}
449
+
450
+
451
+
452
+	/**
453
+	 * @param int $available_spaces
454
+	 * @throws \EE_Error
455
+	 */
456
+	private function displayAvailabilityError($available_spaces = 1)
457
+	{
458
+		// add error messaging - we're using the _n function that will generate
459
+		// the appropriate singular or plural message based on the number of $available_spaces
460
+		if (\EE_Registry::instance()->CART->all_ticket_quantity_count()) {
461
+			$msg = sprintf(
462
+				_n(
463
+					'We\'re sorry, but there is only %1$s available space left for this event at this particular date and time. Please select a different number (or different combination) of tickets by cancelling the current selection and choosing again, or proceed to registration.',
464
+					'We\'re sorry, but there are only %1$s available spaces left for this event at this particular date and time. Please select a different number (or different combination) of tickets by cancelling the current selection and choosing again, or proceed to registration.',
465
+					$available_spaces,
466
+					'event_espresso'
467
+				),
468
+				$available_spaces,
469
+				'<br />'
470
+			);
471
+		} else {
472
+			$msg = sprintf(
473
+				_n(
474
+					'We\'re sorry, but there is only %1$s available space left for this event at this particular date and time. Please select a different number (or different combination) of tickets.',
475
+					'We\'re sorry, but there are only %1$s available spaces left for this event at this particular date and time. Please select a different number (or different combination) of tickets.',
476
+					$available_spaces,
477
+					'event_espresso'
478
+				),
479
+				$available_spaces,
480
+				'<br />'
481
+			);
482
+		}
483
+		\EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
484
+	}
485
+
486
+
487
+
488
+	/**
489
+	 * ticketDatetimeAvailability
490
+	 * creates an array of tickets plus all of the datetimes available to each ticket
491
+	 * and tracks the spaces remaining for each of those datetimes
492
+	 *
493
+	 * @param \EE_Ticket $ticket - selected ticket
494
+	 * @param bool       $get_original_ticket_spaces
495
+	 * @return int
496
+	 * @throws \EE_Error
497
+	 */
498
+	private function ticketDatetimeAvailability( \EE_Ticket $ticket, $get_original_ticket_spaces = false )
499
+	{
500
+		// if the $_available_spaces array has not been set up yet...
501
+		if ( ! isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
502
+			$this->setInitialTicketDatetimeAvailability( $ticket );
503
+		}
504
+		$available_spaces = $ticket->qty() - $ticket->sold();
505
+		if ( isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
506
+			// loop thru tickets, which will ALSO include individual ticket records AND a total
507
+			foreach ( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] as $DTD_ID => $spaces ) {
508
+				// if we want the original datetime availability BEFORE we started subtracting tickets ?
509
+				if ( $get_original_ticket_spaces ) {
510
+					// then grab the available spaces from the "tickets" array
511
+					// and compare with the above to get the lowest number
512
+					$available_spaces = min(
513
+						$available_spaces,
514
+						self::$_available_spaces[ 'tickets' ][ $ticket->ID() ][ $DTD_ID ]
515
+					);
516
+				} else {
517
+					// we want the updated ticket availability as stored in the "datetimes" array
518
+					$available_spaces = min( $available_spaces, self::$_available_spaces[ 'datetimes' ][ $DTD_ID ] );
519
+				}
520
+			}
521
+		}
522
+		return $available_spaces;
523
+	}
524
+
525
+
526
+
527
+	/**
528
+	 * @param \EE_Ticket $ticket
529
+	 * @return void
530
+	 * @throws \EE_Error
531
+	 */
532
+	private function setInitialTicketDatetimeAvailability( \EE_Ticket $ticket )
533
+	{
534
+		// first, get all of the datetimes that are available to this ticket
535
+		$datetimes = $ticket->get_many_related(
536
+			'Datetime',
537
+			array(
538
+				array(
539
+					'DTT_EVT_end' => array(
540
+						'>=',
541
+						\EEM_Datetime::instance()->current_time_for_query( 'DTT_EVT_end' ),
542
+					),
543
+				),
544
+				'order_by' => array( 'DTT_EVT_start' => 'ASC' ),
545
+			)
546
+		);
547
+		if ( ! empty( $datetimes ) ) {
548
+			// now loop thru all of the datetimes
549
+			foreach ( $datetimes as $datetime ) {
550
+				if ( $datetime instanceof \EE_Datetime ) {
551
+					// the number of spaces available for the datetime without considering individual ticket quantities
552
+					$spaces_remaining = $datetime->spaces_remaining();
553
+					// save the total available spaces ( the lesser of the ticket qty minus the number of tickets sold
554
+					// or the datetime spaces remaining) to this ticket using the datetime ID as the key
555
+					self::$_available_spaces[ 'tickets' ][ $ticket->ID() ][ $datetime->ID() ] = min(
556
+						$ticket->qty() - $ticket->sold(),
557
+						$spaces_remaining
558
+					);
559
+					// if the remaining spaces for this datetime is already set,
560
+					// then compare that against the datetime spaces remaining, and take the lowest number,
561
+					// else just take the datetime spaces remaining, and assign to the datetimes array
562
+					self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ] = isset(
563
+						self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ]
564
+					)
565
+						? min( self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ], $spaces_remaining )
566
+						: $spaces_remaining;
567
+				}
568
+			}
569
+		}
570
+	}
571
+
572
+
573
+
574
+	/**
575
+	 * @param    \EE_Ticket $ticket
576
+	 * @param    int        $qty
577
+	 * @return    void
578
+	 */
579
+	private function recalculateTicketDatetimeAvailability( \EE_Ticket $ticket, $qty = 0 )
580
+	{
581
+		if ( isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
582
+			// loop thru tickets, which will ALSO include individual ticket records AND a total
583
+			foreach ( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] as $DTD_ID => $spaces ) {
584
+				// subtract the qty of selected tickets from each datetime's available spaces this ticket has access to,
585
+				self::$_available_spaces[ 'datetimes' ][ $DTD_ID ] -= $qty;
586
+			}
587
+		}
588
+	}
589 589
 
590 590
 
591 591
 }
Please login to merge, or discard this patch.
Spacing   +105 added lines, -105 removed lines patch added patch discarded remove patch
@@ -1,8 +1,8 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 namespace EventEspresso\modules\ticket_selector;
3 3
 
4
-if ( ! defined( 'EVENT_ESPRESSO_VERSION' ) ) {
5
-    exit( 'No direct script access allowed' );
4
+if ( ! defined('EVENT_ESPRESSO_VERSION')) {
5
+    exit('No direct script access allowed');
6 6
 }
7 7
 
8 8
 
@@ -49,7 +49,7 @@  discard block
 block discarded – undo
49 49
             );
50 50
         } else {
51 51
             wp_safe_redirect(
52
-                site_url('/' . \EE_Registry::instance()->CFG->core->event_cpt_slug . '/')
52
+                site_url('/'.\EE_Registry::instance()->CFG->core->event_cpt_slug.'/')
53 53
             );
54 54
         }
55 55
         exit();
@@ -103,9 +103,9 @@  discard block
 block discarded – undo
103 103
      */
104 104
     public function processTicketSelections()
105 105
     {
106
-        do_action( 'EED_Ticket_Selector__process_ticket_selections__before' );
106
+        do_action('EED_Ticket_Selector__process_ticket_selections__before');
107 107
         // do we have an event id?
108
-        if ( ! \EE_Registry::instance()->REQ->is_set( 'tkt-slctr-event-id' ) ) {
108
+        if ( ! \EE_Registry::instance()->REQ->is_set('tkt-slctr-event-id')) {
109 109
             // $_POST['tkt-slctr-event-id'] was not set ?!?!?!?
110 110
             \EE_Error::add_error(
111 111
                 sprintf(
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
             );
122 122
         }
123 123
         //if event id is valid
124
-        $id = absint( \EE_Registry::instance()->REQ->get( 'tkt-slctr-event-id' ) );
124
+        $id = absint(\EE_Registry::instance()->REQ->get('tkt-slctr-event-id'));
125 125
         // check nonce
126 126
         if ( ! $this->processTicketSelectorNonce('process_ticket_selections', $id)) {
127 127
             return false;
@@ -133,38 +133,38 @@  discard block
 block discarded – undo
133 133
         );
134 134
         //we should really only have 1 registration in the works now (ie, no MER) so clear any previous items in the cart.
135 135
         // When MER happens this will probably need to be tweaked, possibly wrapped in a conditional checking for some constant defined in MER etc.
136
-        \EE_Registry::instance()->load_core( 'Session' );
136
+        \EE_Registry::instance()->load_core('Session');
137 137
         // unless otherwise requested, clear the session
138
-        if ( apply_filters( 'FHEE__EE_Ticket_Selector__process_ticket_selections__clear_session', true ) ) {
139
-            \EE_Registry::instance()->SSN->clear_session( __CLASS__, __FUNCTION__ );
138
+        if (apply_filters('FHEE__EE_Ticket_Selector__process_ticket_selections__clear_session', true)) {
139
+            \EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
140 140
         }
141 141
         //d( \EE_Registry::instance()->SSN );
142
-        do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
142
+        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
143 143
         // validate/sanitize data
144
-        $valid = $this->validatePostData( $id );
144
+        $valid = $this->validatePostData($id);
145 145
         //EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
146 146
         //EEH_Debug_Tools::printr( $valid, '$valid', __FILE__, __LINE__ );
147 147
         //EEH_Debug_Tools::printr( $valid[ 'total_tickets' ], 'total_tickets', __FILE__, __LINE__ );
148 148
         //EEH_Debug_Tools::printr( $valid[ 'max_atndz' ], 'max_atndz', __FILE__, __LINE__ );
149 149
         //check total tickets ordered vs max number of attendees that can register
150
-        if ( $valid[ 'total_tickets' ] > $valid[ 'max_atndz' ] ) {
150
+        if ($valid['total_tickets'] > $valid['max_atndz']) {
151 151
             // ordering too many tickets !!!
152 152
             $total_tickets_string = _n(
153 153
                 'You have attempted to purchase %s ticket.',
154 154
                 'You have attempted to purchase %s tickets.',
155
-                $valid[ 'total_tickets' ],
155
+                $valid['total_tickets'],
156 156
                 'event_espresso'
157 157
             );
158
-            $limit_error_1 = sprintf( $total_tickets_string, $valid[ 'total_tickets' ] );
158
+            $limit_error_1 = sprintf($total_tickets_string, $valid['total_tickets']);
159 159
             // dev only message
160 160
             $max_atndz_string = _n(
161 161
                 'The registration limit for this event is %s ticket per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
162 162
                 'The registration limit for this event is %s tickets per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
163
-                $valid[ 'max_atndz' ],
163
+                $valid['max_atndz'],
164 164
                 'event_espresso'
165 165
             );
166
-            $limit_error_2 = sprintf( $max_atndz_string, $valid[ 'max_atndz' ], $valid[ 'max_atndz' ] );
167
-            \EE_Error::add_error( $limit_error_1 . '<br/>' . $limit_error_2, __FILE__, __FUNCTION__, __LINE__ );
166
+            $limit_error_2 = sprintf($max_atndz_string, $valid['max_atndz'], $valid['max_atndz']);
167
+            \EE_Error::add_error($limit_error_1.'<br/>'.$limit_error_2, __FILE__, __FUNCTION__, __LINE__);
168 168
         } else {
169 169
             // all data appears to be valid
170 170
             $tckts_slctd = false;
@@ -172,21 +172,21 @@  discard block
 block discarded – undo
172 172
             $valid = apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data', $valid);
173 173
             if ($valid['total_tickets'] > 0) {
174 174
                 // load cart
175
-               \EE_Registry::instance()->load_core( 'Cart' );
175
+               \EE_Registry::instance()->load_core('Cart');
176 176
                 // cycle thru the number of data rows sent from the event listing
177
-                for ( $x = 0; $x < $valid[ 'rows' ]; $x++ ) {
177
+                for ($x = 0; $x < $valid['rows']; $x++) {
178 178
                     // does this row actually contain a ticket quantity?
179
-                    if ( isset( $valid['qty'][$x] ) && $valid['qty'][$x] > 0 ) {
179
+                    if (isset($valid['qty'][$x]) && $valid['qty'][$x] > 0) {
180 180
                         // YES we have a ticket quantity
181 181
                         $tckts_slctd = true;
182 182
                         //						d( $valid['ticket_obj'][$x] );
183
-                        if ( $valid['ticket_obj'][$x] instanceof \EE_Ticket ) {
183
+                        if ($valid['ticket_obj'][$x] instanceof \EE_Ticket) {
184 184
                             // then add ticket to cart
185 185
                             $tickets_added += $this->addTicketToCart(
186
-                                $valid[ 'ticket_obj' ][ $x ],
187
-                                $valid[ 'qty' ][ $x ]
186
+                                $valid['ticket_obj'][$x],
187
+                                $valid['qty'][$x]
188 188
                             );
189
-                            if ( \EE_Error::has_error() ) {
189
+                            if (\EE_Error::has_error()) {
190 190
                                 break;
191 191
                             }
192 192
                         } else {
@@ -220,10 +220,10 @@  discard block
 block discarded – undo
220 220
                         $this
221 221
                     );
222 222
                     \EE_Registry::instance()->CART->recalculate_all_cart_totals();
223
-                    \EE_Registry::instance()->CART->save_cart( false );
223
+                    \EE_Registry::instance()->CART->save_cart(false);
224 224
                     // exit('KILL REDIRECT AFTER CART UPDATE'); // <<<<<<<<  OR HERE TO KILL REDIRECT AFTER CART UPDATE
225 225
                     // just return TRUE for registrations being made from admin
226
-                    if ( is_admin() ) {
226
+                    if (is_admin()) {
227 227
                         return true;
228 228
                     }
229 229
                     \EE_Error::get_notices(false, true);
@@ -237,28 +237,28 @@  discard block
 block discarded – undo
237 237
                 } else {
238 238
                     if ( ! \EE_Error::has_error() && ! \EE_Error::has_error(true, 'attention')) {
239 239
                         // nothing added to cart
240
-                        \EE_Error::add_attention( __( 'No tickets were added for the event', 'event_espresso' ),
241
-                                                  __FILE__, __FUNCTION__, __LINE__ );
240
+                        \EE_Error::add_attention(__('No tickets were added for the event', 'event_espresso'),
241
+                                                  __FILE__, __FUNCTION__, __LINE__);
242 242
                     }
243 243
                 }
244 244
             } else {
245 245
                 // no ticket quantities were selected
246
-                \EE_Error::add_error( __( 'You need to select a ticket quantity before you can proceed.',
247
-                                          'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
246
+                \EE_Error::add_error(__('You need to select a ticket quantity before you can proceed.',
247
+                                          'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
248 248
             }
249 249
         }
250 250
         //die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL BEFORE REDIRECT
251 251
         // at this point, just return if registration is being made from admin
252
-        if ( is_admin() ) {
252
+        if (is_admin()) {
253 253
             return false;
254 254
         }
255
-        if ( $valid[ 'return_url' ] ) {
256
-            \EE_Error::get_notices( false, true );
257
-            wp_safe_redirect( $valid[ 'return_url' ] );
255
+        if ($valid['return_url']) {
256
+            \EE_Error::get_notices(false, true);
257
+            wp_safe_redirect($valid['return_url']);
258 258
             exit();
259
-        } elseif ( isset( $event_to_add[ 'id' ] ) ) {
260
-            \EE_Error::get_notices( false, true );
261
-            wp_safe_redirect( get_permalink( $event_to_add[ 'id' ] ) );
259
+        } elseif (isset($event_to_add['id'])) {
260
+            \EE_Error::get_notices(false, true);
261
+            wp_safe_redirect(get_permalink($event_to_add['id']));
262 262
             exit();
263 263
         } else {
264 264
             echo \EE_Error::get_notices();
@@ -274,12 +274,12 @@  discard block
 block discarded – undo
274 274
      * @param int $id
275 275
      * @return array|FALSE
276 276
      */
277
-    private function validatePostData( $id = 0 )
277
+    private function validatePostData($id = 0)
278 278
     {
279
-        do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
280
-        if ( ! $id ) {
279
+        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
280
+        if ( ! $id) {
281 281
             \EE_Error::add_error(
282
-                __( 'The event id provided was not valid.', 'event_espresso' ),
282
+                __('The event id provided was not valid.', 'event_espresso'),
283 283
                 __FILE__,
284 284
                 __FUNCTION__,
285 285
                 __LINE__
@@ -289,7 +289,7 @@  discard block
 block discarded – undo
289 289
         // start with an empty array()
290 290
         $valid_data = array();
291 291
         // grab valid id
292
-        $valid_data[ 'id' ] = $id;
292
+        $valid_data['id'] = $id;
293 293
         // array of other form names
294 294
         $inputs_to_clean = array(
295 295
             'event_id'   => 'tkt-slctr-event-id',
@@ -300,79 +300,79 @@  discard block
 block discarded – undo
300 300
             'return_url' => 'tkt-slctr-return-url-',
301 301
         );
302 302
         // let's track the total number of tickets ordered.'
303
-        $valid_data[ 'total_tickets' ] = 0;
303
+        $valid_data['total_tickets'] = 0;
304 304
         // cycle through $inputs_to_clean array
305
-        foreach ( $inputs_to_clean as $what => $input_to_clean ) {
305
+        foreach ($inputs_to_clean as $what => $input_to_clean) {
306 306
             // check for POST data
307
-            if ( \EE_Registry::instance()->REQ->is_set( $input_to_clean . $id ) ) {
307
+            if (\EE_Registry::instance()->REQ->is_set($input_to_clean.$id)) {
308 308
                 // grab value
309
-                $input_value = \EE_Registry::instance()->REQ->get( $input_to_clean . $id );
310
-                switch ( $what ) {
309
+                $input_value = \EE_Registry::instance()->REQ->get($input_to_clean.$id);
310
+                switch ($what) {
311 311
                     // integers
312 312
                     case 'event_id':
313
-                        $valid_data[ $what ] = absint( $input_value );
313
+                        $valid_data[$what] = absint($input_value);
314 314
                         // get event via the event id we put in the form
315
-                        $valid_data[ 'event' ] = \EE_Registry::instance()
316
-                                                             ->load_model( 'Event' )
317
-                                                             ->get_one_by_ID( $valid_data[ 'event_id' ] );
315
+                        $valid_data['event'] = \EE_Registry::instance()
316
+                                                             ->load_model('Event')
317
+                                                             ->get_one_by_ID($valid_data['event_id']);
318 318
                         break;
319 319
                     case 'rows':
320 320
                     case 'max_atndz':
321
-                        $valid_data[ $what ] = absint( $input_value );
321
+                        $valid_data[$what] = absint($input_value);
322 322
                         break;
323 323
                     // arrays of integers
324 324
                     case 'qty':
325 325
                         /** @var array $row_qty */
326 326
                         $row_qty = $input_value;
327 327
                         // if qty is coming from a radio button input, then we need to assemble an array of rows
328
-                        if ( ! is_array( $row_qty ) ) {
328
+                        if ( ! is_array($row_qty)) {
329 329
                             // get number of rows
330
-                            $rows = \EE_Registry::instance()->REQ->is_set( 'tkt-slctr-rows-' . $id )
331
-                                ? absint( \EE_Registry::instance()->REQ->get( 'tkt-slctr-rows-' . $id ) )
330
+                            $rows = \EE_Registry::instance()->REQ->is_set('tkt-slctr-rows-'.$id)
331
+                                ? absint(\EE_Registry::instance()->REQ->get('tkt-slctr-rows-'.$id))
332 332
                                 : 1;
333 333
                             // explode ints by the dash
334
-                            $row_qty = explode( '-', $row_qty );
335
-                            $row = isset( $row_qty[ 0 ] ) ? absint( $row_qty[ 0 ] ) : 1;
336
-                            $qty = isset( $row_qty[ 1 ] ) ? absint( $row_qty[ 1 ] ) : 0;
337
-                            $row_qty = array( $row => $qty );
338
-                            for ( $x = 1; $x <= $rows; $x++ ) {
339
-                                if ( ! isset( $row_qty[ $x ] ) ) {
340
-                                    $row_qty[ $x ] = 0;
334
+                            $row_qty = explode('-', $row_qty);
335
+                            $row = isset($row_qty[0]) ? absint($row_qty[0]) : 1;
336
+                            $qty = isset($row_qty[1]) ? absint($row_qty[1]) : 0;
337
+                            $row_qty = array($row => $qty);
338
+                            for ($x = 1; $x <= $rows; $x++) {
339
+                                if ( ! isset($row_qty[$x])) {
340
+                                    $row_qty[$x] = 0;
341 341
                                 }
342 342
                             }
343 343
                         }
344
-                        ksort( $row_qty );
344
+                        ksort($row_qty);
345 345
                         // cycle thru values
346
-                        foreach ( $row_qty as $qty ) {
347
-                            $qty = absint( $qty );
346
+                        foreach ($row_qty as $qty) {
347
+                            $qty = absint($qty);
348 348
                             // sanitize as integers
349
-                            $valid_data[ $what ][] = $qty;
350
-                            $valid_data[ 'total_tickets' ] += $qty;
349
+                            $valid_data[$what][] = $qty;
350
+                            $valid_data['total_tickets'] += $qty;
351 351
                         }
352 352
                         break;
353 353
                     // array of integers
354 354
                     case 'ticket_id':
355 355
                         $value_array = array();
356 356
                         // cycle thru values
357
-                        foreach ( (array)$input_value as $key => $value ) {
357
+                        foreach ((array) $input_value as $key => $value) {
358 358
                             // allow only numbers, letters,  spaces, commas and dashes
359
-                            $value_array[ $key ] = wp_strip_all_tags( $value );
359
+                            $value_array[$key] = wp_strip_all_tags($value);
360 360
                             // get ticket via the ticket id we put in the form
361
-                            $ticket_obj = \EE_Registry::instance()->load_model( 'Ticket' )->get_one_by_ID( $value );
362
-                            $valid_data[ 'ticket_obj' ][ $key ] = $ticket_obj;
361
+                            $ticket_obj = \EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($value);
362
+                            $valid_data['ticket_obj'][$key] = $ticket_obj;
363 363
                         }
364
-                        $valid_data[ $what ] = $value_array;
364
+                        $valid_data[$what] = $value_array;
365 365
                         break;
366 366
                     case 'return_url' :
367 367
                         // grab and sanitize return-url
368
-                        $input_value = esc_url_raw( $input_value );
368
+                        $input_value = esc_url_raw($input_value);
369 369
                         // was the request coming from an iframe ? if so, then:
370
-                        if ( strpos($input_value, 'event_list=iframe')) {
370
+                        if (strpos($input_value, 'event_list=iframe')) {
371 371
                             // get anchor fragment
372 372
                             $input_value = explode('#', $input_value);
373 373
                             $input_value = end($input_value);
374 374
                             // use event list url instead, but append anchor
375
-                            $input_value = \EEH_Event_View::event_archive_url() . '#' . $input_value;
375
+                            $input_value = \EEH_Event_View::event_archive_url().'#'.$input_value;
376 376
                         }
377 377
                         $valid_data[$what] = $input_value;
378 378
                         break;
@@ -392,13 +392,13 @@  discard block
 block discarded – undo
392 392
      * @return TRUE on success, FALSE on fail
393 393
      * @throws \EE_Error
394 394
      */
395
-    private function addTicketToCart( \EE_Ticket $ticket = null, $qty = 1 )
395
+    private function addTicketToCart(\EE_Ticket $ticket = null, $qty = 1)
396 396
     {
397
-        do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
397
+        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
398 398
         // get the number of spaces left for this datetime ticket
399
-        $available_spaces = $this->ticketDatetimeAvailability( $ticket );
399
+        $available_spaces = $this->ticketDatetimeAvailability($ticket);
400 400
         // compare available spaces against the number of tickets being purchased
401
-        if ( $available_spaces >= $qty ) {
401
+        if ($available_spaces >= $qty) {
402 402
             // allow addons to prevent a ticket from being added to cart
403 403
             if (
404 404
                 ! apply_filters(
@@ -413,17 +413,17 @@  discard block
 block discarded – undo
413 413
             }
414 414
             $qty = absint(apply_filters('FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty', $qty, $ticket));
415 415
             // add event to cart
416
-            if ( \EE_Registry::instance()->CART->add_ticket_to_cart( $ticket, $qty ) ) {
417
-                $this->recalculateTicketDatetimeAvailability( $ticket, $qty );
416
+            if (\EE_Registry::instance()->CART->add_ticket_to_cart($ticket, $qty)) {
417
+                $this->recalculateTicketDatetimeAvailability($ticket, $qty);
418 418
                 return true;
419 419
             }
420 420
             return false;
421 421
         }
422 422
         // tickets can not be purchased but let's find the exact number left
423 423
         // for the last ticket selected PRIOR to subtracting tickets
424
-        $available_spaces = $this->ticketDatetimeAvailability( $ticket, true );
424
+        $available_spaces = $this->ticketDatetimeAvailability($ticket, true);
425 425
         // greedy greedy greedy eh?
426
-        if ( $available_spaces > 0 ) {
426
+        if ($available_spaces > 0) {
427 427
             if (
428 428
             apply_filters(
429 429
                 'FHEE__EE_Ticket_Selector___add_ticket_to_cart__allow_display_availability_error',
@@ -495,27 +495,27 @@  discard block
 block discarded – undo
495 495
      * @return int
496 496
      * @throws \EE_Error
497 497
      */
498
-    private function ticketDatetimeAvailability( \EE_Ticket $ticket, $get_original_ticket_spaces = false )
498
+    private function ticketDatetimeAvailability(\EE_Ticket $ticket, $get_original_ticket_spaces = false)
499 499
     {
500 500
         // if the $_available_spaces array has not been set up yet...
501
-        if ( ! isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
502
-            $this->setInitialTicketDatetimeAvailability( $ticket );
501
+        if ( ! isset(self::$_available_spaces['tickets'][$ticket->ID()])) {
502
+            $this->setInitialTicketDatetimeAvailability($ticket);
503 503
         }
504 504
         $available_spaces = $ticket->qty() - $ticket->sold();
505
-        if ( isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
505
+        if (isset(self::$_available_spaces['tickets'][$ticket->ID()])) {
506 506
             // loop thru tickets, which will ALSO include individual ticket records AND a total
507
-            foreach ( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] as $DTD_ID => $spaces ) {
507
+            foreach (self::$_available_spaces['tickets'][$ticket->ID()] as $DTD_ID => $spaces) {
508 508
                 // if we want the original datetime availability BEFORE we started subtracting tickets ?
509
-                if ( $get_original_ticket_spaces ) {
509
+                if ($get_original_ticket_spaces) {
510 510
                     // then grab the available spaces from the "tickets" array
511 511
                     // and compare with the above to get the lowest number
512 512
                     $available_spaces = min(
513 513
                         $available_spaces,
514
-                        self::$_available_spaces[ 'tickets' ][ $ticket->ID() ][ $DTD_ID ]
514
+                        self::$_available_spaces['tickets'][$ticket->ID()][$DTD_ID]
515 515
                     );
516 516
                 } else {
517 517
                     // we want the updated ticket availability as stored in the "datetimes" array
518
-                    $available_spaces = min( $available_spaces, self::$_available_spaces[ 'datetimes' ][ $DTD_ID ] );
518
+                    $available_spaces = min($available_spaces, self::$_available_spaces['datetimes'][$DTD_ID]);
519 519
                 }
520 520
             }
521 521
         }
@@ -529,7 +529,7 @@  discard block
 block discarded – undo
529 529
      * @return void
530 530
      * @throws \EE_Error
531 531
      */
532
-    private function setInitialTicketDatetimeAvailability( \EE_Ticket $ticket )
532
+    private function setInitialTicketDatetimeAvailability(\EE_Ticket $ticket)
533 533
     {
534 534
         // first, get all of the datetimes that are available to this ticket
535 535
         $datetimes = $ticket->get_many_related(
@@ -538,31 +538,31 @@  discard block
 block discarded – undo
538 538
                 array(
539 539
                     'DTT_EVT_end' => array(
540 540
                         '>=',
541
-                        \EEM_Datetime::instance()->current_time_for_query( 'DTT_EVT_end' ),
541
+                        \EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
542 542
                     ),
543 543
                 ),
544
-                'order_by' => array( 'DTT_EVT_start' => 'ASC' ),
544
+                'order_by' => array('DTT_EVT_start' => 'ASC'),
545 545
             )
546 546
         );
547
-        if ( ! empty( $datetimes ) ) {
547
+        if ( ! empty($datetimes)) {
548 548
             // now loop thru all of the datetimes
549
-            foreach ( $datetimes as $datetime ) {
550
-                if ( $datetime instanceof \EE_Datetime ) {
549
+            foreach ($datetimes as $datetime) {
550
+                if ($datetime instanceof \EE_Datetime) {
551 551
                     // the number of spaces available for the datetime without considering individual ticket quantities
552 552
                     $spaces_remaining = $datetime->spaces_remaining();
553 553
                     // save the total available spaces ( the lesser of the ticket qty minus the number of tickets sold
554 554
                     // or the datetime spaces remaining) to this ticket using the datetime ID as the key
555
-                    self::$_available_spaces[ 'tickets' ][ $ticket->ID() ][ $datetime->ID() ] = min(
555
+                    self::$_available_spaces['tickets'][$ticket->ID()][$datetime->ID()] = min(
556 556
                         $ticket->qty() - $ticket->sold(),
557 557
                         $spaces_remaining
558 558
                     );
559 559
                     // if the remaining spaces for this datetime is already set,
560 560
                     // then compare that against the datetime spaces remaining, and take the lowest number,
561 561
                     // else just take the datetime spaces remaining, and assign to the datetimes array
562
-                    self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ] = isset(
563
-                        self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ]
562
+                    self::$_available_spaces['datetimes'][$datetime->ID()] = isset(
563
+                        self::$_available_spaces['datetimes'][$datetime->ID()]
564 564
                     )
565
-                        ? min( self::$_available_spaces[ 'datetimes' ][ $datetime->ID() ], $spaces_remaining )
565
+                        ? min(self::$_available_spaces['datetimes'][$datetime->ID()], $spaces_remaining)
566 566
                         : $spaces_remaining;
567 567
                 }
568 568
             }
@@ -576,13 +576,13 @@  discard block
 block discarded – undo
576 576
      * @param    int        $qty
577 577
      * @return    void
578 578
      */
579
-    private function recalculateTicketDatetimeAvailability( \EE_Ticket $ticket, $qty = 0 )
579
+    private function recalculateTicketDatetimeAvailability(\EE_Ticket $ticket, $qty = 0)
580 580
     {
581
-        if ( isset( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] ) ) {
581
+        if (isset(self::$_available_spaces['tickets'][$ticket->ID()])) {
582 582
             // loop thru tickets, which will ALSO include individual ticket records AND a total
583
-            foreach ( self::$_available_spaces[ 'tickets' ][ $ticket->ID() ] as $DTD_ID => $spaces ) {
583
+            foreach (self::$_available_spaces['tickets'][$ticket->ID()] as $DTD_ID => $spaces) {
584 584
                 // subtract the qty of selected tickets from each datetime's available spaces this ticket has access to,
585
-                self::$_available_spaces[ 'datetimes' ][ $DTD_ID ] -= $qty;
585
+                self::$_available_spaces['datetimes'][$DTD_ID] -= $qty;
586 586
             }
587 587
         }
588 588
     }
Please login to merge, or discard this patch.
core/db_models/relations/EE_Model_Relation_Base.php 1 patch
Indentation   +477 added lines, -477 removed lines patch added patch discarded remove patch
@@ -17,482 +17,482 @@
 block discarded – undo
17 17
  */
18 18
 abstract class EE_Model_Relation_Base implements HasSchemaInterface
19 19
 {
20
-    /**
21
-     * The model name of which this relation is a component (ie, the model that called new EE_Model_Relation_Base)
22
-     *
23
-     * @var string eg Event, Question_Group, Registration
24
-     */
25
-    private $_this_model_name;
26
-    /**
27
-     * The model name pointed to by this relation (ie, the model we want to establish a relationship to)
28
-     *
29
-     * @var string eg Event, Question_Group, Registration
30
-     */
31
-    private $_other_model_name;
32
-
33
-    /**
34
-     * this is typically used when calling the relation models to make sure they inherit any set timezone from the
35
-     * initiating model.
36
-     *
37
-     * @var string
38
-     */
39
-    protected $_timezone;
40
-
41
-    /**
42
-     * If you try to delete "this_model", and there are related "other_models",
43
-     * and this isn't null, then abandon the deletion and add this warning.
44
-     * This effectively makes it impossible to delete "this_model" while there are
45
-     * related "other_models" along this relation.
46
-     *
47
-     * @var string (internationalized)
48
-     */
49
-    protected $_blocking_delete_error_message;
50
-
51
-    protected $_blocking_delete = false;
52
-
53
-    /**
54
-     * Object representing the relationship between two models. This knows how to join the models,
55
-     * get related models across the relation, and add-and-remove the relationships.
56
-     *
57
-     * @param boolean $block_deletes                 if there are related models across this relation, block (prevent
58
-     *                                               and add an error) the deletion of this model
59
-     * @param string  $blocking_delete_error_message a customized error message on blocking deletes instead of the
60
-     *                                               default
61
-     */
62
-    public function __construct($block_deletes, $blocking_delete_error_message)
63
-    {
64
-        $this->_blocking_delete               = $block_deletes;
65
-        $this->_blocking_delete_error_message = $blocking_delete_error_message;
66
-    }
67
-
68
-
69
-    /**
70
-     * @param $this_model_name
71
-     * @param $other_model_name
72
-     * @throws EE_Error
73
-     */
74
-    public function _construct_finalize_set_models($this_model_name, $other_model_name)
75
-    {
76
-        $this->_this_model_name  = $this_model_name;
77
-        $this->_other_model_name = $other_model_name;
78
-        if (is_string($this->_blocking_delete)) {
79
-            throw new EE_Error(sprintf(__("When instantiating the relation of type %s from %s to %s, the \$block_deletes argument should be a boolean, not a string (%s)",
80
-                "event_espresso"),
81
-                get_class($this), $this_model_name, $other_model_name, $this->_blocking_delete));
82
-        }
83
-    }
84
-
85
-
86
-    /**
87
-     * Gets the model where this relation is defined.
88
-     *
89
-     * @return EEM_Base
90
-     */
91
-    public function get_this_model()
92
-    {
93
-        return $this->_get_model($this->_this_model_name);
94
-    }
95
-
96
-
97
-    /**
98
-     * Gets the model which this relation establishes the relation TO (ie,
99
-     * this relation object was defined on get_this_model(), get_other_model() is the other one)
100
-     *
101
-     * @return EEM_Base
102
-     */
103
-    public function get_other_model()
104
-    {
105
-        return $this->_get_model($this->_other_model_name);
106
-    }
107
-
108
-
109
-    /**
110
-     * Internally used by get_this_model() and get_other_model()
111
-     *
112
-     * @param string $model_name like Event, Question_Group, etc. omit the EEM_
113
-     * @return EEM_Base
114
-     */
115
-    protected function _get_model($model_name)
116
-    {
117
-        $modelInstance = EE_Registry::instance()->load_model($model_name);
118
-        $modelInstance->set_timezone($this->_timezone);
119
-        return $modelInstance;
120
-    }
121
-
122
-
123
-    /**
124
-     * entirely possible that relations may be called from a model and we need to make sure those relations have their
125
-     * timezone set correctly.
126
-     *
127
-     * @param string $timezone timezone to set.
128
-     */
129
-    public function set_timezone($timezone)
130
-    {
131
-        if ($timezone !== null) {
132
-            $this->_timezone = $timezone;
133
-        }
134
-    }
135
-
136
-
137
-    /**
138
-     * @param        $other_table
139
-     * @param        $other_table_alias
140
-     * @param        $other_table_column
141
-     * @param        $this_table_alias
142
-     * @param        $this_table_join_column
143
-     * @param string $extra_join_sql
144
-     * @return string
145
-     */
146
-    protected function _left_join(
147
-        $other_table,
148
-        $other_table_alias,
149
-        $other_table_column,
150
-        $this_table_alias,
151
-        $this_table_join_column,
152
-        $extra_join_sql = ''
153
-    ) {
154
-        return " LEFT JOIN " . $other_table . " AS " . $other_table_alias . " ON " . $other_table_alias . "." . $other_table_column . "=" . $this_table_alias . "." . $this_table_join_column . ($extra_join_sql ? " AND $extra_join_sql" : '');
155
-    }
156
-
157
-
158
-    /**
159
-     * Gets all the model objects of type of other model related to $model_object,
160
-     * according to this relation. This is the same code for EE_HABTM_Relation and EE_Has_Many_Relation.
161
-     * For both of those child classes, $model_object must be saved so that it has an ID before querying,
162
-     * otherwise an error will be thrown. Note: by default we disable default_where_conditions
163
-     * EE_Belongs_To_Relation doesn't need to be saved before querying.
164
-     *
165
-     * @param EE_Base_Class|int $model_object_or_id                      or the primary key of this model
166
-     * @param array             $query_params                            like EEM_Base::get_all's $query_params
167
-     * @param boolean           $values_already_prepared_by_model_object @deprecated since 4.8.1
168
-     * @return EE_Base_Class[]
169
-     * @throws \EE_Error
170
-     */
171
-    public function get_all_related(
172
-        $model_object_or_id,
173
-        $query_params = array(),
174
-        $values_already_prepared_by_model_object = false
175
-    ) {
176
-        if ($values_already_prepared_by_model_object !== false) {
177
-            EE_Error::doing_it_wrong('EE_Model_Relation_Base::get_all_related',
178
-                __('The argument $values_already_prepared_by_model_object is no longer used.', 'event_espresso'),
179
-                '4.8.1');
180
-        }
181
-        $query_params                                      = $this->_disable_default_where_conditions_on_query_param($query_params);
182
-        $query_param_where_this_model_pk                   = $this->get_this_model()->get_this_model_name()
183
-                                                             . "."
184
-                                                             . $this->get_this_model()->get_primary_key_field()->get_name();
185
-        $model_object_id                                   = $this->_get_model_object_id($model_object_or_id);
186
-        $query_params[0][$query_param_where_this_model_pk] = $model_object_id;
187
-        return $this->get_other_model()->get_all($query_params);
188
-    }
189
-
190
-
191
-    /**
192
-     * Alters the $query_params to disable default where conditions, unless otherwise specified
193
-     *
194
-     * @param string $query_params
195
-     * @return array
196
-     */
197
-    protected function _disable_default_where_conditions_on_query_param($query_params)
198
-    {
199
-        if (! isset($query_params['default_where_conditions'])) {
200
-            $query_params['default_where_conditions'] = 'none';
201
-        }
202
-        return $query_params;
203
-    }
204
-
205
-
206
-    /**
207
-     * Deletes the related model objects which meet the query parameters. If no
208
-     * parameters are specified, then all related model objects will be deleted.
209
-     * Note: If the related model is extends EEM_Soft_Delete_Base, then the related
210
-     * model objects will only be soft-deleted.
211
-     *
212
-     * @param EE_Base_Class|int|string $model_object_or_id
213
-     * @param array                    $query_params
214
-     * @return int of how many related models got deleted
215
-     * @throws \EE_Error
216
-     */
217
-    public function delete_all_related($model_object_or_id, $query_params = array())
218
-    {
219
-        //for each thing we would delete,
220
-        $related_model_objects = $this->get_all_related($model_object_or_id, $query_params);
221
-        //determine if it's blocked by anything else before it can be deleted
222
-        $deleted_count = 0;
223
-        foreach ($related_model_objects as $related_model_object) {
224
-            $delete_is_blocked = $this->get_other_model()->delete_is_blocked_by_related_models($related_model_object,
225
-                $model_object_or_id);
226
-            /* @var $model_object_or_id EE_Base_Class */
227
-            if (! $delete_is_blocked) {
228
-                $this->remove_relation_to($model_object_or_id, $related_model_object);
229
-                $related_model_object->delete();
230
-                $deleted_count++;
231
-            }
232
-        }
233
-        return $deleted_count;
234
-    }
235
-
236
-
237
-    /**
238
-     * Deletes the related model objects which meet the query parameters. If no
239
-     * parameters are specified, then all related model objects will be deleted.
240
-     * Note: If the related model is extends EEM_Soft_Delete_Base, then the related
241
-     * model objects will only be soft-deleted.
242
-     *
243
-     * @param EE_Base_Class|int|string $model_object_or_id
244
-     * @param array                    $query_params
245
-     * @return int of how many related models got deleted
246
-     * @throws \EE_Error
247
-     */
248
-    public function delete_related_permanently($model_object_or_id, $query_params = array())
249
-    {
250
-        //for each thing we would delete,
251
-        $related_model_objects = $this->get_all_related($model_object_or_id, $query_params);
252
-        //determine if it's blocked by anything else before it can be deleted
253
-        $deleted_count = 0;
254
-        foreach ($related_model_objects as $related_model_object) {
255
-            $delete_is_blocked = $this->get_other_model()->delete_is_blocked_by_related_models($related_model_object,
256
-                $model_object_or_id);
257
-            /* @var $model_object_or_id EE_Base_Class */
258
-            if ($related_model_object instanceof EE_Soft_Delete_Base_Class) {
259
-                $this->remove_relation_to($model_object_or_id, $related_model_object);
260
-                $deleted_count++;
261
-                if (! $delete_is_blocked) {
262
-                    $related_model_object->delete_permanently();
263
-                } else {
264
-                    //delete is blocked
265
-                    //brent and darren, in this case, wanted to just soft delete it then
266
-                    $related_model_object->delete();
267
-                }
268
-            } else {
269
-                //its not a soft-deletable thing anyways. do the normal logic.
270
-                if (! $delete_is_blocked) {
271
-                    $this->remove_relation_to($model_object_or_id, $related_model_object);
272
-                    $related_model_object->delete();
273
-                    $deleted_count++;
274
-                }
275
-            }
276
-        }
277
-        return $deleted_count;
278
-    }
279
-
280
-
281
-    /**
282
-     * this just returns a model_object_id for incoming item that could be an object or id.
283
-     *
284
-     * @param  EE_Base_Class|int $model_object_or_id model object or the primary key of this model
285
-     * @throws EE_Error
286
-     * @return int
287
-     */
288
-    protected function _get_model_object_id($model_object_or_id)
289
-    {
290
-        $model_object_id = $model_object_or_id;
291
-        if ($model_object_or_id instanceof EE_Base_Class) {
292
-            $model_object_id = $model_object_or_id->ID();
293
-        }
294
-        if (! $model_object_id) {
295
-            throw new EE_Error(sprintf(__("Sorry, we cant get the related %s model objects to %s model object before it has an ID. You can solve that by just saving it before trying to get its related model objects",
296
-                "event_espresso"), $this->get_other_model()->get_this_model_name(),
297
-                $this->get_this_model()->get_this_model_name()));
298
-        }
299
-        return $model_object_id;
300
-    }
301
-
302
-
303
-    /**
304
-     * Gets the SQL string for performing the join between this model and the other model.
305
-     *
306
-     * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
307
-     * @return string of SQL, eg "LEFT JOIN table_name AS table_alias ON this_model_primary_table.pk =
308
-     *                other_model_primary_table.fk" etc
309
-     */
310
-    abstract public function get_join_statement($model_relation_chain);
311
-
312
-
313
-    /**
314
-     * Adds a relationships between the two model objects provided. Each type of relationship handles this differently
315
-     * (EE_Belongs_To is a slight exception, it should more accurately be called set_relation_to(...), as this
316
-     * relationship only allows this model to be related to a single other model of this type)
317
-     *
318
-     * @param       $this_obj_or_id
319
-     * @param       $other_obj_or_id
320
-     * @param array $extra_join_model_fields_n_values
321
-     * @return \EE_Base_Class the EE_Base_Class which was added as a relation. (Convenient if you only pass an ID for
322
-     *                        $other_obj_or_id)
323
-     */
324
-    abstract public function add_relation_to(
325
-        $this_obj_or_id,
326
-        $other_obj_or_id,
327
-        $extra_join_model_fields_n_values = array()
328
-    );
329
-
330
-
331
-    /**
332
-     * Similar to 'add_relation_to(...)', performs the opposite action of removing the relationship between the two
333
-     * model objects
334
-     *
335
-     * @param       $this_obj_or_id
336
-     * @param       $other_obj_or_id
337
-     * @param array $where_query
338
-     * @return bool
339
-     */
340
-    abstract public function remove_relation_to($this_obj_or_id, $other_obj_or_id, $where_query = array());
341
-
342
-
343
-    /**
344
-     * Removes ALL relation instances for this relation obj
345
-     *
346
-     * @param EE_Base_Class|int $this_obj_or_id
347
-     * @param array             $where_query_param like EEM_Base::get_all's $query_params[0] (where conditions)
348
-     * @return EE_Base_Class[]
349
-     * @throws \EE_Error
350
-     */
351
-    public function remove_relations($this_obj_or_id, $where_query_param = array())
352
-    {
353
-        $related_things = $this->get_all_related($this_obj_or_id, array($where_query_param));
354
-        $objs_removed   = array();
355
-        foreach ($related_things as $related_thing) {
356
-            $objs_removed[] = $this->remove_relation_to($this_obj_or_id, $related_thing);
357
-        }
358
-        return $objs_removed;
359
-    }
360
-
361
-
362
-    /**
363
-     * If you aren't allowed to delete this model when there are related models across this
364
-     * relation object, return true. Otherwise, if you can delete this model even though
365
-     * related objects exist, returns false.
366
-     *
367
-     * @return boolean
368
-     */
369
-    public function block_delete_if_related_models_exist()
370
-    {
371
-        return $this->_blocking_delete;
372
-    }
373
-
374
-
375
-    /**
376
-     * Gets the error message to show
377
-     *
378
-     * @return string
379
-     */
380
-    public function get_deletion_error_message()
381
-    {
382
-        if ($this->_blocking_delete_error_message) {
383
-            return $this->_blocking_delete_error_message;
384
-        } else {
20
+	/**
21
+	 * The model name of which this relation is a component (ie, the model that called new EE_Model_Relation_Base)
22
+	 *
23
+	 * @var string eg Event, Question_Group, Registration
24
+	 */
25
+	private $_this_model_name;
26
+	/**
27
+	 * The model name pointed to by this relation (ie, the model we want to establish a relationship to)
28
+	 *
29
+	 * @var string eg Event, Question_Group, Registration
30
+	 */
31
+	private $_other_model_name;
32
+
33
+	/**
34
+	 * this is typically used when calling the relation models to make sure they inherit any set timezone from the
35
+	 * initiating model.
36
+	 *
37
+	 * @var string
38
+	 */
39
+	protected $_timezone;
40
+
41
+	/**
42
+	 * If you try to delete "this_model", and there are related "other_models",
43
+	 * and this isn't null, then abandon the deletion and add this warning.
44
+	 * This effectively makes it impossible to delete "this_model" while there are
45
+	 * related "other_models" along this relation.
46
+	 *
47
+	 * @var string (internationalized)
48
+	 */
49
+	protected $_blocking_delete_error_message;
50
+
51
+	protected $_blocking_delete = false;
52
+
53
+	/**
54
+	 * Object representing the relationship between two models. This knows how to join the models,
55
+	 * get related models across the relation, and add-and-remove the relationships.
56
+	 *
57
+	 * @param boolean $block_deletes                 if there are related models across this relation, block (prevent
58
+	 *                                               and add an error) the deletion of this model
59
+	 * @param string  $blocking_delete_error_message a customized error message on blocking deletes instead of the
60
+	 *                                               default
61
+	 */
62
+	public function __construct($block_deletes, $blocking_delete_error_message)
63
+	{
64
+		$this->_blocking_delete               = $block_deletes;
65
+		$this->_blocking_delete_error_message = $blocking_delete_error_message;
66
+	}
67
+
68
+
69
+	/**
70
+	 * @param $this_model_name
71
+	 * @param $other_model_name
72
+	 * @throws EE_Error
73
+	 */
74
+	public function _construct_finalize_set_models($this_model_name, $other_model_name)
75
+	{
76
+		$this->_this_model_name  = $this_model_name;
77
+		$this->_other_model_name = $other_model_name;
78
+		if (is_string($this->_blocking_delete)) {
79
+			throw new EE_Error(sprintf(__("When instantiating the relation of type %s from %s to %s, the \$block_deletes argument should be a boolean, not a string (%s)",
80
+				"event_espresso"),
81
+				get_class($this), $this_model_name, $other_model_name, $this->_blocking_delete));
82
+		}
83
+	}
84
+
85
+
86
+	/**
87
+	 * Gets the model where this relation is defined.
88
+	 *
89
+	 * @return EEM_Base
90
+	 */
91
+	public function get_this_model()
92
+	{
93
+		return $this->_get_model($this->_this_model_name);
94
+	}
95
+
96
+
97
+	/**
98
+	 * Gets the model which this relation establishes the relation TO (ie,
99
+	 * this relation object was defined on get_this_model(), get_other_model() is the other one)
100
+	 *
101
+	 * @return EEM_Base
102
+	 */
103
+	public function get_other_model()
104
+	{
105
+		return $this->_get_model($this->_other_model_name);
106
+	}
107
+
108
+
109
+	/**
110
+	 * Internally used by get_this_model() and get_other_model()
111
+	 *
112
+	 * @param string $model_name like Event, Question_Group, etc. omit the EEM_
113
+	 * @return EEM_Base
114
+	 */
115
+	protected function _get_model($model_name)
116
+	{
117
+		$modelInstance = EE_Registry::instance()->load_model($model_name);
118
+		$modelInstance->set_timezone($this->_timezone);
119
+		return $modelInstance;
120
+	}
121
+
122
+
123
+	/**
124
+	 * entirely possible that relations may be called from a model and we need to make sure those relations have their
125
+	 * timezone set correctly.
126
+	 *
127
+	 * @param string $timezone timezone to set.
128
+	 */
129
+	public function set_timezone($timezone)
130
+	{
131
+		if ($timezone !== null) {
132
+			$this->_timezone = $timezone;
133
+		}
134
+	}
135
+
136
+
137
+	/**
138
+	 * @param        $other_table
139
+	 * @param        $other_table_alias
140
+	 * @param        $other_table_column
141
+	 * @param        $this_table_alias
142
+	 * @param        $this_table_join_column
143
+	 * @param string $extra_join_sql
144
+	 * @return string
145
+	 */
146
+	protected function _left_join(
147
+		$other_table,
148
+		$other_table_alias,
149
+		$other_table_column,
150
+		$this_table_alias,
151
+		$this_table_join_column,
152
+		$extra_join_sql = ''
153
+	) {
154
+		return " LEFT JOIN " . $other_table . " AS " . $other_table_alias . " ON " . $other_table_alias . "." . $other_table_column . "=" . $this_table_alias . "." . $this_table_join_column . ($extra_join_sql ? " AND $extra_join_sql" : '');
155
+	}
156
+
157
+
158
+	/**
159
+	 * Gets all the model objects of type of other model related to $model_object,
160
+	 * according to this relation. This is the same code for EE_HABTM_Relation and EE_Has_Many_Relation.
161
+	 * For both of those child classes, $model_object must be saved so that it has an ID before querying,
162
+	 * otherwise an error will be thrown. Note: by default we disable default_where_conditions
163
+	 * EE_Belongs_To_Relation doesn't need to be saved before querying.
164
+	 *
165
+	 * @param EE_Base_Class|int $model_object_or_id                      or the primary key of this model
166
+	 * @param array             $query_params                            like EEM_Base::get_all's $query_params
167
+	 * @param boolean           $values_already_prepared_by_model_object @deprecated since 4.8.1
168
+	 * @return EE_Base_Class[]
169
+	 * @throws \EE_Error
170
+	 */
171
+	public function get_all_related(
172
+		$model_object_or_id,
173
+		$query_params = array(),
174
+		$values_already_prepared_by_model_object = false
175
+	) {
176
+		if ($values_already_prepared_by_model_object !== false) {
177
+			EE_Error::doing_it_wrong('EE_Model_Relation_Base::get_all_related',
178
+				__('The argument $values_already_prepared_by_model_object is no longer used.', 'event_espresso'),
179
+				'4.8.1');
180
+		}
181
+		$query_params                                      = $this->_disable_default_where_conditions_on_query_param($query_params);
182
+		$query_param_where_this_model_pk                   = $this->get_this_model()->get_this_model_name()
183
+															 . "."
184
+															 . $this->get_this_model()->get_primary_key_field()->get_name();
185
+		$model_object_id                                   = $this->_get_model_object_id($model_object_or_id);
186
+		$query_params[0][$query_param_where_this_model_pk] = $model_object_id;
187
+		return $this->get_other_model()->get_all($query_params);
188
+	}
189
+
190
+
191
+	/**
192
+	 * Alters the $query_params to disable default where conditions, unless otherwise specified
193
+	 *
194
+	 * @param string $query_params
195
+	 * @return array
196
+	 */
197
+	protected function _disable_default_where_conditions_on_query_param($query_params)
198
+	{
199
+		if (! isset($query_params['default_where_conditions'])) {
200
+			$query_params['default_where_conditions'] = 'none';
201
+		}
202
+		return $query_params;
203
+	}
204
+
205
+
206
+	/**
207
+	 * Deletes the related model objects which meet the query parameters. If no
208
+	 * parameters are specified, then all related model objects will be deleted.
209
+	 * Note: If the related model is extends EEM_Soft_Delete_Base, then the related
210
+	 * model objects will only be soft-deleted.
211
+	 *
212
+	 * @param EE_Base_Class|int|string $model_object_or_id
213
+	 * @param array                    $query_params
214
+	 * @return int of how many related models got deleted
215
+	 * @throws \EE_Error
216
+	 */
217
+	public function delete_all_related($model_object_or_id, $query_params = array())
218
+	{
219
+		//for each thing we would delete,
220
+		$related_model_objects = $this->get_all_related($model_object_or_id, $query_params);
221
+		//determine if it's blocked by anything else before it can be deleted
222
+		$deleted_count = 0;
223
+		foreach ($related_model_objects as $related_model_object) {
224
+			$delete_is_blocked = $this->get_other_model()->delete_is_blocked_by_related_models($related_model_object,
225
+				$model_object_or_id);
226
+			/* @var $model_object_or_id EE_Base_Class */
227
+			if (! $delete_is_blocked) {
228
+				$this->remove_relation_to($model_object_or_id, $related_model_object);
229
+				$related_model_object->delete();
230
+				$deleted_count++;
231
+			}
232
+		}
233
+		return $deleted_count;
234
+	}
235
+
236
+
237
+	/**
238
+	 * Deletes the related model objects which meet the query parameters. If no
239
+	 * parameters are specified, then all related model objects will be deleted.
240
+	 * Note: If the related model is extends EEM_Soft_Delete_Base, then the related
241
+	 * model objects will only be soft-deleted.
242
+	 *
243
+	 * @param EE_Base_Class|int|string $model_object_or_id
244
+	 * @param array                    $query_params
245
+	 * @return int of how many related models got deleted
246
+	 * @throws \EE_Error
247
+	 */
248
+	public function delete_related_permanently($model_object_or_id, $query_params = array())
249
+	{
250
+		//for each thing we would delete,
251
+		$related_model_objects = $this->get_all_related($model_object_or_id, $query_params);
252
+		//determine if it's blocked by anything else before it can be deleted
253
+		$deleted_count = 0;
254
+		foreach ($related_model_objects as $related_model_object) {
255
+			$delete_is_blocked = $this->get_other_model()->delete_is_blocked_by_related_models($related_model_object,
256
+				$model_object_or_id);
257
+			/* @var $model_object_or_id EE_Base_Class */
258
+			if ($related_model_object instanceof EE_Soft_Delete_Base_Class) {
259
+				$this->remove_relation_to($model_object_or_id, $related_model_object);
260
+				$deleted_count++;
261
+				if (! $delete_is_blocked) {
262
+					$related_model_object->delete_permanently();
263
+				} else {
264
+					//delete is blocked
265
+					//brent and darren, in this case, wanted to just soft delete it then
266
+					$related_model_object->delete();
267
+				}
268
+			} else {
269
+				//its not a soft-deletable thing anyways. do the normal logic.
270
+				if (! $delete_is_blocked) {
271
+					$this->remove_relation_to($model_object_or_id, $related_model_object);
272
+					$related_model_object->delete();
273
+					$deleted_count++;
274
+				}
275
+			}
276
+		}
277
+		return $deleted_count;
278
+	}
279
+
280
+
281
+	/**
282
+	 * this just returns a model_object_id for incoming item that could be an object or id.
283
+	 *
284
+	 * @param  EE_Base_Class|int $model_object_or_id model object or the primary key of this model
285
+	 * @throws EE_Error
286
+	 * @return int
287
+	 */
288
+	protected function _get_model_object_id($model_object_or_id)
289
+	{
290
+		$model_object_id = $model_object_or_id;
291
+		if ($model_object_or_id instanceof EE_Base_Class) {
292
+			$model_object_id = $model_object_or_id->ID();
293
+		}
294
+		if (! $model_object_id) {
295
+			throw new EE_Error(sprintf(__("Sorry, we cant get the related %s model objects to %s model object before it has an ID. You can solve that by just saving it before trying to get its related model objects",
296
+				"event_espresso"), $this->get_other_model()->get_this_model_name(),
297
+				$this->get_this_model()->get_this_model_name()));
298
+		}
299
+		return $model_object_id;
300
+	}
301
+
302
+
303
+	/**
304
+	 * Gets the SQL string for performing the join between this model and the other model.
305
+	 *
306
+	 * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
307
+	 * @return string of SQL, eg "LEFT JOIN table_name AS table_alias ON this_model_primary_table.pk =
308
+	 *                other_model_primary_table.fk" etc
309
+	 */
310
+	abstract public function get_join_statement($model_relation_chain);
311
+
312
+
313
+	/**
314
+	 * Adds a relationships between the two model objects provided. Each type of relationship handles this differently
315
+	 * (EE_Belongs_To is a slight exception, it should more accurately be called set_relation_to(...), as this
316
+	 * relationship only allows this model to be related to a single other model of this type)
317
+	 *
318
+	 * @param       $this_obj_or_id
319
+	 * @param       $other_obj_or_id
320
+	 * @param array $extra_join_model_fields_n_values
321
+	 * @return \EE_Base_Class the EE_Base_Class which was added as a relation. (Convenient if you only pass an ID for
322
+	 *                        $other_obj_or_id)
323
+	 */
324
+	abstract public function add_relation_to(
325
+		$this_obj_or_id,
326
+		$other_obj_or_id,
327
+		$extra_join_model_fields_n_values = array()
328
+	);
329
+
330
+
331
+	/**
332
+	 * Similar to 'add_relation_to(...)', performs the opposite action of removing the relationship between the two
333
+	 * model objects
334
+	 *
335
+	 * @param       $this_obj_or_id
336
+	 * @param       $other_obj_or_id
337
+	 * @param array $where_query
338
+	 * @return bool
339
+	 */
340
+	abstract public function remove_relation_to($this_obj_or_id, $other_obj_or_id, $where_query = array());
341
+
342
+
343
+	/**
344
+	 * Removes ALL relation instances for this relation obj
345
+	 *
346
+	 * @param EE_Base_Class|int $this_obj_or_id
347
+	 * @param array             $where_query_param like EEM_Base::get_all's $query_params[0] (where conditions)
348
+	 * @return EE_Base_Class[]
349
+	 * @throws \EE_Error
350
+	 */
351
+	public function remove_relations($this_obj_or_id, $where_query_param = array())
352
+	{
353
+		$related_things = $this->get_all_related($this_obj_or_id, array($where_query_param));
354
+		$objs_removed   = array();
355
+		foreach ($related_things as $related_thing) {
356
+			$objs_removed[] = $this->remove_relation_to($this_obj_or_id, $related_thing);
357
+		}
358
+		return $objs_removed;
359
+	}
360
+
361
+
362
+	/**
363
+	 * If you aren't allowed to delete this model when there are related models across this
364
+	 * relation object, return true. Otherwise, if you can delete this model even though
365
+	 * related objects exist, returns false.
366
+	 *
367
+	 * @return boolean
368
+	 */
369
+	public function block_delete_if_related_models_exist()
370
+	{
371
+		return $this->_blocking_delete;
372
+	}
373
+
374
+
375
+	/**
376
+	 * Gets the error message to show
377
+	 *
378
+	 * @return string
379
+	 */
380
+	public function get_deletion_error_message()
381
+	{
382
+		if ($this->_blocking_delete_error_message) {
383
+			return $this->_blocking_delete_error_message;
384
+		} else {
385 385
 //			return sprintf(__('Cannot delete %1$s when there are related %2$s', "event_espresso"),$this->get_this_model()->item_name(2),$this->get_other_model()->item_name(2));
386
-            return sprintf(
387
-                __('This %1$s is currently linked to one or more %2$s records. If this %1$s is incorrect, then please remove it from all %3$s before attempting to delete it.',
388
-                    "event_espresso"),
389
-                $this->get_this_model()->item_name(1),
390
-                $this->get_other_model()->item_name(1),
391
-                $this->get_other_model()->item_name(2)
392
-            );
393
-        }
394
-    }
395
-
396
-    /**
397
-     * Returns whatever is set as the nicename for the object.
398
-     *
399
-     * @return string
400
-     */
401
-    public function getSchemaDescription()
402
-    {
403
-        $description = $this instanceof EE_Belongs_To_Relation
404
-            ? esc_html__('The related %1$s entity to the %2$s.', 'event_espresso')
405
-            : esc_html__('The related %1$s entities to the %2$s.', 'event_espresso');
406
-        return sprintf(
407
-            $description,
408
-            $this->get_other_model()->get_this_model_name(),
409
-            $this->get_this_model()->get_this_model_name()
410
-        );
411
-    }
412
-
413
-    /**
414
-     * Returns whatever is set as the $_schema_type property for the object.
415
-     * Note: this will automatically add 'null' to the schema if the object is_nullable()
416
-     *
417
-     * @return string|array
418
-     */
419
-    public function getSchemaType()
420
-    {
421
-        return $this instanceof EE_Belongs_To_Relation ? 'object' : 'array';
422
-    }
423
-
424
-    /**
425
-     * This is usually present when the $_schema_type property is 'object'.  Any child classes will need to override
426
-     * this method and return the properties for the schema.
427
-     * The reason this is not a property on the class is because there may be filters set on the values for the property
428
-     * that won't be exposed on construct.  For example enum type schemas may have the enum values filtered.
429
-     *
430
-     * @return array
431
-     */
432
-    public function getSchemaProperties()
433
-    {
434
-        return array();
435
-    }
436
-
437
-    /**
438
-     * If a child class has enum values, they should override this method and provide a simple array
439
-     * of the enum values.
440
-     * The reason this is not a property on the class is because there may be filterable enum values that
441
-     * are set on the instantiated object that could be filtered after construct.
442
-     *
443
-     * @return array
444
-     */
445
-    public function getSchemaEnum()
446
-    {
447
-        return array();
448
-    }
449
-
450
-    /**
451
-     * This returns the value of the $_schema_format property on the object.
452
-     *
453
-     * @return string
454
-     */
455
-    public function getSchemaFormat()
456
-    {
457
-        return array();
458
-    }
459
-
460
-    /**
461
-     * This returns the value of the $_schema_readonly property on the object.
462
-     *
463
-     * @return bool
464
-     */
465
-    public function getSchemaReadonly()
466
-    {
467
-        return true;
468
-    }
469
-
470
-    /**
471
-     * This returns elements used to represent this field in the json schema.
472
-     *
473
-     * @link http://json-schema.org/
474
-     * @return array
475
-     */
476
-    public function getSchema()
477
-    {
478
-        $schema = array(
479
-            'description' => $this->getSchemaDescription(),
480
-            'type' => $this->getSchemaType(),
481
-            'relation' => true,
482
-            'relation_type' => get_class($this),
483
-            'readonly' => $this->getSchemaReadonly()
484
-        );
485
-
486
-        if ($this instanceof EE_HABTM_Relation) {
487
-            $schema['joining_model_name'] = $this->get_join_model()->get_this_model_name();
488
-        }
489
-
490
-        if ($this->getSchemaType() === 'array') {
491
-            $schema['items'] = array(
492
-                'type' => 'object'
493
-            );
494
-        }
495
-
496
-        return $schema;
497
-    }
386
+			return sprintf(
387
+				__('This %1$s is currently linked to one or more %2$s records. If this %1$s is incorrect, then please remove it from all %3$s before attempting to delete it.',
388
+					"event_espresso"),
389
+				$this->get_this_model()->item_name(1),
390
+				$this->get_other_model()->item_name(1),
391
+				$this->get_other_model()->item_name(2)
392
+			);
393
+		}
394
+	}
395
+
396
+	/**
397
+	 * Returns whatever is set as the nicename for the object.
398
+	 *
399
+	 * @return string
400
+	 */
401
+	public function getSchemaDescription()
402
+	{
403
+		$description = $this instanceof EE_Belongs_To_Relation
404
+			? esc_html__('The related %1$s entity to the %2$s.', 'event_espresso')
405
+			: esc_html__('The related %1$s entities to the %2$s.', 'event_espresso');
406
+		return sprintf(
407
+			$description,
408
+			$this->get_other_model()->get_this_model_name(),
409
+			$this->get_this_model()->get_this_model_name()
410
+		);
411
+	}
412
+
413
+	/**
414
+	 * Returns whatever is set as the $_schema_type property for the object.
415
+	 * Note: this will automatically add 'null' to the schema if the object is_nullable()
416
+	 *
417
+	 * @return string|array
418
+	 */
419
+	public function getSchemaType()
420
+	{
421
+		return $this instanceof EE_Belongs_To_Relation ? 'object' : 'array';
422
+	}
423
+
424
+	/**
425
+	 * This is usually present when the $_schema_type property is 'object'.  Any child classes will need to override
426
+	 * this method and return the properties for the schema.
427
+	 * The reason this is not a property on the class is because there may be filters set on the values for the property
428
+	 * that won't be exposed on construct.  For example enum type schemas may have the enum values filtered.
429
+	 *
430
+	 * @return array
431
+	 */
432
+	public function getSchemaProperties()
433
+	{
434
+		return array();
435
+	}
436
+
437
+	/**
438
+	 * If a child class has enum values, they should override this method and provide a simple array
439
+	 * of the enum values.
440
+	 * The reason this is not a property on the class is because there may be filterable enum values that
441
+	 * are set on the instantiated object that could be filtered after construct.
442
+	 *
443
+	 * @return array
444
+	 */
445
+	public function getSchemaEnum()
446
+	{
447
+		return array();
448
+	}
449
+
450
+	/**
451
+	 * This returns the value of the $_schema_format property on the object.
452
+	 *
453
+	 * @return string
454
+	 */
455
+	public function getSchemaFormat()
456
+	{
457
+		return array();
458
+	}
459
+
460
+	/**
461
+	 * This returns the value of the $_schema_readonly property on the object.
462
+	 *
463
+	 * @return bool
464
+	 */
465
+	public function getSchemaReadonly()
466
+	{
467
+		return true;
468
+	}
469
+
470
+	/**
471
+	 * This returns elements used to represent this field in the json schema.
472
+	 *
473
+	 * @link http://json-schema.org/
474
+	 * @return array
475
+	 */
476
+	public function getSchema()
477
+	{
478
+		$schema = array(
479
+			'description' => $this->getSchemaDescription(),
480
+			'type' => $this->getSchemaType(),
481
+			'relation' => true,
482
+			'relation_type' => get_class($this),
483
+			'readonly' => $this->getSchemaReadonly()
484
+		);
485
+
486
+		if ($this instanceof EE_HABTM_Relation) {
487
+			$schema['joining_model_name'] = $this->get_join_model()->get_this_model_name();
488
+		}
489
+
490
+		if ($this->getSchemaType() === 'array') {
491
+			$schema['items'] = array(
492
+				'type' => 'object'
493
+			);
494
+		}
495
+
496
+		return $schema;
497
+	}
498 498
 }
Please login to merge, or discard this patch.
core/libraries/rest_api/controllers/model/Read.php 2 patches
Indentation   +1177 added lines, -1177 removed lines patch added patch discarded remove patch
@@ -9,7 +9,7 @@  discard block
 block discarded – undo
9 9
 use EE_Datetime_Field;
10 10
 
11 11
 if (! defined('EVENT_ESPRESSO_VERSION')) {
12
-    exit('No direct script access allowed');
12
+	exit('No direct script access allowed');
13 13
 }
14 14
 
15 15
 
@@ -27,1182 +27,1182 @@  discard block
 block discarded – undo
27 27
 
28 28
 
29 29
 
30
-    /**
31
-     * @var Calculated_Model_Fields
32
-     */
33
-    protected $_fields_calculator;
34
-
35
-
36
-
37
-    /**
38
-     * Read constructor.
39
-     */
40
-    public function __construct()
41
-    {
42
-        parent::__construct();
43
-        $this->_fields_calculator = new Calculated_Model_Fields();
44
-    }
45
-
46
-
47
-
48
-    /**
49
-     * Handles requests to get all (or a filtered subset) of entities for a particular model
50
-     *
51
-     * @param \WP_REST_Request $request
52
-     * @return \WP_REST_Response|\WP_Error
53
-     */
54
-    public static function handle_request_get_all(\WP_REST_Request $request)
55
-    {
56
-        $controller = new Read();
57
-        try {
58
-            $matches = $controller->parse_route(
59
-                $request->get_route(),
60
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
61
-                array('version', 'model')
62
-            );
63
-            $controller->set_requested_version($matches['version']);
64
-            $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
65
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
66
-                return $controller->send_response(
67
-                    new \WP_Error(
68
-                        'endpoint_parsing_error',
69
-                        sprintf(
70
-                            __('There is no model for endpoint %s. Please contact event espresso support',
71
-                                'event_espresso'),
72
-                            $model_name_singular
73
-                        )
74
-                    )
75
-                );
76
-            }
77
-            return $controller->send_response(
78
-                $controller->get_entities_from_model(
79
-                    $controller->get_model_version_info()->load_model($model_name_singular),
80
-                    $request
81
-                )
82
-            );
83
-        } catch (\Exception $e) {
84
-            return $controller->send_response($e);
85
-        }
86
-    }
87
-
88
-
89
-    /**
90
-     * Prepares and returns schema for any OPTIONS request.
91
-     *
92
-     * @param string $model_name  Something like `Event` or `Registration`
93
-     * @param string $version     The API endpoint version being used.
94
-     * @return array
95
-     */
96
-    public static function handle_schema_request($model_name, $version)
97
-    {
98
-        $controller = new Read();
99
-        try {
100
-            $controller->set_requested_version($version);
101
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
102
-                return array();
103
-            }
104
-            //get the model for this version
105
-            $model = $controller->get_model_version_info()->load_model($model_name);
106
-            $model_schema = new JsonModelSchema($model);
107
-            return $model_schema->getModelSchemaForRelations(
108
-                $controller->get_model_version_info()->relation_settings($model),
109
-                $controller->_add_extra_fields_to_schema(
110
-                    $model,
111
-                    $model_schema->getModelSchemaForFields(
112
-                        $controller->get_model_version_info()->fields_on_model_in_this_version($model),
113
-                        $model_schema->getInitialSchemaStructure()
114
-                    )
115
-                )
116
-            );
117
-        } catch (\Exception $e) {
118
-            return array();
119
-        }
120
-    }
121
-
122
-
123
-    /**
124
-     * Adds additional fields to the schema
125
-     * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
126
-     * needs to be added to the schema.
127
-     *
128
-     * @param \EEM_Base $model
129
-     * @param string    $schema
130
-     */
131
-    protected function _add_extra_fields_to_schema(\EEM_Base $model, $schema)
132
-    {
133
-        foreach ($this->get_model_version_info()->fields_on_model_in_this_version($model) as $field_name => $field) {
134
-            if ($field instanceof EE_Datetime_Field) {
135
-                $schema['properties'][$field_name . '_gmt'] = $field->getSchema();
136
-                //modify the description
137
-                $schema['properties'][$field_name . '_gmt']['description'] = sprintf(
138
-                    esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
139
-                    $field->get_nicename()
140
-                );
141
-            }
142
-        }
143
-        return $schema;
144
-    }
145
-
146
-
147
-
148
-
149
-    /**
150
-     * Used to figure out the route from the request when a `WP_REST_Request` object is not available
151
-     * @return string
152
-     */
153
-    protected function get_route_from_request() {
154
-        if (isset($GLOBALS['wp'])
155
-            && $GLOBALS['wp'] instanceof \WP
156
-            && isset($GLOBALS['wp']->query_vars['rest_route'] )
157
-        ) {
158
-            return $GLOBALS['wp']->query_vars['rest_route'];
159
-        } else {
160
-            return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
161
-        }
162
-    }
163
-
164
-
165
-
166
-    /**
167
-     * Gets a single entity related to the model indicated in the path and its id
168
-     *
169
-     * @param \WP_REST_Request $request
170
-     * @return \WP_REST_Response|\WP_Error
171
-     */
172
-    public static function handle_request_get_one(\WP_REST_Request $request)
173
-    {
174
-        $controller = new Read();
175
-        try {
176
-            $matches = $controller->parse_route(
177
-                $request->get_route(),
178
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
179
-                array('version', 'model', 'id'));
180
-            $controller->set_requested_version($matches['version']);
181
-            $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
182
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
183
-                return $controller->send_response(
184
-                    new \WP_Error(
185
-                        'endpoint_parsing_error',
186
-                        sprintf(
187
-                            __('There is no model for endpoint %s. Please contact event espresso support',
188
-                                'event_espresso'),
189
-                            $model_name_singular
190
-                        )
191
-                    )
192
-                );
193
-            }
194
-            return $controller->send_response(
195
-                $controller->get_entity_from_model(
196
-                    $controller->get_model_version_info()->load_model($model_name_singular),
197
-                    $request
198
-                )
199
-            );
200
-        } catch (\Exception $e) {
201
-            return $controller->send_response($e);
202
-        }
203
-    }
204
-
205
-
206
-
207
-    /**
208
-     * Gets all the related entities (or if its a belongs-to relation just the one)
209
-     * to the item with the given id
210
-     *
211
-     * @param \WP_REST_Request $request
212
-     * @return \WP_REST_Response|\WP_Error
213
-     */
214
-    public static function handle_request_get_related(\WP_REST_Request $request)
215
-    {
216
-        $controller = new Read();
217
-        try {
218
-            $matches = $controller->parse_route(
219
-                $request->get_route(),
220
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
221
-                array('version', 'model', 'id', 'related_model')
222
-            );
223
-            $controller->set_requested_version($matches['version']);
224
-            $main_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
225
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
226
-                return $controller->send_response(
227
-                    new \WP_Error(
228
-                        'endpoint_parsing_error',
229
-                        sprintf(
230
-                            __('There is no model for endpoint %s. Please contact event espresso support',
231
-                                'event_espresso'),
232
-                            $main_model_name_singular
233
-                        )
234
-                    )
235
-                );
236
-            }
237
-            $main_model = $controller->get_model_version_info()->load_model($main_model_name_singular);
238
-            //assume the related model name is plural and try to find the model's name
239
-            $related_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['related_model']);
240
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
241
-                //so the word didn't singularize well. Maybe that's just because it's a singular word?
242
-                $related_model_name_singular = \EEH_Inflector::humanize($matches['related_model']);
243
-            }
244
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
245
-                return $controller->send_response(
246
-                    new \WP_Error(
247
-                        'endpoint_parsing_error',
248
-                        sprintf(
249
-                            __('There is no model for endpoint %s. Please contact event espresso support',
250
-                                'event_espresso'),
251
-                            $related_model_name_singular
252
-                        )
253
-                    )
254
-                );
255
-            }
256
-            return $controller->send_response(
257
-                $controller->get_entities_from_relation(
258
-                    $request->get_param('id'),
259
-                    $main_model->related_settings_for($related_model_name_singular),
260
-                    $request
261
-                )
262
-            );
263
-        } catch (\Exception $e) {
264
-            return $controller->send_response($e);
265
-        }
266
-    }
267
-
268
-
269
-
270
-    /**
271
-     * Gets a collection for the given model and filters
272
-     *
273
-     * @param \EEM_Base        $model
274
-     * @param \WP_REST_Request $request
275
-     * @return array|\WP_Error
276
-     */
277
-    public function get_entities_from_model($model, $request)
278
-    {
279
-        $query_params = $this->create_model_query_params($model, $request->get_params());
280
-        if (! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
281
-            $model_name_plural = \EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
282
-            return new \WP_Error(
283
-                sprintf('rest_%s_cannot_list', $model_name_plural),
284
-                sprintf(
285
-                    __('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
286
-                    $model_name_plural,
287
-                    Capabilities::get_missing_permissions_string($model, $query_params['caps'])
288
-                ),
289
-                array('status' => 403)
290
-            );
291
-        }
292
-        if (! $request->get_header('no_rest_headers')) {
293
-            $this->_set_headers_from_query_params($model, $query_params);
294
-        }
295
-        /** @type array $results */
296
-        $results = $model->get_all_wpdb_results($query_params);
297
-        $nice_results = array();
298
-        foreach ($results as $result) {
299
-            $nice_results[] = $this->create_entity_from_wpdb_result(
300
-                $model,
301
-                $result,
302
-                $request
303
-            );
304
-        }
305
-        return $nice_results;
306
-    }
307
-
308
-
309
-
310
-    /**
311
-     * @param array                   $primary_model_query_params query params for finding the item from which
312
-     *                                                            relations will be based
313
-     * @param \EE_Model_Relation_Base $relation
314
-     * @param \WP_REST_Request        $request
315
-     * @return \WP_Error|array
316
-     */
317
-    protected function _get_entities_from_relation($primary_model_query_params, $relation, $request)
318
-    {
319
-        $context = $this->validate_context($request->get_param('caps'));
320
-        $model = $relation->get_this_model();
321
-        $related_model = $relation->get_other_model();
322
-        if (! isset($primary_model_query_params[0])) {
323
-            $primary_model_query_params[0] = array();
324
-        }
325
-        //check if they can access the 1st model object
326
-        $primary_model_query_params = array(
327
-            0       => $primary_model_query_params[0],
328
-            'limit' => 1,
329
-        );
330
-        if ($model instanceof \EEM_Soft_Delete_Base) {
331
-            $primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($primary_model_query_params);
332
-        }
333
-        $restricted_query_params = $primary_model_query_params;
334
-        $restricted_query_params['caps'] = $context;
335
-        $this->_set_debug_info('main model query params', $restricted_query_params);
336
-        $this->_set_debug_info('missing caps', Capabilities::get_missing_permissions_string($related_model, $context));
337
-        if (
338
-        ! (
339
-            Capabilities::current_user_has_partial_access_to($related_model, $context)
340
-            && $model->exists($restricted_query_params)
341
-        )
342
-        ) {
343
-            if ($relation instanceof \EE_Belongs_To_Relation) {
344
-                $related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
345
-            } else {
346
-                $related_model_name_maybe_plural = \EEH_Inflector::pluralize_and_lower($related_model->get_this_model_name());
347
-            }
348
-            return new \WP_Error(
349
-                sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
350
-                sprintf(
351
-                    __('Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
352
-                        'event_espresso'),
353
-                    $related_model_name_maybe_plural,
354
-                    $relation->get_this_model()->get_this_model_name(),
355
-                    implode(
356
-                        ',',
357
-                        array_keys(
358
-                            Capabilities::get_missing_permissions($related_model, $context)
359
-                        )
360
-                    )
361
-                ),
362
-                array('status' => 403)
363
-            );
364
-        }
365
-        $query_params = $this->create_model_query_params($relation->get_other_model(), $request->get_params());
366
-        foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
367
-            $query_params[0][$relation->get_this_model()->get_this_model_name()
368
-                             . '.'
369
-                             . $where_condition_key] = $where_condition_value;
370
-        }
371
-        $query_params['default_where_conditions'] = 'none';
372
-        $query_params['caps'] = $context;
373
-        if (! $request->get_header('no_rest_headers')) {
374
-            $this->_set_headers_from_query_params($relation->get_other_model(), $query_params);
375
-        }
376
-        /** @type array $results */
377
-        $results = $relation->get_other_model()->get_all_wpdb_results($query_params);
378
-        $nice_results = array();
379
-        foreach ($results as $result) {
380
-            $nice_result = $this->create_entity_from_wpdb_result(
381
-                $relation->get_other_model(),
382
-                $result,
383
-                $request
384
-            );
385
-            if ($relation instanceof \EE_HABTM_Relation) {
386
-                //put the unusual stuff (properties from the HABTM relation) first, and make sure
387
-                //if there are conflicts we prefer the properties from the main model
388
-                $join_model_result = $this->create_entity_from_wpdb_result(
389
-                    $relation->get_join_model(),
390
-                    $result,
391
-                    $request
392
-                );
393
-                $joined_result = array_merge($nice_result, $join_model_result);
394
-                //but keep the meta stuff from the main model
395
-                if (isset($nice_result['meta'])) {
396
-                    $joined_result['meta'] = $nice_result['meta'];
397
-                }
398
-                $nice_result = $joined_result;
399
-            }
400
-            $nice_results[] = $nice_result;
401
-        }
402
-        if ($relation instanceof \EE_Belongs_To_Relation) {
403
-            return array_shift($nice_results);
404
-        } else {
405
-            return $nice_results;
406
-        }
407
-    }
408
-
409
-
410
-
411
-    /**
412
-     * Gets the collection for given relation object
413
-     * The same as Read::get_entities_from_model(), except if the relation
414
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
415
-     * the join-model-object into the results
416
-     *
417
-     * @param string                  $id the ID of the thing we are fetching related stuff from
418
-     * @param \EE_Model_Relation_Base $relation
419
-     * @param \WP_REST_Request        $request
420
-     * @return array|\WP_Error
421
-     * @throws \EE_Error
422
-     */
423
-    public function get_entities_from_relation($id, $relation, $request)
424
-    {
425
-        if (! $relation->get_this_model()->has_primary_key_field()) {
426
-            throw new \EE_Error(
427
-                sprintf(
428
-                    __('Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
429
-                        'event_espresso'),
430
-                    $relation->get_this_model()->get_this_model_name()
431
-                )
432
-            );
433
-        }
434
-        return $this->_get_entities_from_relation(
435
-            array(
436
-                array(
437
-                    $relation->get_this_model()->primary_key_name() => $id,
438
-                ),
439
-            ),
440
-            $relation,
441
-            $request
442
-        );
443
-    }
444
-
445
-
446
-
447
-    /**
448
-     * Sets the headers that are based on the model and query params,
449
-     * like the total records. This should only be called on the original request
450
-     * from the client, not on subsequent internal
451
-     *
452
-     * @param \EEM_Base $model
453
-     * @param array     $query_params
454
-     * @return void
455
-     */
456
-    protected function _set_headers_from_query_params($model, $query_params)
457
-    {
458
-        $this->_set_debug_info('model query params', $query_params);
459
-        $this->_set_debug_info('missing caps',
460
-            Capabilities::get_missing_permissions_string($model, $query_params['caps']));
461
-        //normally the limit to a 2-part array, where the 2nd item is the limit
462
-        if (! isset($query_params['limit'])) {
463
-            $query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
464
-        }
465
-        if (is_array($query_params['limit'])) {
466
-            $limit_parts = $query_params['limit'];
467
-        } else {
468
-            $limit_parts = explode(',', $query_params['limit']);
469
-            if (count($limit_parts) == 1) {
470
-                $limit_parts = array(0, $limit_parts[0]);
471
-            }
472
-        }
473
-        //remove the group by and having parts of the query, as those will
474
-        //make the sql query return an array of values, instead of just a single value
475
-        unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
476
-        $count = $model->count($query_params, null, true);
477
-        $pages = $count / $limit_parts[1];
478
-        $this->_set_response_header('Total', $count, false);
479
-        $this->_set_response_header('PageSize', $limit_parts[1], false);
480
-        $this->_set_response_header('TotalPages', ceil($pages), false);
481
-    }
482
-
483
-
484
-
485
-    /**
486
-     * Changes database results into REST API entities
487
-     *
488
-     * @param \EEM_Base        $model
489
-     * @param array            $db_row     like results from $wpdb->get_results()
490
-     * @param \WP_REST_Request $rest_request
491
-     * @param string           $deprecated no longer used
492
-     * @return array ready for being converted into json for sending to client
493
-     */
494
-    public function create_entity_from_wpdb_result($model, $db_row, $rest_request, $deprecated = null)
495
-    {
496
-        if (! $rest_request instanceof \WP_REST_Request) {
497
-            //ok so this was called in the old style, where the 3rd arg was
498
-            //$include, and the 4th arg was $context
499
-            //now setup the request just to avoid fatal errors, although we won't be able
500
-            //to truly make use of it because it's kinda devoid of info
501
-            $rest_request = new \WP_REST_Request();
502
-            $rest_request->set_param('include', $rest_request);
503
-            $rest_request->set_param('caps', $deprecated);
504
-        }
505
-        if ($rest_request->get_param('caps') == null) {
506
-            $rest_request->set_param('caps', \EEM_Base::caps_read);
507
-        }
508
-        $entity_array = $this->_create_bare_entity_from_wpdb_results($model, $db_row);
509
-        $entity_array = $this->_add_extra_fields($model, $db_row, $entity_array);
510
-        $entity_array['_links'] = $this->_get_entity_links($model, $db_row, $entity_array);
511
-        $entity_array['_calculated_fields'] = $this->_get_entity_calculations($model, $db_row, $rest_request);
512
-        $entity_array = $this->_include_requested_models($model, $rest_request, $entity_array, $db_row);
513
-        $entity_array = apply_filters(
514
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
515
-            $entity_array,
516
-            $model,
517
-            $rest_request->get_param('caps'),
518
-            $rest_request,
519
-            $this
520
-        );
521
-        $result_without_inaccessible_fields = Capabilities::filter_out_inaccessible_entity_fields(
522
-            $entity_array,
523
-            $model,
524
-            $rest_request->get_param('caps'),
525
-            $this->get_model_version_info(),
526
-            $model->get_index_primary_key_string(
527
-                $model->deduce_fields_n_values_from_cols_n_values($db_row)
528
-            )
529
-        );
530
-        $this->_set_debug_info(
531
-            'inaccessible fields',
532
-            array_keys(array_diff_key($entity_array, $result_without_inaccessible_fields))
533
-        );
534
-        return apply_filters(
535
-            'FHEE__Read__create_entity_from_wpdb_results__entity_return',
536
-            $result_without_inaccessible_fields,
537
-            $model,
538
-            $rest_request->get_param('caps')
539
-        );
540
-    }
541
-
542
-
543
-
544
-    /**
545
-     * Creates a REST entity array (JSON object we're going to return in the response, but
546
-     * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
547
-     * from $wpdb->get_row( $sql, ARRAY_A)
548
-     *
549
-     * @param \EEM_Base $model
550
-     * @param array     $db_row
551
-     * @return array entity mostly ready for converting to JSON and sending in the response
552
-     */
553
-    protected function _create_bare_entity_from_wpdb_results(\EEM_Base $model, $db_row)
554
-    {
555
-        $result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
556
-        $result = array_intersect_key($result,
557
-            $this->get_model_version_info()->fields_on_model_in_this_version($model));
558
-        foreach ($result as $field_name => $raw_field_value) {
559
-            $field_obj = $model->field_settings_for($field_name);
560
-            $field_value = $field_obj->prepare_for_set_from_db($raw_field_value);
561
-            if ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_ignored())) {
562
-                unset($result[$field_name]);
563
-            } elseif (
564
-            $this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_rendered_format())
565
-            ) {
566
-                $result[$field_name] = array(
567
-                    'raw'      => $field_obj->prepare_for_get($field_value),
568
-                    'rendered' => $field_obj->prepare_for_pretty_echoing($field_value),
569
-                );
570
-            } elseif (
571
-            $this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_pretty_format())
572
-            ) {
573
-                $result[$field_name] = array(
574
-                    'raw'    => $field_obj->prepare_for_get($field_value),
575
-                    'pretty' => $field_obj->prepare_for_pretty_echoing($field_value),
576
-                );
577
-            } elseif ($field_obj instanceof \EE_Datetime_Field) {
578
-                if ($field_value instanceof \DateTime) {
579
-                    $timezone = $field_value->getTimezone();
580
-                    $field_value->setTimezone(new \DateTimeZone('UTC'));
581
-                    $result[$field_name . '_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
582
-                        $field_obj,
583
-                        $field_value,
584
-                        $this->get_model_version_info()->requested_version()
585
-                    );
586
-                    $field_value->setTimezone($timezone);
587
-                    $result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
588
-                        $field_obj,
589
-                        $field_value,
590
-                        $this->get_model_version_info()->requested_version()
591
-                    );
592
-                }
593
-            } else {
594
-                $result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
595
-                    $field_obj,
596
-                    $field_obj->prepare_for_get($field_value),
597
-                    $this->get_model_version_info()->requested_version()
598
-                );
599
-            }
600
-        }
601
-        return $result;
602
-    }
603
-
604
-
605
-
606
-    /**
607
-     * Adds a few extra fields to the entity response
608
-     *
609
-     * @param \EEM_Base $model
610
-     * @param array     $db_row
611
-     * @param array     $entity_array
612
-     * @return array modified entity
613
-     */
614
-    protected function _add_extra_fields(\EEM_Base $model, $db_row, $entity_array)
615
-    {
616
-        if ($model instanceof \EEM_CPT_Base) {
617
-            $entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
618
-        }
619
-        return $entity_array;
620
-    }
621
-
622
-
623
-
624
-    /**
625
-     * Gets links we want to add to the response
626
-     *
627
-     * @global \WP_REST_Server $wp_rest_server
628
-     * @param \EEM_Base        $model
629
-     * @param array            $db_row
630
-     * @param array            $entity_array
631
-     * @return array the _links item in the entity
632
-     */
633
-    protected function _get_entity_links($model, $db_row, $entity_array)
634
-    {
635
-        //add basic links
636
-        $links = array();
637
-        if ($model->has_primary_key_field()) {
638
-            $links['self'] = array(
639
-                array(
640
-                    'href' => $this->get_versioned_link_to(
641
-                        \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
642
-                        . '/'
643
-                        . $entity_array[$model->primary_key_name()]
644
-                    ),
645
-                ),
646
-            );
647
-        }
648
-        $links['collection'] = array(
649
-            array(
650
-                'href' => $this->get_versioned_link_to(
651
-                    \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
652
-                ),
653
-            ),
654
-        );
655
-        //add links to related models
656
-        if ($model->has_primary_key_field()) {
657
-            foreach ($this->get_model_version_info()->relation_settings($model) as $relation_name => $relation_obj) {
658
-                $related_model_part = Read::get_related_entity_name($relation_name, $relation_obj);
659
-                $links[\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
660
-                    array(
661
-                        'href'   => $this->get_versioned_link_to(
662
-                            \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
663
-                            . '/'
664
-                            . $entity_array[$model->primary_key_name()]
665
-                            . '/'
666
-                            . $related_model_part
667
-                        ),
668
-                        'single' => $relation_obj instanceof \EE_Belongs_To_Relation ? true : false,
669
-                    ),
670
-                );
671
-            }
672
-        }
673
-        return $links;
674
-    }
675
-
676
-
677
-
678
-    /**
679
-     * Adds the included models indicated in the request to the entity provided
680
-     *
681
-     * @param \EEM_Base        $model
682
-     * @param \WP_REST_Request $rest_request
683
-     * @param array            $entity_array
684
-     * @param array            $db_row
685
-     * @return array the modified entity
686
-     */
687
-    protected function _include_requested_models(
688
-        \EEM_Base $model,
689
-        \WP_REST_Request $rest_request,
690
-        $entity_array,
691
-        $db_row = array()
692
-    ) {
693
-        //if $db_row not included, hope the entity array has what we need
694
-        if (! $db_row) {
695
-            $db_row = $entity_array;
696
-        }
697
-        $includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), '');
698
-        $includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model);
699
-        //if they passed in * or didn't specify any includes, return everything
700
-        if (! in_array('*', $includes_for_this_model)
701
-            && ! empty($includes_for_this_model)
702
-        ) {
703
-            if ($model->has_primary_key_field()) {
704
-                //always include the primary key. ya just gotta know that at least
705
-                $includes_for_this_model[] = $model->primary_key_name();
706
-            }
707
-            if ($this->explode_and_get_items_prefixed_with($rest_request->get_param('calculate'), '')) {
708
-                $includes_for_this_model[] = '_calculated_fields';
709
-            }
710
-            $entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
711
-        }
712
-        $relation_settings = $this->get_model_version_info()->relation_settings($model);
713
-        foreach ($relation_settings as $relation_name => $relation_obj) {
714
-            $related_fields_to_include = $this->explode_and_get_items_prefixed_with(
715
-                $rest_request->get_param('include'),
716
-                $relation_name
717
-            );
718
-            $related_fields_to_calculate = $this->explode_and_get_items_prefixed_with(
719
-                $rest_request->get_param('calculate'),
720
-                $relation_name
721
-            );
722
-            //did they specify they wanted to include a related model, or
723
-            //specific fields from a related model?
724
-            //or did they specify to calculate a field from a related model?
725
-            if ($related_fields_to_include || $related_fields_to_calculate) {
726
-                //if so, we should include at least some part of the related model
727
-                $pretend_related_request = new \WP_REST_Request();
728
-                $pretend_related_request->set_query_params(
729
-                    array(
730
-                        'caps'      => $rest_request->get_param('caps'),
731
-                        'include'   => $related_fields_to_include,
732
-                        'calculate' => $related_fields_to_calculate,
733
-                    )
734
-                );
735
-                $pretend_related_request->add_header('no_rest_headers', true);
736
-                $primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
737
-                    $model->get_index_primary_key_string(
738
-                        $model->deduce_fields_n_values_from_cols_n_values($db_row)
739
-                    )
740
-                );
741
-                $related_results = $this->_get_entities_from_relation(
742
-                    $primary_model_query_params,
743
-                    $relation_obj,
744
-                    $pretend_related_request
745
-                );
746
-                $entity_array[Read::get_related_entity_name($relation_name, $relation_obj)] = $related_results
747
-                                                                                              instanceof
748
-                                                                                              \WP_Error
749
-                    ? null
750
-                    : $related_results;
751
-            }
752
-        }
753
-        return $entity_array;
754
-    }
755
-
756
-
757
-
758
-    /**
759
-     * Returns a new array with all the names of models removed. Eg
760
-     * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
761
-     *
762
-     * @param array $arr
763
-     * @return array
764
-     */
765
-    private function _remove_model_names_from_array($arr)
766
-    {
767
-        return array_diff($arr, array_keys(\EE_Registry::instance()->non_abstract_db_models));
768
-    }
769
-
770
-
771
-
772
-    /**
773
-     * Gets the calculated fields for the response
774
-     *
775
-     * @param \EEM_Base        $model
776
-     * @param array            $wpdb_row
777
-     * @param \WP_REST_Request $rest_request
778
-     * @return \stdClass the _calculations item in the entity
779
-     */
780
-    protected function _get_entity_calculations($model, $wpdb_row, $rest_request)
781
-    {
782
-        $calculated_fields = $this->explode_and_get_items_prefixed_with(
783
-            $rest_request->get_param('calculate'),
784
-            ''
785
-        );
786
-        //note: setting calculate=* doesn't do anything
787
-        $calculated_fields_to_return = new \stdClass();
788
-        foreach ($calculated_fields as $field_to_calculate) {
789
-            try {
790
-                $calculated_fields_to_return->$field_to_calculate = Model_Data_Translator::prepare_field_value_for_json(
791
-                    null,
792
-                    $this->_fields_calculator->retrieve_calculated_field_value(
793
-                        $model,
794
-                        $field_to_calculate,
795
-                        $wpdb_row,
796
-                        $rest_request,
797
-                        $this
798
-                    ),
799
-                    $this->get_model_version_info()->requested_version()
800
-                );
801
-            } catch (Rest_Exception $e) {
802
-                //if we don't have permission to read it, just leave it out. but let devs know about the problem
803
-                $this->_set_response_header(
804
-                    'Notices-Field-Calculation-Errors['
805
-                    . $e->get_string_code()
806
-                    . ']['
807
-                    . $model->get_this_model_name()
808
-                    . ']['
809
-                    . $field_to_calculate
810
-                    . ']',
811
-                    $e->getMessage(),
812
-                    true
813
-                );
814
-            }
815
-        }
816
-        return $calculated_fields_to_return;
817
-    }
818
-
819
-
820
-
821
-    /**
822
-     * Gets the full URL to the resource, taking the requested version into account
823
-     *
824
-     * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
825
-     * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
826
-     */
827
-    public function get_versioned_link_to($link_part_after_version_and_slash)
828
-    {
829
-        return rest_url(
830
-            \EED_Core_Rest_Api::ee_api_namespace
831
-            . $this->get_model_version_info()->requested_version()
832
-            . '/'
833
-            . $link_part_after_version_and_slash
834
-        );
835
-    }
836
-
837
-
838
-
839
-    /**
840
-     * Gets the correct lowercase name for the relation in the API according
841
-     * to the relation's type
842
-     *
843
-     * @param string                  $relation_name
844
-     * @param \EE_Model_Relation_Base $relation_obj
845
-     * @return string
846
-     */
847
-    public static function get_related_entity_name($relation_name, $relation_obj)
848
-    {
849
-        if ($relation_obj instanceof \EE_Belongs_To_Relation) {
850
-            return strtolower($relation_name);
851
-        } else {
852
-            return \EEH_Inflector::pluralize_and_lower($relation_name);
853
-        }
854
-    }
855
-
856
-
857
-
858
-    /**
859
-     * Gets the one model object with the specified id for the specified model
860
-     *
861
-     * @param \EEM_Base        $model
862
-     * @param \WP_REST_Request $request
863
-     * @return array|\WP_Error
864
-     */
865
-    public function get_entity_from_model($model, $request)
866
-    {
867
-        $query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
868
-        if ($model instanceof \EEM_Soft_Delete_Base) {
869
-            $query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
870
-        }
871
-        $restricted_query_params = $query_params;
872
-        $restricted_query_params['caps'] = $this->validate_context($request->get_param('caps'));
873
-        $this->_set_debug_info('model query params', $restricted_query_params);
874
-        $model_rows = $model->get_all_wpdb_results($restricted_query_params);
875
-        if (! empty ($model_rows)) {
876
-            return $this->create_entity_from_wpdb_result(
877
-                $model,
878
-                array_shift($model_rows),
879
-                $request);
880
-        } else {
881
-            //ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
882
-            $lowercase_model_name = strtolower($model->get_this_model_name());
883
-            $model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
884
-            if (! empty($model_rows_found_sans_restrictions)) {
885
-                //you got shafted- it existed but we didn't want to tell you!
886
-                return new \WP_Error(
887
-                    'rest_user_cannot_read',
888
-                    sprintf(
889
-                        __('Sorry, you cannot read this %1$s. Missing permissions are: %2$s', 'event_espresso'),
890
-                        strtolower($model->get_this_model_name()),
891
-                        Capabilities::get_missing_permissions_string(
892
-                            $model,
893
-                            $this->validate_context($request->get_param('caps')))
894
-                    ),
895
-                    array('status' => 403)
896
-                );
897
-            } else {
898
-                //it's not you. It just doesn't exist
899
-                return new \WP_Error(
900
-                    sprintf('rest_%s_invalid_id', $lowercase_model_name),
901
-                    sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
902
-                    array('status' => 404)
903
-                );
904
-            }
905
-        }
906
-    }
907
-
908
-
909
-
910
-    /**
911
-     * If a context is provided which isn't valid, maybe it was added in a future
912
-     * version so just treat it as a default read
913
-     *
914
-     * @param string $context
915
-     * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
916
-     */
917
-    public function validate_context($context)
918
-    {
919
-        if (! $context) {
920
-            $context = \EEM_Base::caps_read;
921
-        }
922
-        $valid_contexts = \EEM_Base::valid_cap_contexts();
923
-        if (in_array($context, $valid_contexts)) {
924
-            return $context;
925
-        } else {
926
-            return \EEM_Base::caps_read;
927
-        }
928
-    }
929
-
930
-
931
-
932
-    /**
933
-     * Verifies the passed in value is an allowable default where conditions value.
934
-     *
935
-     * @param $default_query_params
936
-     * @return string
937
-     */
938
-    public function validate_default_query_params($default_query_params)
939
-    {
940
-        $valid_default_where_conditions_for_api_calls = array(
941
-            \EEM_Base::default_where_conditions_all,
942
-            \EEM_Base::default_where_conditions_minimum_all,
943
-            \EEM_Base::default_where_conditions_minimum_others,
944
-        );
945
-        if (! $default_query_params) {
946
-            $default_query_params = \EEM_Base::default_where_conditions_all;
947
-        }
948
-        if (
949
-        in_array(
950
-            $default_query_params,
951
-            $valid_default_where_conditions_for_api_calls,
952
-            true
953
-        )
954
-        ) {
955
-            return $default_query_params;
956
-        } else {
957
-            return \EEM_Base::default_where_conditions_all;
958
-        }
959
-    }
960
-
961
-
962
-
963
-    /**
964
-     * Translates API filter get parameter into $query_params array used by EEM_Base::get_all().
965
-     * Note: right now the query parameter keys for fields (and related fields)
966
-     * can be left as-is, but it's quite possible this will change someday.
967
-     * Also, this method's contents might be candidate for moving to Model_Data_Translator
968
-     *
969
-     * @param \EEM_Base $model
970
-     * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
971
-     * @return array like what EEM_Base::get_all() expects or FALSE to indicate
972
-     *                                    that absolutely no results should be returned
973
-     * @throws \EE_Error
974
-     */
975
-    public function create_model_query_params($model, $query_parameters)
976
-    {
977
-        $model_query_params = array();
978
-        if (isset($query_parameters['where'])) {
979
-            $model_query_params[0] = Model_Data_Translator::prepare_conditions_query_params_for_models(
980
-                $query_parameters['where'],
981
-                $model,
982
-                $this->get_model_version_info()->requested_version()
983
-            );
984
-        }
985
-        if (isset($query_parameters['order_by'])) {
986
-            $order_by = $query_parameters['order_by'];
987
-        } elseif (isset($query_parameters['orderby'])) {
988
-            $order_by = $query_parameters['orderby'];
989
-        } else {
990
-            $order_by = null;
991
-        }
992
-        if ($order_by !== null) {
993
-            if (is_array($order_by)) {
994
-                $order_by = Model_Data_Translator::prepare_field_names_in_array_keys_from_json($order_by);
995
-            } else {
996
-                //it's a single item
997
-                $order_by = Model_Data_Translator::prepare_field_name_from_json($order_by);
998
-            }
999
-            $model_query_params['order_by'] = $order_by;
1000
-        }
1001
-        if (isset($query_parameters['group_by'])) {
1002
-            $group_by = $query_parameters['group_by'];
1003
-        } elseif (isset($query_parameters['groupby'])) {
1004
-            $group_by = $query_parameters['groupby'];
1005
-        } else {
1006
-            $group_by = array_keys($model->get_combined_primary_key_fields());
1007
-        }
1008
-        //make sure they're all real names
1009
-        if (is_array($group_by)) {
1010
-            $group_by = Model_Data_Translator::prepare_field_names_from_json($group_by);
1011
-        }
1012
-        if ($group_by !== null) {
1013
-            $model_query_params['group_by'] = $group_by;
1014
-        }
1015
-        if (isset($query_parameters['having'])) {
1016
-            $model_query_params['having'] = Model_Data_Translator::prepare_conditions_query_params_for_models(
1017
-                $query_parameters['having'],
1018
-                $model,
1019
-                $this->get_model_version_info()->requested_version()
1020
-            );
1021
-        }
1022
-        if (isset($query_parameters['order'])) {
1023
-            $model_query_params['order'] = $query_parameters['order'];
1024
-        }
1025
-        if (isset($query_parameters['mine'])) {
1026
-            $model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1027
-        }
1028
-        if (isset($query_parameters['limit'])) {
1029
-            //limit should be either a string like '23' or '23,43', or an array with two items in it
1030
-            if (! is_array($query_parameters['limit'])) {
1031
-                $limit_array = explode(',', (string)$query_parameters['limit']);
1032
-            } else {
1033
-                $limit_array = $query_parameters['limit'];
1034
-            }
1035
-            $sanitized_limit = array();
1036
-            foreach ($limit_array as $key => $limit_part) {
1037
-                if ($this->_debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1038
-                    throw new \EE_Error(
1039
-                        sprintf(
1040
-                            __('An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
1041
-                                'event_espresso'),
1042
-                            wp_json_encode($query_parameters['limit'])
1043
-                        )
1044
-                    );
1045
-                }
1046
-                $sanitized_limit[] = (int)$limit_part;
1047
-            }
1048
-            $model_query_params['limit'] = implode(',', $sanitized_limit);
1049
-        } else {
1050
-            $model_query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
1051
-        }
1052
-        if (isset($query_parameters['caps'])) {
1053
-            $model_query_params['caps'] = $this->validate_context($query_parameters['caps']);
1054
-        } else {
1055
-            $model_query_params['caps'] = \EEM_Base::caps_read;
1056
-        }
1057
-        if (isset($query_parameters['default_where_conditions'])) {
1058
-            $model_query_params['default_where_conditions'] = $this->validate_default_query_params($query_parameters['default_where_conditions']);
1059
-        }
1060
-        return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model);
1061
-    }
1062
-
1063
-
1064
-
1065
-    /**
1066
-     * Changes the REST-style query params for use in the models
1067
-     *
1068
-     * @deprecated
1069
-     * @param \EEM_Base $model
1070
-     * @param array     $query_params sub-array from @see EEM_Base::get_all()
1071
-     * @return array
1072
-     */
1073
-    public function prepare_rest_query_params_key_for_models($model, $query_params)
1074
-    {
1075
-        $model_ready_query_params = array();
1076
-        foreach ($query_params as $key => $value) {
1077
-            if (is_array($value)) {
1078
-                $model_ready_query_params[$key] = $this->prepare_rest_query_params_key_for_models($model, $value);
1079
-            } else {
1080
-                $model_ready_query_params[$key] = $value;
1081
-            }
1082
-        }
1083
-        return $model_ready_query_params;
1084
-    }
1085
-
1086
-
1087
-
1088
-    /**
1089
-     * @deprecated
1090
-     * @param $model
1091
-     * @param $query_params
1092
-     * @return array
1093
-     */
1094
-    public function prepare_rest_query_params_values_for_models($model, $query_params)
1095
-    {
1096
-        $model_ready_query_params = array();
1097
-        foreach ($query_params as $key => $value) {
1098
-            if (is_array($value)) {
1099
-                $model_ready_query_params[$key] = $this->prepare_rest_query_params_values_for_models($model, $value);
1100
-            } else {
1101
-                $model_ready_query_params[$key] = $value;
1102
-            }
1103
-        }
1104
-        return $model_ready_query_params;
1105
-    }
1106
-
1107
-
1108
-
1109
-    /**
1110
-     * Explodes the string on commas, and only returns items with $prefix followed by a period.
1111
-     * If no prefix is specified, returns items with no period.
1112
-     *
1113
-     * @param string|array $string_to_explode eg "jibba,jabba, blah, blaabla" or array('jibba', 'jabba' )
1114
-     * @param string       $prefix            "Event" or "foobar"
1115
-     * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1116
-     *                                        we only return strings starting with that and a period; if no prefix was
1117
-     *                                        specified we return all items containing NO periods
1118
-     */
1119
-    public function explode_and_get_items_prefixed_with($string_to_explode, $prefix)
1120
-    {
1121
-        if (is_string($string_to_explode)) {
1122
-            $exploded_contents = explode(',', $string_to_explode);
1123
-        } else if (is_array($string_to_explode)) {
1124
-            $exploded_contents = $string_to_explode;
1125
-        } else {
1126
-            $exploded_contents = array();
1127
-        }
1128
-        //if the string was empty, we want an empty array
1129
-        $exploded_contents = array_filter($exploded_contents);
1130
-        $contents_with_prefix = array();
1131
-        foreach ($exploded_contents as $item) {
1132
-            $item = trim($item);
1133
-            //if no prefix was provided, so we look for items with no "." in them
1134
-            if (! $prefix) {
1135
-                //does this item have a period?
1136
-                if (strpos($item, '.') === false) {
1137
-                    //if not, then its what we're looking for
1138
-                    $contents_with_prefix[] = $item;
1139
-                }
1140
-            } else if (strpos($item, $prefix . '.') === 0) {
1141
-                //this item has the prefix and a period, grab it
1142
-                $contents_with_prefix[] = substr(
1143
-                    $item,
1144
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1145
-                );
1146
-            } else if ($item === $prefix) {
1147
-                //this item is JUST the prefix
1148
-                //so let's grab everything after, which is a blank string
1149
-                $contents_with_prefix[] = '';
1150
-            }
1151
-        }
1152
-        return $contents_with_prefix;
1153
-    }
1154
-
1155
-
1156
-
1157
-    /**
1158
-     * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1159
-     * Deprecated because its return values were really quite confusing- sometimes it returned
1160
-     * an empty array (when the include string was blank or '*') or sometimes it returned
1161
-     * array('*') (when you provided a model and a model of that kind was found).
1162
-     * Parses the $include_string so we fetch all the field names relating to THIS model
1163
-     * (ie have NO period in them), or for the provided model (ie start with the model
1164
-     * name and then a period).
1165
-     * @param string $include_string @see Read:handle_request_get_all
1166
-     * @param string $model_name
1167
-     * @return array of fields for this model. If $model_name is provided, then
1168
-     *                               the fields for that model, with the model's name removed from each.
1169
-     *                               If $include_string was blank or '*' returns an empty array
1170
-     */
1171
-    public function extract_includes_for_this_model($include_string, $model_name = null)
1172
-    {
1173
-        if (is_array($include_string)) {
1174
-            $include_string = implode(',', $include_string);
1175
-        }
1176
-        if ($include_string === '*' || $include_string === '') {
1177
-            return array();
1178
-        }
1179
-        $includes = explode(',', $include_string);
1180
-        $extracted_fields_to_include = array();
1181
-        if ($model_name) {
1182
-            foreach ($includes as $field_to_include) {
1183
-                $field_to_include = trim($field_to_include);
1184
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1185
-                    //found the model name at the exact start
1186
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1187
-                    $extracted_fields_to_include[] = $field_sans_model_name;
1188
-                } elseif ($field_to_include == $model_name) {
1189
-                    $extracted_fields_to_include[] = '*';
1190
-                }
1191
-            }
1192
-        } else {
1193
-            //look for ones with no period
1194
-            foreach ($includes as $field_to_include) {
1195
-                $field_to_include = trim($field_to_include);
1196
-                if (
1197
-                    strpos($field_to_include, '.') === false
1198
-                    && ! $this->get_model_version_info()->is_model_name_in_this_version($field_to_include)
1199
-                ) {
1200
-                    $extracted_fields_to_include[] = $field_to_include;
1201
-                }
1202
-            }
1203
-        }
1204
-        return $extracted_fields_to_include;
1205
-    }
30
+	/**
31
+	 * @var Calculated_Model_Fields
32
+	 */
33
+	protected $_fields_calculator;
34
+
35
+
36
+
37
+	/**
38
+	 * Read constructor.
39
+	 */
40
+	public function __construct()
41
+	{
42
+		parent::__construct();
43
+		$this->_fields_calculator = new Calculated_Model_Fields();
44
+	}
45
+
46
+
47
+
48
+	/**
49
+	 * Handles requests to get all (or a filtered subset) of entities for a particular model
50
+	 *
51
+	 * @param \WP_REST_Request $request
52
+	 * @return \WP_REST_Response|\WP_Error
53
+	 */
54
+	public static function handle_request_get_all(\WP_REST_Request $request)
55
+	{
56
+		$controller = new Read();
57
+		try {
58
+			$matches = $controller->parse_route(
59
+				$request->get_route(),
60
+				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
61
+				array('version', 'model')
62
+			);
63
+			$controller->set_requested_version($matches['version']);
64
+			$model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
65
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
66
+				return $controller->send_response(
67
+					new \WP_Error(
68
+						'endpoint_parsing_error',
69
+						sprintf(
70
+							__('There is no model for endpoint %s. Please contact event espresso support',
71
+								'event_espresso'),
72
+							$model_name_singular
73
+						)
74
+					)
75
+				);
76
+			}
77
+			return $controller->send_response(
78
+				$controller->get_entities_from_model(
79
+					$controller->get_model_version_info()->load_model($model_name_singular),
80
+					$request
81
+				)
82
+			);
83
+		} catch (\Exception $e) {
84
+			return $controller->send_response($e);
85
+		}
86
+	}
87
+
88
+
89
+	/**
90
+	 * Prepares and returns schema for any OPTIONS request.
91
+	 *
92
+	 * @param string $model_name  Something like `Event` or `Registration`
93
+	 * @param string $version     The API endpoint version being used.
94
+	 * @return array
95
+	 */
96
+	public static function handle_schema_request($model_name, $version)
97
+	{
98
+		$controller = new Read();
99
+		try {
100
+			$controller->set_requested_version($version);
101
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
102
+				return array();
103
+			}
104
+			//get the model for this version
105
+			$model = $controller->get_model_version_info()->load_model($model_name);
106
+			$model_schema = new JsonModelSchema($model);
107
+			return $model_schema->getModelSchemaForRelations(
108
+				$controller->get_model_version_info()->relation_settings($model),
109
+				$controller->_add_extra_fields_to_schema(
110
+					$model,
111
+					$model_schema->getModelSchemaForFields(
112
+						$controller->get_model_version_info()->fields_on_model_in_this_version($model),
113
+						$model_schema->getInitialSchemaStructure()
114
+					)
115
+				)
116
+			);
117
+		} catch (\Exception $e) {
118
+			return array();
119
+		}
120
+	}
121
+
122
+
123
+	/**
124
+	 * Adds additional fields to the schema
125
+	 * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
126
+	 * needs to be added to the schema.
127
+	 *
128
+	 * @param \EEM_Base $model
129
+	 * @param string    $schema
130
+	 */
131
+	protected function _add_extra_fields_to_schema(\EEM_Base $model, $schema)
132
+	{
133
+		foreach ($this->get_model_version_info()->fields_on_model_in_this_version($model) as $field_name => $field) {
134
+			if ($field instanceof EE_Datetime_Field) {
135
+				$schema['properties'][$field_name . '_gmt'] = $field->getSchema();
136
+				//modify the description
137
+				$schema['properties'][$field_name . '_gmt']['description'] = sprintf(
138
+					esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
139
+					$field->get_nicename()
140
+				);
141
+			}
142
+		}
143
+		return $schema;
144
+	}
145
+
146
+
147
+
148
+
149
+	/**
150
+	 * Used to figure out the route from the request when a `WP_REST_Request` object is not available
151
+	 * @return string
152
+	 */
153
+	protected function get_route_from_request() {
154
+		if (isset($GLOBALS['wp'])
155
+			&& $GLOBALS['wp'] instanceof \WP
156
+			&& isset($GLOBALS['wp']->query_vars['rest_route'] )
157
+		) {
158
+			return $GLOBALS['wp']->query_vars['rest_route'];
159
+		} else {
160
+			return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
161
+		}
162
+	}
163
+
164
+
165
+
166
+	/**
167
+	 * Gets a single entity related to the model indicated in the path and its id
168
+	 *
169
+	 * @param \WP_REST_Request $request
170
+	 * @return \WP_REST_Response|\WP_Error
171
+	 */
172
+	public static function handle_request_get_one(\WP_REST_Request $request)
173
+	{
174
+		$controller = new Read();
175
+		try {
176
+			$matches = $controller->parse_route(
177
+				$request->get_route(),
178
+				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
179
+				array('version', 'model', 'id'));
180
+			$controller->set_requested_version($matches['version']);
181
+			$model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
182
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
183
+				return $controller->send_response(
184
+					new \WP_Error(
185
+						'endpoint_parsing_error',
186
+						sprintf(
187
+							__('There is no model for endpoint %s. Please contact event espresso support',
188
+								'event_espresso'),
189
+							$model_name_singular
190
+						)
191
+					)
192
+				);
193
+			}
194
+			return $controller->send_response(
195
+				$controller->get_entity_from_model(
196
+					$controller->get_model_version_info()->load_model($model_name_singular),
197
+					$request
198
+				)
199
+			);
200
+		} catch (\Exception $e) {
201
+			return $controller->send_response($e);
202
+		}
203
+	}
204
+
205
+
206
+
207
+	/**
208
+	 * Gets all the related entities (or if its a belongs-to relation just the one)
209
+	 * to the item with the given id
210
+	 *
211
+	 * @param \WP_REST_Request $request
212
+	 * @return \WP_REST_Response|\WP_Error
213
+	 */
214
+	public static function handle_request_get_related(\WP_REST_Request $request)
215
+	{
216
+		$controller = new Read();
217
+		try {
218
+			$matches = $controller->parse_route(
219
+				$request->get_route(),
220
+				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
221
+				array('version', 'model', 'id', 'related_model')
222
+			);
223
+			$controller->set_requested_version($matches['version']);
224
+			$main_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
225
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
226
+				return $controller->send_response(
227
+					new \WP_Error(
228
+						'endpoint_parsing_error',
229
+						sprintf(
230
+							__('There is no model for endpoint %s. Please contact event espresso support',
231
+								'event_espresso'),
232
+							$main_model_name_singular
233
+						)
234
+					)
235
+				);
236
+			}
237
+			$main_model = $controller->get_model_version_info()->load_model($main_model_name_singular);
238
+			//assume the related model name is plural and try to find the model's name
239
+			$related_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['related_model']);
240
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
241
+				//so the word didn't singularize well. Maybe that's just because it's a singular word?
242
+				$related_model_name_singular = \EEH_Inflector::humanize($matches['related_model']);
243
+			}
244
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
245
+				return $controller->send_response(
246
+					new \WP_Error(
247
+						'endpoint_parsing_error',
248
+						sprintf(
249
+							__('There is no model for endpoint %s. Please contact event espresso support',
250
+								'event_espresso'),
251
+							$related_model_name_singular
252
+						)
253
+					)
254
+				);
255
+			}
256
+			return $controller->send_response(
257
+				$controller->get_entities_from_relation(
258
+					$request->get_param('id'),
259
+					$main_model->related_settings_for($related_model_name_singular),
260
+					$request
261
+				)
262
+			);
263
+		} catch (\Exception $e) {
264
+			return $controller->send_response($e);
265
+		}
266
+	}
267
+
268
+
269
+
270
+	/**
271
+	 * Gets a collection for the given model and filters
272
+	 *
273
+	 * @param \EEM_Base        $model
274
+	 * @param \WP_REST_Request $request
275
+	 * @return array|\WP_Error
276
+	 */
277
+	public function get_entities_from_model($model, $request)
278
+	{
279
+		$query_params = $this->create_model_query_params($model, $request->get_params());
280
+		if (! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
281
+			$model_name_plural = \EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
282
+			return new \WP_Error(
283
+				sprintf('rest_%s_cannot_list', $model_name_plural),
284
+				sprintf(
285
+					__('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
286
+					$model_name_plural,
287
+					Capabilities::get_missing_permissions_string($model, $query_params['caps'])
288
+				),
289
+				array('status' => 403)
290
+			);
291
+		}
292
+		if (! $request->get_header('no_rest_headers')) {
293
+			$this->_set_headers_from_query_params($model, $query_params);
294
+		}
295
+		/** @type array $results */
296
+		$results = $model->get_all_wpdb_results($query_params);
297
+		$nice_results = array();
298
+		foreach ($results as $result) {
299
+			$nice_results[] = $this->create_entity_from_wpdb_result(
300
+				$model,
301
+				$result,
302
+				$request
303
+			);
304
+		}
305
+		return $nice_results;
306
+	}
307
+
308
+
309
+
310
+	/**
311
+	 * @param array                   $primary_model_query_params query params for finding the item from which
312
+	 *                                                            relations will be based
313
+	 * @param \EE_Model_Relation_Base $relation
314
+	 * @param \WP_REST_Request        $request
315
+	 * @return \WP_Error|array
316
+	 */
317
+	protected function _get_entities_from_relation($primary_model_query_params, $relation, $request)
318
+	{
319
+		$context = $this->validate_context($request->get_param('caps'));
320
+		$model = $relation->get_this_model();
321
+		$related_model = $relation->get_other_model();
322
+		if (! isset($primary_model_query_params[0])) {
323
+			$primary_model_query_params[0] = array();
324
+		}
325
+		//check if they can access the 1st model object
326
+		$primary_model_query_params = array(
327
+			0       => $primary_model_query_params[0],
328
+			'limit' => 1,
329
+		);
330
+		if ($model instanceof \EEM_Soft_Delete_Base) {
331
+			$primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($primary_model_query_params);
332
+		}
333
+		$restricted_query_params = $primary_model_query_params;
334
+		$restricted_query_params['caps'] = $context;
335
+		$this->_set_debug_info('main model query params', $restricted_query_params);
336
+		$this->_set_debug_info('missing caps', Capabilities::get_missing_permissions_string($related_model, $context));
337
+		if (
338
+		! (
339
+			Capabilities::current_user_has_partial_access_to($related_model, $context)
340
+			&& $model->exists($restricted_query_params)
341
+		)
342
+		) {
343
+			if ($relation instanceof \EE_Belongs_To_Relation) {
344
+				$related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
345
+			} else {
346
+				$related_model_name_maybe_plural = \EEH_Inflector::pluralize_and_lower($related_model->get_this_model_name());
347
+			}
348
+			return new \WP_Error(
349
+				sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
350
+				sprintf(
351
+					__('Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
352
+						'event_espresso'),
353
+					$related_model_name_maybe_plural,
354
+					$relation->get_this_model()->get_this_model_name(),
355
+					implode(
356
+						',',
357
+						array_keys(
358
+							Capabilities::get_missing_permissions($related_model, $context)
359
+						)
360
+					)
361
+				),
362
+				array('status' => 403)
363
+			);
364
+		}
365
+		$query_params = $this->create_model_query_params($relation->get_other_model(), $request->get_params());
366
+		foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
367
+			$query_params[0][$relation->get_this_model()->get_this_model_name()
368
+							 . '.'
369
+							 . $where_condition_key] = $where_condition_value;
370
+		}
371
+		$query_params['default_where_conditions'] = 'none';
372
+		$query_params['caps'] = $context;
373
+		if (! $request->get_header('no_rest_headers')) {
374
+			$this->_set_headers_from_query_params($relation->get_other_model(), $query_params);
375
+		}
376
+		/** @type array $results */
377
+		$results = $relation->get_other_model()->get_all_wpdb_results($query_params);
378
+		$nice_results = array();
379
+		foreach ($results as $result) {
380
+			$nice_result = $this->create_entity_from_wpdb_result(
381
+				$relation->get_other_model(),
382
+				$result,
383
+				$request
384
+			);
385
+			if ($relation instanceof \EE_HABTM_Relation) {
386
+				//put the unusual stuff (properties from the HABTM relation) first, and make sure
387
+				//if there are conflicts we prefer the properties from the main model
388
+				$join_model_result = $this->create_entity_from_wpdb_result(
389
+					$relation->get_join_model(),
390
+					$result,
391
+					$request
392
+				);
393
+				$joined_result = array_merge($nice_result, $join_model_result);
394
+				//but keep the meta stuff from the main model
395
+				if (isset($nice_result['meta'])) {
396
+					$joined_result['meta'] = $nice_result['meta'];
397
+				}
398
+				$nice_result = $joined_result;
399
+			}
400
+			$nice_results[] = $nice_result;
401
+		}
402
+		if ($relation instanceof \EE_Belongs_To_Relation) {
403
+			return array_shift($nice_results);
404
+		} else {
405
+			return $nice_results;
406
+		}
407
+	}
408
+
409
+
410
+
411
+	/**
412
+	 * Gets the collection for given relation object
413
+	 * The same as Read::get_entities_from_model(), except if the relation
414
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
415
+	 * the join-model-object into the results
416
+	 *
417
+	 * @param string                  $id the ID of the thing we are fetching related stuff from
418
+	 * @param \EE_Model_Relation_Base $relation
419
+	 * @param \WP_REST_Request        $request
420
+	 * @return array|\WP_Error
421
+	 * @throws \EE_Error
422
+	 */
423
+	public function get_entities_from_relation($id, $relation, $request)
424
+	{
425
+		if (! $relation->get_this_model()->has_primary_key_field()) {
426
+			throw new \EE_Error(
427
+				sprintf(
428
+					__('Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
429
+						'event_espresso'),
430
+					$relation->get_this_model()->get_this_model_name()
431
+				)
432
+			);
433
+		}
434
+		return $this->_get_entities_from_relation(
435
+			array(
436
+				array(
437
+					$relation->get_this_model()->primary_key_name() => $id,
438
+				),
439
+			),
440
+			$relation,
441
+			$request
442
+		);
443
+	}
444
+
445
+
446
+
447
+	/**
448
+	 * Sets the headers that are based on the model and query params,
449
+	 * like the total records. This should only be called on the original request
450
+	 * from the client, not on subsequent internal
451
+	 *
452
+	 * @param \EEM_Base $model
453
+	 * @param array     $query_params
454
+	 * @return void
455
+	 */
456
+	protected function _set_headers_from_query_params($model, $query_params)
457
+	{
458
+		$this->_set_debug_info('model query params', $query_params);
459
+		$this->_set_debug_info('missing caps',
460
+			Capabilities::get_missing_permissions_string($model, $query_params['caps']));
461
+		//normally the limit to a 2-part array, where the 2nd item is the limit
462
+		if (! isset($query_params['limit'])) {
463
+			$query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
464
+		}
465
+		if (is_array($query_params['limit'])) {
466
+			$limit_parts = $query_params['limit'];
467
+		} else {
468
+			$limit_parts = explode(',', $query_params['limit']);
469
+			if (count($limit_parts) == 1) {
470
+				$limit_parts = array(0, $limit_parts[0]);
471
+			}
472
+		}
473
+		//remove the group by and having parts of the query, as those will
474
+		//make the sql query return an array of values, instead of just a single value
475
+		unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
476
+		$count = $model->count($query_params, null, true);
477
+		$pages = $count / $limit_parts[1];
478
+		$this->_set_response_header('Total', $count, false);
479
+		$this->_set_response_header('PageSize', $limit_parts[1], false);
480
+		$this->_set_response_header('TotalPages', ceil($pages), false);
481
+	}
482
+
483
+
484
+
485
+	/**
486
+	 * Changes database results into REST API entities
487
+	 *
488
+	 * @param \EEM_Base        $model
489
+	 * @param array            $db_row     like results from $wpdb->get_results()
490
+	 * @param \WP_REST_Request $rest_request
491
+	 * @param string           $deprecated no longer used
492
+	 * @return array ready for being converted into json for sending to client
493
+	 */
494
+	public function create_entity_from_wpdb_result($model, $db_row, $rest_request, $deprecated = null)
495
+	{
496
+		if (! $rest_request instanceof \WP_REST_Request) {
497
+			//ok so this was called in the old style, where the 3rd arg was
498
+			//$include, and the 4th arg was $context
499
+			//now setup the request just to avoid fatal errors, although we won't be able
500
+			//to truly make use of it because it's kinda devoid of info
501
+			$rest_request = new \WP_REST_Request();
502
+			$rest_request->set_param('include', $rest_request);
503
+			$rest_request->set_param('caps', $deprecated);
504
+		}
505
+		if ($rest_request->get_param('caps') == null) {
506
+			$rest_request->set_param('caps', \EEM_Base::caps_read);
507
+		}
508
+		$entity_array = $this->_create_bare_entity_from_wpdb_results($model, $db_row);
509
+		$entity_array = $this->_add_extra_fields($model, $db_row, $entity_array);
510
+		$entity_array['_links'] = $this->_get_entity_links($model, $db_row, $entity_array);
511
+		$entity_array['_calculated_fields'] = $this->_get_entity_calculations($model, $db_row, $rest_request);
512
+		$entity_array = $this->_include_requested_models($model, $rest_request, $entity_array, $db_row);
513
+		$entity_array = apply_filters(
514
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
515
+			$entity_array,
516
+			$model,
517
+			$rest_request->get_param('caps'),
518
+			$rest_request,
519
+			$this
520
+		);
521
+		$result_without_inaccessible_fields = Capabilities::filter_out_inaccessible_entity_fields(
522
+			$entity_array,
523
+			$model,
524
+			$rest_request->get_param('caps'),
525
+			$this->get_model_version_info(),
526
+			$model->get_index_primary_key_string(
527
+				$model->deduce_fields_n_values_from_cols_n_values($db_row)
528
+			)
529
+		);
530
+		$this->_set_debug_info(
531
+			'inaccessible fields',
532
+			array_keys(array_diff_key($entity_array, $result_without_inaccessible_fields))
533
+		);
534
+		return apply_filters(
535
+			'FHEE__Read__create_entity_from_wpdb_results__entity_return',
536
+			$result_without_inaccessible_fields,
537
+			$model,
538
+			$rest_request->get_param('caps')
539
+		);
540
+	}
541
+
542
+
543
+
544
+	/**
545
+	 * Creates a REST entity array (JSON object we're going to return in the response, but
546
+	 * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
547
+	 * from $wpdb->get_row( $sql, ARRAY_A)
548
+	 *
549
+	 * @param \EEM_Base $model
550
+	 * @param array     $db_row
551
+	 * @return array entity mostly ready for converting to JSON and sending in the response
552
+	 */
553
+	protected function _create_bare_entity_from_wpdb_results(\EEM_Base $model, $db_row)
554
+	{
555
+		$result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
556
+		$result = array_intersect_key($result,
557
+			$this->get_model_version_info()->fields_on_model_in_this_version($model));
558
+		foreach ($result as $field_name => $raw_field_value) {
559
+			$field_obj = $model->field_settings_for($field_name);
560
+			$field_value = $field_obj->prepare_for_set_from_db($raw_field_value);
561
+			if ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_ignored())) {
562
+				unset($result[$field_name]);
563
+			} elseif (
564
+			$this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_rendered_format())
565
+			) {
566
+				$result[$field_name] = array(
567
+					'raw'      => $field_obj->prepare_for_get($field_value),
568
+					'rendered' => $field_obj->prepare_for_pretty_echoing($field_value),
569
+				);
570
+			} elseif (
571
+			$this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_pretty_format())
572
+			) {
573
+				$result[$field_name] = array(
574
+					'raw'    => $field_obj->prepare_for_get($field_value),
575
+					'pretty' => $field_obj->prepare_for_pretty_echoing($field_value),
576
+				);
577
+			} elseif ($field_obj instanceof \EE_Datetime_Field) {
578
+				if ($field_value instanceof \DateTime) {
579
+					$timezone = $field_value->getTimezone();
580
+					$field_value->setTimezone(new \DateTimeZone('UTC'));
581
+					$result[$field_name . '_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
582
+						$field_obj,
583
+						$field_value,
584
+						$this->get_model_version_info()->requested_version()
585
+					);
586
+					$field_value->setTimezone($timezone);
587
+					$result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
588
+						$field_obj,
589
+						$field_value,
590
+						$this->get_model_version_info()->requested_version()
591
+					);
592
+				}
593
+			} else {
594
+				$result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
595
+					$field_obj,
596
+					$field_obj->prepare_for_get($field_value),
597
+					$this->get_model_version_info()->requested_version()
598
+				);
599
+			}
600
+		}
601
+		return $result;
602
+	}
603
+
604
+
605
+
606
+	/**
607
+	 * Adds a few extra fields to the entity response
608
+	 *
609
+	 * @param \EEM_Base $model
610
+	 * @param array     $db_row
611
+	 * @param array     $entity_array
612
+	 * @return array modified entity
613
+	 */
614
+	protected function _add_extra_fields(\EEM_Base $model, $db_row, $entity_array)
615
+	{
616
+		if ($model instanceof \EEM_CPT_Base) {
617
+			$entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
618
+		}
619
+		return $entity_array;
620
+	}
621
+
622
+
623
+
624
+	/**
625
+	 * Gets links we want to add to the response
626
+	 *
627
+	 * @global \WP_REST_Server $wp_rest_server
628
+	 * @param \EEM_Base        $model
629
+	 * @param array            $db_row
630
+	 * @param array            $entity_array
631
+	 * @return array the _links item in the entity
632
+	 */
633
+	protected function _get_entity_links($model, $db_row, $entity_array)
634
+	{
635
+		//add basic links
636
+		$links = array();
637
+		if ($model->has_primary_key_field()) {
638
+			$links['self'] = array(
639
+				array(
640
+					'href' => $this->get_versioned_link_to(
641
+						\EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
642
+						. '/'
643
+						. $entity_array[$model->primary_key_name()]
644
+					),
645
+				),
646
+			);
647
+		}
648
+		$links['collection'] = array(
649
+			array(
650
+				'href' => $this->get_versioned_link_to(
651
+					\EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
652
+				),
653
+			),
654
+		);
655
+		//add links to related models
656
+		if ($model->has_primary_key_field()) {
657
+			foreach ($this->get_model_version_info()->relation_settings($model) as $relation_name => $relation_obj) {
658
+				$related_model_part = Read::get_related_entity_name($relation_name, $relation_obj);
659
+				$links[\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
660
+					array(
661
+						'href'   => $this->get_versioned_link_to(
662
+							\EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
663
+							. '/'
664
+							. $entity_array[$model->primary_key_name()]
665
+							. '/'
666
+							. $related_model_part
667
+						),
668
+						'single' => $relation_obj instanceof \EE_Belongs_To_Relation ? true : false,
669
+					),
670
+				);
671
+			}
672
+		}
673
+		return $links;
674
+	}
675
+
676
+
677
+
678
+	/**
679
+	 * Adds the included models indicated in the request to the entity provided
680
+	 *
681
+	 * @param \EEM_Base        $model
682
+	 * @param \WP_REST_Request $rest_request
683
+	 * @param array            $entity_array
684
+	 * @param array            $db_row
685
+	 * @return array the modified entity
686
+	 */
687
+	protected function _include_requested_models(
688
+		\EEM_Base $model,
689
+		\WP_REST_Request $rest_request,
690
+		$entity_array,
691
+		$db_row = array()
692
+	) {
693
+		//if $db_row not included, hope the entity array has what we need
694
+		if (! $db_row) {
695
+			$db_row = $entity_array;
696
+		}
697
+		$includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), '');
698
+		$includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model);
699
+		//if they passed in * or didn't specify any includes, return everything
700
+		if (! in_array('*', $includes_for_this_model)
701
+			&& ! empty($includes_for_this_model)
702
+		) {
703
+			if ($model->has_primary_key_field()) {
704
+				//always include the primary key. ya just gotta know that at least
705
+				$includes_for_this_model[] = $model->primary_key_name();
706
+			}
707
+			if ($this->explode_and_get_items_prefixed_with($rest_request->get_param('calculate'), '')) {
708
+				$includes_for_this_model[] = '_calculated_fields';
709
+			}
710
+			$entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
711
+		}
712
+		$relation_settings = $this->get_model_version_info()->relation_settings($model);
713
+		foreach ($relation_settings as $relation_name => $relation_obj) {
714
+			$related_fields_to_include = $this->explode_and_get_items_prefixed_with(
715
+				$rest_request->get_param('include'),
716
+				$relation_name
717
+			);
718
+			$related_fields_to_calculate = $this->explode_and_get_items_prefixed_with(
719
+				$rest_request->get_param('calculate'),
720
+				$relation_name
721
+			);
722
+			//did they specify they wanted to include a related model, or
723
+			//specific fields from a related model?
724
+			//or did they specify to calculate a field from a related model?
725
+			if ($related_fields_to_include || $related_fields_to_calculate) {
726
+				//if so, we should include at least some part of the related model
727
+				$pretend_related_request = new \WP_REST_Request();
728
+				$pretend_related_request->set_query_params(
729
+					array(
730
+						'caps'      => $rest_request->get_param('caps'),
731
+						'include'   => $related_fields_to_include,
732
+						'calculate' => $related_fields_to_calculate,
733
+					)
734
+				);
735
+				$pretend_related_request->add_header('no_rest_headers', true);
736
+				$primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
737
+					$model->get_index_primary_key_string(
738
+						$model->deduce_fields_n_values_from_cols_n_values($db_row)
739
+					)
740
+				);
741
+				$related_results = $this->_get_entities_from_relation(
742
+					$primary_model_query_params,
743
+					$relation_obj,
744
+					$pretend_related_request
745
+				);
746
+				$entity_array[Read::get_related_entity_name($relation_name, $relation_obj)] = $related_results
747
+																							  instanceof
748
+																							  \WP_Error
749
+					? null
750
+					: $related_results;
751
+			}
752
+		}
753
+		return $entity_array;
754
+	}
755
+
756
+
757
+
758
+	/**
759
+	 * Returns a new array with all the names of models removed. Eg
760
+	 * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
761
+	 *
762
+	 * @param array $arr
763
+	 * @return array
764
+	 */
765
+	private function _remove_model_names_from_array($arr)
766
+	{
767
+		return array_diff($arr, array_keys(\EE_Registry::instance()->non_abstract_db_models));
768
+	}
769
+
770
+
771
+
772
+	/**
773
+	 * Gets the calculated fields for the response
774
+	 *
775
+	 * @param \EEM_Base        $model
776
+	 * @param array            $wpdb_row
777
+	 * @param \WP_REST_Request $rest_request
778
+	 * @return \stdClass the _calculations item in the entity
779
+	 */
780
+	protected function _get_entity_calculations($model, $wpdb_row, $rest_request)
781
+	{
782
+		$calculated_fields = $this->explode_and_get_items_prefixed_with(
783
+			$rest_request->get_param('calculate'),
784
+			''
785
+		);
786
+		//note: setting calculate=* doesn't do anything
787
+		$calculated_fields_to_return = new \stdClass();
788
+		foreach ($calculated_fields as $field_to_calculate) {
789
+			try {
790
+				$calculated_fields_to_return->$field_to_calculate = Model_Data_Translator::prepare_field_value_for_json(
791
+					null,
792
+					$this->_fields_calculator->retrieve_calculated_field_value(
793
+						$model,
794
+						$field_to_calculate,
795
+						$wpdb_row,
796
+						$rest_request,
797
+						$this
798
+					),
799
+					$this->get_model_version_info()->requested_version()
800
+				);
801
+			} catch (Rest_Exception $e) {
802
+				//if we don't have permission to read it, just leave it out. but let devs know about the problem
803
+				$this->_set_response_header(
804
+					'Notices-Field-Calculation-Errors['
805
+					. $e->get_string_code()
806
+					. ']['
807
+					. $model->get_this_model_name()
808
+					. ']['
809
+					. $field_to_calculate
810
+					. ']',
811
+					$e->getMessage(),
812
+					true
813
+				);
814
+			}
815
+		}
816
+		return $calculated_fields_to_return;
817
+	}
818
+
819
+
820
+
821
+	/**
822
+	 * Gets the full URL to the resource, taking the requested version into account
823
+	 *
824
+	 * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
825
+	 * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
826
+	 */
827
+	public function get_versioned_link_to($link_part_after_version_and_slash)
828
+	{
829
+		return rest_url(
830
+			\EED_Core_Rest_Api::ee_api_namespace
831
+			. $this->get_model_version_info()->requested_version()
832
+			. '/'
833
+			. $link_part_after_version_and_slash
834
+		);
835
+	}
836
+
837
+
838
+
839
+	/**
840
+	 * Gets the correct lowercase name for the relation in the API according
841
+	 * to the relation's type
842
+	 *
843
+	 * @param string                  $relation_name
844
+	 * @param \EE_Model_Relation_Base $relation_obj
845
+	 * @return string
846
+	 */
847
+	public static function get_related_entity_name($relation_name, $relation_obj)
848
+	{
849
+		if ($relation_obj instanceof \EE_Belongs_To_Relation) {
850
+			return strtolower($relation_name);
851
+		} else {
852
+			return \EEH_Inflector::pluralize_and_lower($relation_name);
853
+		}
854
+	}
855
+
856
+
857
+
858
+	/**
859
+	 * Gets the one model object with the specified id for the specified model
860
+	 *
861
+	 * @param \EEM_Base        $model
862
+	 * @param \WP_REST_Request $request
863
+	 * @return array|\WP_Error
864
+	 */
865
+	public function get_entity_from_model($model, $request)
866
+	{
867
+		$query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
868
+		if ($model instanceof \EEM_Soft_Delete_Base) {
869
+			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
870
+		}
871
+		$restricted_query_params = $query_params;
872
+		$restricted_query_params['caps'] = $this->validate_context($request->get_param('caps'));
873
+		$this->_set_debug_info('model query params', $restricted_query_params);
874
+		$model_rows = $model->get_all_wpdb_results($restricted_query_params);
875
+		if (! empty ($model_rows)) {
876
+			return $this->create_entity_from_wpdb_result(
877
+				$model,
878
+				array_shift($model_rows),
879
+				$request);
880
+		} else {
881
+			//ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
882
+			$lowercase_model_name = strtolower($model->get_this_model_name());
883
+			$model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
884
+			if (! empty($model_rows_found_sans_restrictions)) {
885
+				//you got shafted- it existed but we didn't want to tell you!
886
+				return new \WP_Error(
887
+					'rest_user_cannot_read',
888
+					sprintf(
889
+						__('Sorry, you cannot read this %1$s. Missing permissions are: %2$s', 'event_espresso'),
890
+						strtolower($model->get_this_model_name()),
891
+						Capabilities::get_missing_permissions_string(
892
+							$model,
893
+							$this->validate_context($request->get_param('caps')))
894
+					),
895
+					array('status' => 403)
896
+				);
897
+			} else {
898
+				//it's not you. It just doesn't exist
899
+				return new \WP_Error(
900
+					sprintf('rest_%s_invalid_id', $lowercase_model_name),
901
+					sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
902
+					array('status' => 404)
903
+				);
904
+			}
905
+		}
906
+	}
907
+
908
+
909
+
910
+	/**
911
+	 * If a context is provided which isn't valid, maybe it was added in a future
912
+	 * version so just treat it as a default read
913
+	 *
914
+	 * @param string $context
915
+	 * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
916
+	 */
917
+	public function validate_context($context)
918
+	{
919
+		if (! $context) {
920
+			$context = \EEM_Base::caps_read;
921
+		}
922
+		$valid_contexts = \EEM_Base::valid_cap_contexts();
923
+		if (in_array($context, $valid_contexts)) {
924
+			return $context;
925
+		} else {
926
+			return \EEM_Base::caps_read;
927
+		}
928
+	}
929
+
930
+
931
+
932
+	/**
933
+	 * Verifies the passed in value is an allowable default where conditions value.
934
+	 *
935
+	 * @param $default_query_params
936
+	 * @return string
937
+	 */
938
+	public function validate_default_query_params($default_query_params)
939
+	{
940
+		$valid_default_where_conditions_for_api_calls = array(
941
+			\EEM_Base::default_where_conditions_all,
942
+			\EEM_Base::default_where_conditions_minimum_all,
943
+			\EEM_Base::default_where_conditions_minimum_others,
944
+		);
945
+		if (! $default_query_params) {
946
+			$default_query_params = \EEM_Base::default_where_conditions_all;
947
+		}
948
+		if (
949
+		in_array(
950
+			$default_query_params,
951
+			$valid_default_where_conditions_for_api_calls,
952
+			true
953
+		)
954
+		) {
955
+			return $default_query_params;
956
+		} else {
957
+			return \EEM_Base::default_where_conditions_all;
958
+		}
959
+	}
960
+
961
+
962
+
963
+	/**
964
+	 * Translates API filter get parameter into $query_params array used by EEM_Base::get_all().
965
+	 * Note: right now the query parameter keys for fields (and related fields)
966
+	 * can be left as-is, but it's quite possible this will change someday.
967
+	 * Also, this method's contents might be candidate for moving to Model_Data_Translator
968
+	 *
969
+	 * @param \EEM_Base $model
970
+	 * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
971
+	 * @return array like what EEM_Base::get_all() expects or FALSE to indicate
972
+	 *                                    that absolutely no results should be returned
973
+	 * @throws \EE_Error
974
+	 */
975
+	public function create_model_query_params($model, $query_parameters)
976
+	{
977
+		$model_query_params = array();
978
+		if (isset($query_parameters['where'])) {
979
+			$model_query_params[0] = Model_Data_Translator::prepare_conditions_query_params_for_models(
980
+				$query_parameters['where'],
981
+				$model,
982
+				$this->get_model_version_info()->requested_version()
983
+			);
984
+		}
985
+		if (isset($query_parameters['order_by'])) {
986
+			$order_by = $query_parameters['order_by'];
987
+		} elseif (isset($query_parameters['orderby'])) {
988
+			$order_by = $query_parameters['orderby'];
989
+		} else {
990
+			$order_by = null;
991
+		}
992
+		if ($order_by !== null) {
993
+			if (is_array($order_by)) {
994
+				$order_by = Model_Data_Translator::prepare_field_names_in_array_keys_from_json($order_by);
995
+			} else {
996
+				//it's a single item
997
+				$order_by = Model_Data_Translator::prepare_field_name_from_json($order_by);
998
+			}
999
+			$model_query_params['order_by'] = $order_by;
1000
+		}
1001
+		if (isset($query_parameters['group_by'])) {
1002
+			$group_by = $query_parameters['group_by'];
1003
+		} elseif (isset($query_parameters['groupby'])) {
1004
+			$group_by = $query_parameters['groupby'];
1005
+		} else {
1006
+			$group_by = array_keys($model->get_combined_primary_key_fields());
1007
+		}
1008
+		//make sure they're all real names
1009
+		if (is_array($group_by)) {
1010
+			$group_by = Model_Data_Translator::prepare_field_names_from_json($group_by);
1011
+		}
1012
+		if ($group_by !== null) {
1013
+			$model_query_params['group_by'] = $group_by;
1014
+		}
1015
+		if (isset($query_parameters['having'])) {
1016
+			$model_query_params['having'] = Model_Data_Translator::prepare_conditions_query_params_for_models(
1017
+				$query_parameters['having'],
1018
+				$model,
1019
+				$this->get_model_version_info()->requested_version()
1020
+			);
1021
+		}
1022
+		if (isset($query_parameters['order'])) {
1023
+			$model_query_params['order'] = $query_parameters['order'];
1024
+		}
1025
+		if (isset($query_parameters['mine'])) {
1026
+			$model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1027
+		}
1028
+		if (isset($query_parameters['limit'])) {
1029
+			//limit should be either a string like '23' or '23,43', or an array with two items in it
1030
+			if (! is_array($query_parameters['limit'])) {
1031
+				$limit_array = explode(',', (string)$query_parameters['limit']);
1032
+			} else {
1033
+				$limit_array = $query_parameters['limit'];
1034
+			}
1035
+			$sanitized_limit = array();
1036
+			foreach ($limit_array as $key => $limit_part) {
1037
+				if ($this->_debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1038
+					throw new \EE_Error(
1039
+						sprintf(
1040
+							__('An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
1041
+								'event_espresso'),
1042
+							wp_json_encode($query_parameters['limit'])
1043
+						)
1044
+					);
1045
+				}
1046
+				$sanitized_limit[] = (int)$limit_part;
1047
+			}
1048
+			$model_query_params['limit'] = implode(',', $sanitized_limit);
1049
+		} else {
1050
+			$model_query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
1051
+		}
1052
+		if (isset($query_parameters['caps'])) {
1053
+			$model_query_params['caps'] = $this->validate_context($query_parameters['caps']);
1054
+		} else {
1055
+			$model_query_params['caps'] = \EEM_Base::caps_read;
1056
+		}
1057
+		if (isset($query_parameters['default_where_conditions'])) {
1058
+			$model_query_params['default_where_conditions'] = $this->validate_default_query_params($query_parameters['default_where_conditions']);
1059
+		}
1060
+		return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model);
1061
+	}
1062
+
1063
+
1064
+
1065
+	/**
1066
+	 * Changes the REST-style query params for use in the models
1067
+	 *
1068
+	 * @deprecated
1069
+	 * @param \EEM_Base $model
1070
+	 * @param array     $query_params sub-array from @see EEM_Base::get_all()
1071
+	 * @return array
1072
+	 */
1073
+	public function prepare_rest_query_params_key_for_models($model, $query_params)
1074
+	{
1075
+		$model_ready_query_params = array();
1076
+		foreach ($query_params as $key => $value) {
1077
+			if (is_array($value)) {
1078
+				$model_ready_query_params[$key] = $this->prepare_rest_query_params_key_for_models($model, $value);
1079
+			} else {
1080
+				$model_ready_query_params[$key] = $value;
1081
+			}
1082
+		}
1083
+		return $model_ready_query_params;
1084
+	}
1085
+
1086
+
1087
+
1088
+	/**
1089
+	 * @deprecated
1090
+	 * @param $model
1091
+	 * @param $query_params
1092
+	 * @return array
1093
+	 */
1094
+	public function prepare_rest_query_params_values_for_models($model, $query_params)
1095
+	{
1096
+		$model_ready_query_params = array();
1097
+		foreach ($query_params as $key => $value) {
1098
+			if (is_array($value)) {
1099
+				$model_ready_query_params[$key] = $this->prepare_rest_query_params_values_for_models($model, $value);
1100
+			} else {
1101
+				$model_ready_query_params[$key] = $value;
1102
+			}
1103
+		}
1104
+		return $model_ready_query_params;
1105
+	}
1106
+
1107
+
1108
+
1109
+	/**
1110
+	 * Explodes the string on commas, and only returns items with $prefix followed by a period.
1111
+	 * If no prefix is specified, returns items with no period.
1112
+	 *
1113
+	 * @param string|array $string_to_explode eg "jibba,jabba, blah, blaabla" or array('jibba', 'jabba' )
1114
+	 * @param string       $prefix            "Event" or "foobar"
1115
+	 * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1116
+	 *                                        we only return strings starting with that and a period; if no prefix was
1117
+	 *                                        specified we return all items containing NO periods
1118
+	 */
1119
+	public function explode_and_get_items_prefixed_with($string_to_explode, $prefix)
1120
+	{
1121
+		if (is_string($string_to_explode)) {
1122
+			$exploded_contents = explode(',', $string_to_explode);
1123
+		} else if (is_array($string_to_explode)) {
1124
+			$exploded_contents = $string_to_explode;
1125
+		} else {
1126
+			$exploded_contents = array();
1127
+		}
1128
+		//if the string was empty, we want an empty array
1129
+		$exploded_contents = array_filter($exploded_contents);
1130
+		$contents_with_prefix = array();
1131
+		foreach ($exploded_contents as $item) {
1132
+			$item = trim($item);
1133
+			//if no prefix was provided, so we look for items with no "." in them
1134
+			if (! $prefix) {
1135
+				//does this item have a period?
1136
+				if (strpos($item, '.') === false) {
1137
+					//if not, then its what we're looking for
1138
+					$contents_with_prefix[] = $item;
1139
+				}
1140
+			} else if (strpos($item, $prefix . '.') === 0) {
1141
+				//this item has the prefix and a period, grab it
1142
+				$contents_with_prefix[] = substr(
1143
+					$item,
1144
+					strpos($item, $prefix . '.') + strlen($prefix . '.')
1145
+				);
1146
+			} else if ($item === $prefix) {
1147
+				//this item is JUST the prefix
1148
+				//so let's grab everything after, which is a blank string
1149
+				$contents_with_prefix[] = '';
1150
+			}
1151
+		}
1152
+		return $contents_with_prefix;
1153
+	}
1154
+
1155
+
1156
+
1157
+	/**
1158
+	 * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1159
+	 * Deprecated because its return values were really quite confusing- sometimes it returned
1160
+	 * an empty array (when the include string was blank or '*') or sometimes it returned
1161
+	 * array('*') (when you provided a model and a model of that kind was found).
1162
+	 * Parses the $include_string so we fetch all the field names relating to THIS model
1163
+	 * (ie have NO period in them), or for the provided model (ie start with the model
1164
+	 * name and then a period).
1165
+	 * @param string $include_string @see Read:handle_request_get_all
1166
+	 * @param string $model_name
1167
+	 * @return array of fields for this model. If $model_name is provided, then
1168
+	 *                               the fields for that model, with the model's name removed from each.
1169
+	 *                               If $include_string was blank or '*' returns an empty array
1170
+	 */
1171
+	public function extract_includes_for_this_model($include_string, $model_name = null)
1172
+	{
1173
+		if (is_array($include_string)) {
1174
+			$include_string = implode(',', $include_string);
1175
+		}
1176
+		if ($include_string === '*' || $include_string === '') {
1177
+			return array();
1178
+		}
1179
+		$includes = explode(',', $include_string);
1180
+		$extracted_fields_to_include = array();
1181
+		if ($model_name) {
1182
+			foreach ($includes as $field_to_include) {
1183
+				$field_to_include = trim($field_to_include);
1184
+				if (strpos($field_to_include, $model_name . '.') === 0) {
1185
+					//found the model name at the exact start
1186
+					$field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1187
+					$extracted_fields_to_include[] = $field_sans_model_name;
1188
+				} elseif ($field_to_include == $model_name) {
1189
+					$extracted_fields_to_include[] = '*';
1190
+				}
1191
+			}
1192
+		} else {
1193
+			//look for ones with no period
1194
+			foreach ($includes as $field_to_include) {
1195
+				$field_to_include = trim($field_to_include);
1196
+				if (
1197
+					strpos($field_to_include, '.') === false
1198
+					&& ! $this->get_model_version_info()->is_model_name_in_this_version($field_to_include)
1199
+				) {
1200
+					$extracted_fields_to_include[] = $field_to_include;
1201
+				}
1202
+			}
1203
+		}
1204
+		return $extracted_fields_to_include;
1205
+	}
1206 1206
 }
1207 1207
 
1208 1208
 
Please login to merge, or discard this patch.
Spacing   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -8,7 +8,7 @@  discard block
 block discarded – undo
8 8
 use EventEspresso\core\entities\models\JsonModelSchema;
9 9
 use EE_Datetime_Field;
10 10
 
11
-if (! defined('EVENT_ESPRESSO_VERSION')) {
11
+if ( ! defined('EVENT_ESPRESSO_VERSION')) {
12 12
     exit('No direct script access allowed');
13 13
 }
14 14
 
@@ -57,12 +57,12 @@  discard block
 block discarded – undo
57 57
         try {
58 58
             $matches = $controller->parse_route(
59 59
                 $request->get_route(),
60
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
60
+                '~'.\EED_Core_Rest_Api::ee_api_namespace_for_regex.'(.*)~',
61 61
                 array('version', 'model')
62 62
             );
63 63
             $controller->set_requested_version($matches['version']);
64 64
             $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
65
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
65
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
66 66
                 return $controller->send_response(
67 67
                     new \WP_Error(
68 68
                         'endpoint_parsing_error',
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
         $controller = new Read();
99 99
         try {
100 100
             $controller->set_requested_version($version);
101
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
101
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
102 102
                 return array();
103 103
             }
104 104
             //get the model for this version
@@ -132,9 +132,9 @@  discard block
 block discarded – undo
132 132
     {
133 133
         foreach ($this->get_model_version_info()->fields_on_model_in_this_version($model) as $field_name => $field) {
134 134
             if ($field instanceof EE_Datetime_Field) {
135
-                $schema['properties'][$field_name . '_gmt'] = $field->getSchema();
135
+                $schema['properties'][$field_name.'_gmt'] = $field->getSchema();
136 136
                 //modify the description
137
-                $schema['properties'][$field_name . '_gmt']['description'] = sprintf(
137
+                $schema['properties'][$field_name.'_gmt']['description'] = sprintf(
138 138
                     esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
139 139
                     $field->get_nicename()
140 140
                 );
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
     protected function get_route_from_request() {
154 154
         if (isset($GLOBALS['wp'])
155 155
             && $GLOBALS['wp'] instanceof \WP
156
-            && isset($GLOBALS['wp']->query_vars['rest_route'] )
156
+            && isset($GLOBALS['wp']->query_vars['rest_route'])
157 157
         ) {
158 158
             return $GLOBALS['wp']->query_vars['rest_route'];
159 159
         } else {
@@ -175,11 +175,11 @@  discard block
 block discarded – undo
175 175
         try {
176 176
             $matches = $controller->parse_route(
177 177
                 $request->get_route(),
178
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
178
+                '~'.\EED_Core_Rest_Api::ee_api_namespace_for_regex.'(.*)/(.*)~',
179 179
                 array('version', 'model', 'id'));
180 180
             $controller->set_requested_version($matches['version']);
181 181
             $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
182
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
182
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
183 183
                 return $controller->send_response(
184 184
                     new \WP_Error(
185 185
                         'endpoint_parsing_error',
@@ -217,12 +217,12 @@  discard block
 block discarded – undo
217 217
         try {
218 218
             $matches = $controller->parse_route(
219 219
                 $request->get_route(),
220
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
220
+                '~'.\EED_Core_Rest_Api::ee_api_namespace_for_regex.'(.*)/(.*)/(.*)~',
221 221
                 array('version', 'model', 'id', 'related_model')
222 222
             );
223 223
             $controller->set_requested_version($matches['version']);
224 224
             $main_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
225
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
225
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
226 226
                 return $controller->send_response(
227 227
                     new \WP_Error(
228 228
                         'endpoint_parsing_error',
@@ -237,11 +237,11 @@  discard block
 block discarded – undo
237 237
             $main_model = $controller->get_model_version_info()->load_model($main_model_name_singular);
238 238
             //assume the related model name is plural and try to find the model's name
239 239
             $related_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['related_model']);
240
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
240
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
241 241
                 //so the word didn't singularize well. Maybe that's just because it's a singular word?
242 242
                 $related_model_name_singular = \EEH_Inflector::humanize($matches['related_model']);
243 243
             }
244
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
244
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
245 245
                 return $controller->send_response(
246 246
                     new \WP_Error(
247 247
                         'endpoint_parsing_error',
@@ -277,7 +277,7 @@  discard block
 block discarded – undo
277 277
     public function get_entities_from_model($model, $request)
278 278
     {
279 279
         $query_params = $this->create_model_query_params($model, $request->get_params());
280
-        if (! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
280
+        if ( ! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
281 281
             $model_name_plural = \EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
282 282
             return new \WP_Error(
283 283
                 sprintf('rest_%s_cannot_list', $model_name_plural),
@@ -289,7 +289,7 @@  discard block
 block discarded – undo
289 289
                 array('status' => 403)
290 290
             );
291 291
         }
292
-        if (! $request->get_header('no_rest_headers')) {
292
+        if ( ! $request->get_header('no_rest_headers')) {
293 293
             $this->_set_headers_from_query_params($model, $query_params);
294 294
         }
295 295
         /** @type array $results */
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
         $context = $this->validate_context($request->get_param('caps'));
320 320
         $model = $relation->get_this_model();
321 321
         $related_model = $relation->get_other_model();
322
-        if (! isset($primary_model_query_params[0])) {
322
+        if ( ! isset($primary_model_query_params[0])) {
323 323
             $primary_model_query_params[0] = array();
324 324
         }
325 325
         //check if they can access the 1st model object
@@ -370,7 +370,7 @@  discard block
 block discarded – undo
370 370
         }
371 371
         $query_params['default_where_conditions'] = 'none';
372 372
         $query_params['caps'] = $context;
373
-        if (! $request->get_header('no_rest_headers')) {
373
+        if ( ! $request->get_header('no_rest_headers')) {
374 374
             $this->_set_headers_from_query_params($relation->get_other_model(), $query_params);
375 375
         }
376 376
         /** @type array $results */
@@ -422,7 +422,7 @@  discard block
 block discarded – undo
422 422
      */
423 423
     public function get_entities_from_relation($id, $relation, $request)
424 424
     {
425
-        if (! $relation->get_this_model()->has_primary_key_field()) {
425
+        if ( ! $relation->get_this_model()->has_primary_key_field()) {
426 426
             throw new \EE_Error(
427 427
                 sprintf(
428 428
                     __('Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
@@ -459,7 +459,7 @@  discard block
 block discarded – undo
459 459
         $this->_set_debug_info('missing caps',
460 460
             Capabilities::get_missing_permissions_string($model, $query_params['caps']));
461 461
         //normally the limit to a 2-part array, where the 2nd item is the limit
462
-        if (! isset($query_params['limit'])) {
462
+        if ( ! isset($query_params['limit'])) {
463 463
             $query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
464 464
         }
465 465
         if (is_array($query_params['limit'])) {
@@ -493,7 +493,7 @@  discard block
 block discarded – undo
493 493
      */
494 494
     public function create_entity_from_wpdb_result($model, $db_row, $rest_request, $deprecated = null)
495 495
     {
496
-        if (! $rest_request instanceof \WP_REST_Request) {
496
+        if ( ! $rest_request instanceof \WP_REST_Request) {
497 497
             //ok so this was called in the old style, where the 3rd arg was
498 498
             //$include, and the 4th arg was $context
499 499
             //now setup the request just to avoid fatal errors, although we won't be able
@@ -578,7 +578,7 @@  discard block
 block discarded – undo
578 578
                 if ($field_value instanceof \DateTime) {
579 579
                     $timezone = $field_value->getTimezone();
580 580
                     $field_value->setTimezone(new \DateTimeZone('UTC'));
581
-                    $result[$field_name . '_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
581
+                    $result[$field_name.'_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
582 582
                         $field_obj,
583 583
                         $field_value,
584 584
                         $this->get_model_version_info()->requested_version()
@@ -656,7 +656,7 @@  discard block
 block discarded – undo
656 656
         if ($model->has_primary_key_field()) {
657 657
             foreach ($this->get_model_version_info()->relation_settings($model) as $relation_name => $relation_obj) {
658 658
                 $related_model_part = Read::get_related_entity_name($relation_name, $relation_obj);
659
-                $links[\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
659
+                $links[\EED_Core_Rest_Api::ee_api_link_namespace.$related_model_part] = array(
660 660
                     array(
661 661
                         'href'   => $this->get_versioned_link_to(
662 662
                             \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
@@ -691,13 +691,13 @@  discard block
 block discarded – undo
691 691
         $db_row = array()
692 692
     ) {
693 693
         //if $db_row not included, hope the entity array has what we need
694
-        if (! $db_row) {
694
+        if ( ! $db_row) {
695 695
             $db_row = $entity_array;
696 696
         }
697 697
         $includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), '');
698 698
         $includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model);
699 699
         //if they passed in * or didn't specify any includes, return everything
700
-        if (! in_array('*', $includes_for_this_model)
700
+        if ( ! in_array('*', $includes_for_this_model)
701 701
             && ! empty($includes_for_this_model)
702 702
         ) {
703 703
             if ($model->has_primary_key_field()) {
@@ -872,7 +872,7 @@  discard block
 block discarded – undo
872 872
         $restricted_query_params['caps'] = $this->validate_context($request->get_param('caps'));
873 873
         $this->_set_debug_info('model query params', $restricted_query_params);
874 874
         $model_rows = $model->get_all_wpdb_results($restricted_query_params);
875
-        if (! empty ($model_rows)) {
875
+        if ( ! empty ($model_rows)) {
876 876
             return $this->create_entity_from_wpdb_result(
877 877
                 $model,
878 878
                 array_shift($model_rows),
@@ -881,7 +881,7 @@  discard block
 block discarded – undo
881 881
             //ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
882 882
             $lowercase_model_name = strtolower($model->get_this_model_name());
883 883
             $model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
884
-            if (! empty($model_rows_found_sans_restrictions)) {
884
+            if ( ! empty($model_rows_found_sans_restrictions)) {
885 885
                 //you got shafted- it existed but we didn't want to tell you!
886 886
                 return new \WP_Error(
887 887
                     'rest_user_cannot_read',
@@ -916,7 +916,7 @@  discard block
 block discarded – undo
916 916
      */
917 917
     public function validate_context($context)
918 918
     {
919
-        if (! $context) {
919
+        if ( ! $context) {
920 920
             $context = \EEM_Base::caps_read;
921 921
         }
922 922
         $valid_contexts = \EEM_Base::valid_cap_contexts();
@@ -942,7 +942,7 @@  discard block
 block discarded – undo
942 942
             \EEM_Base::default_where_conditions_minimum_all,
943 943
             \EEM_Base::default_where_conditions_minimum_others,
944 944
         );
945
-        if (! $default_query_params) {
945
+        if ( ! $default_query_params) {
946 946
             $default_query_params = \EEM_Base::default_where_conditions_all;
947 947
         }
948 948
         if (
@@ -1027,14 +1027,14 @@  discard block
 block discarded – undo
1027 1027
         }
1028 1028
         if (isset($query_parameters['limit'])) {
1029 1029
             //limit should be either a string like '23' or '23,43', or an array with two items in it
1030
-            if (! is_array($query_parameters['limit'])) {
1031
-                $limit_array = explode(',', (string)$query_parameters['limit']);
1030
+            if ( ! is_array($query_parameters['limit'])) {
1031
+                $limit_array = explode(',', (string) $query_parameters['limit']);
1032 1032
             } else {
1033 1033
                 $limit_array = $query_parameters['limit'];
1034 1034
             }
1035 1035
             $sanitized_limit = array();
1036 1036
             foreach ($limit_array as $key => $limit_part) {
1037
-                if ($this->_debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1037
+                if ($this->_debug_mode && ( ! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1038 1038
                     throw new \EE_Error(
1039 1039
                         sprintf(
1040 1040
                             __('An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
@@ -1043,7 +1043,7 @@  discard block
 block discarded – undo
1043 1043
                         )
1044 1044
                     );
1045 1045
                 }
1046
-                $sanitized_limit[] = (int)$limit_part;
1046
+                $sanitized_limit[] = (int) $limit_part;
1047 1047
             }
1048 1048
             $model_query_params['limit'] = implode(',', $sanitized_limit);
1049 1049
         } else {
@@ -1131,17 +1131,17 @@  discard block
 block discarded – undo
1131 1131
         foreach ($exploded_contents as $item) {
1132 1132
             $item = trim($item);
1133 1133
             //if no prefix was provided, so we look for items with no "." in them
1134
-            if (! $prefix) {
1134
+            if ( ! $prefix) {
1135 1135
                 //does this item have a period?
1136 1136
                 if (strpos($item, '.') === false) {
1137 1137
                     //if not, then its what we're looking for
1138 1138
                     $contents_with_prefix[] = $item;
1139 1139
                 }
1140
-            } else if (strpos($item, $prefix . '.') === 0) {
1140
+            } else if (strpos($item, $prefix.'.') === 0) {
1141 1141
                 //this item has the prefix and a period, grab it
1142 1142
                 $contents_with_prefix[] = substr(
1143 1143
                     $item,
1144
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1144
+                    strpos($item, $prefix.'.') + strlen($prefix.'.')
1145 1145
                 );
1146 1146
             } else if ($item === $prefix) {
1147 1147
                 //this item is JUST the prefix
@@ -1181,9 +1181,9 @@  discard block
 block discarded – undo
1181 1181
         if ($model_name) {
1182 1182
             foreach ($includes as $field_to_include) {
1183 1183
                 $field_to_include = trim($field_to_include);
1184
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1184
+                if (strpos($field_to_include, $model_name.'.') === 0) {
1185 1185
                     //found the model name at the exact start
1186
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1186
+                    $field_sans_model_name = str_replace($model_name.'.', '', $field_to_include);
1187 1187
                     $extracted_fields_to_include[] = $field_sans_model_name;
1188 1188
                 } elseif ($field_to_include == $model_name) {
1189 1189
                     $extracted_fields_to_include[] = '*';
Please login to merge, or discard this patch.