Completed
Branch trashed-datetimes-in-queries (9af1cf)
by
unknown
38:51 queued 29:30
created
core/db_models/EEM_Datetime.model.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
      * @param int      $TKT_ID           ID of ticket to retrieve the datetimes for
464 464
      * @param boolean  $include_expired  whether to include expired datetimes or not
465 465
      * @param boolean  $include_deleted  whether to include trashed datetimes or not.
466
-     * @param int|null $limit            if null, no limit, if int then limit results by
466
+     * @param integer $limit            if null, no limit, if int then limit results by
467 467
      *                                   that number
468 468
      * @return EE_Datetime[]
469 469
      * @throws EE_Error
@@ -648,7 +648,7 @@  discard block
 block discarded – undo
648 648
     /**
649 649
      * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
650 650
      *
651
-     * @param array $stati_to_include  If included you can restrict the statuses we return counts for by including the
651
+     * @param string[] $stati_to_include  If included you can restrict the statuses we return counts for by including the
652 652
      *                                 stati you want counts for as values in the array.  An empty array returns counts
653 653
      *                                 for all valid stati.
654 654
      * @param array $query_params      If included can be used to refine the conditions for returning the count (i.e.
Please login to merge, or discard this patch.
Indentation   +830 added lines, -830 removed lines patch added patch discarded remove patch
@@ -13,834 +13,834 @@
 block discarded – undo
13 13
 class EEM_Datetime extends EEM_Soft_Delete_Base
14 14
 {
15 15
 
16
-    /**
17
-     * @var EEM_Datetime $_instance
18
-     */
19
-    protected static $_instance;
20
-
21
-
22
-    /**
23
-     * private constructor to prevent direct creation
24
-     *
25
-     * @param string $timezone A string representing the timezone we want to set for returned Date Time Strings
26
-     *                         (and any incoming timezone data that gets saved).
27
-     *                         Note this just sends the timezone info to the date time model field objects.
28
-     *                         Default is NULL
29
-     *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
30
-     * @throws EE_Error
31
-     * @throws InvalidArgumentException
32
-     * @throws InvalidArgumentException
33
-     */
34
-    protected function __construct($timezone)
35
-    {
36
-        $this->singular_item           = esc_html__('Datetime', 'event_espresso');
37
-        $this->plural_item             = esc_html__('Datetimes', 'event_espresso');
38
-        $this->_tables                 = [
39
-            'Datetime' => new EE_Primary_Table('esp_datetime', 'DTT_ID'),
40
-        ];
41
-        $this->_fields                 = [
42
-            'Datetime' => [
43
-                'DTT_ID'          => new EE_Primary_Key_Int_Field(
44
-                    'DTT_ID',
45
-                    esc_html__('Datetime ID', 'event_espresso')
46
-                ),
47
-                'EVT_ID'          => new EE_Foreign_Key_Int_Field(
48
-                    'EVT_ID',
49
-                    esc_html__('Event ID', 'event_espresso'),
50
-                    false,
51
-                    0,
52
-                    'Event'
53
-                ),
54
-                'DTT_name'        => new EE_Plain_Text_Field(
55
-                    'DTT_name',
56
-                    esc_html__('Datetime Name', 'event_espresso'),
57
-                    false,
58
-                    ''
59
-                ),
60
-                'DTT_description' => new EE_Post_Content_Field(
61
-                    'DTT_description',
62
-                    esc_html__('Description for Datetime', 'event_espresso'),
63
-                    false,
64
-                    ''
65
-                ),
66
-                'DTT_EVT_start'   => new EE_Datetime_Field(
67
-                    'DTT_EVT_start',
68
-                    esc_html__('Start time/date of Event', 'event_espresso'),
69
-                    false,
70
-                    EE_Datetime_Field::now,
71
-                    $timezone
72
-                ),
73
-                'DTT_EVT_end'     => new EE_Datetime_Field(
74
-                    'DTT_EVT_end',
75
-                    esc_html__('End time/date of Event', 'event_espresso'),
76
-                    false,
77
-                    EE_Datetime_Field::now,
78
-                    $timezone
79
-                ),
80
-                'DTT_reg_limit'   => new EE_Infinite_Integer_Field(
81
-                    'DTT_reg_limit',
82
-                    esc_html__('Registration Limit for this time', 'event_espresso'),
83
-                    true,
84
-                    EE_INF
85
-                ),
86
-                'DTT_sold'        => new EE_Integer_Field(
87
-                    'DTT_sold',
88
-                    esc_html__('How many sales for this Datetime that have occurred', 'event_espresso'),
89
-                    true,
90
-                    0
91
-                ),
92
-                'DTT_reserved'    => new EE_Integer_Field(
93
-                    'DTT_reserved',
94
-                    esc_html__('Quantity of tickets reserved, but not yet fully purchased', 'event_espresso'),
95
-                    false,
96
-                    0
97
-                ),
98
-                'DTT_is_primary'  => new EE_Boolean_Field(
99
-                    'DTT_is_primary',
100
-                    esc_html__('Flag indicating datetime is primary one for event', 'event_espresso'),
101
-                    false,
102
-                    false
103
-                ),
104
-                'DTT_order'       => new EE_Integer_Field(
105
-                    'DTT_order',
106
-                    esc_html__('The order in which the Datetime is displayed', 'event_espresso'),
107
-                    false,
108
-                    0
109
-                ),
110
-                'DTT_parent'      => new EE_Integer_Field(
111
-                    'DTT_parent',
112
-                    esc_html__('Indicates what DTT_ID is the parent of this DTT_ID', 'event_espresso'),
113
-                    true,
114
-                    0
115
-                ),
116
-                'DTT_deleted'     => new EE_Trashed_Flag_Field(
117
-                    'DTT_deleted',
118
-                    esc_html__('Flag indicating datetime is archived', 'event_espresso'),
119
-                    false,
120
-                    false
121
-                ),
122
-            ],
123
-        ];
124
-        $this->_model_relations        = [
125
-            'Ticket'          => new EE_HABTM_Relation('Datetime_Ticket'),
126
-            'Event'           => new EE_Belongs_To_Relation(),
127
-            'Checkin'         => new EE_Has_Many_Relation(),
128
-            'Datetime_Ticket' => new EE_Has_Many_Relation(),
129
-        ];
130
-        $path_to_event_model           = 'Event';
131
-        $this->model_chain_to_password = $path_to_event_model;
132
-        $this->_model_chain_to_wp_user = $path_to_event_model;
133
-        // this model is generally available for reading
134
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       =
135
-            new EE_Restriction_Generator_Event_Related_Public(
136
-                $path_to_event_model
137
-            );
138
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
139
-            new EE_Restriction_Generator_Event_Related_Protected(
140
-                $path_to_event_model
141
-            );
142
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       =
143
-            new EE_Restriction_Generator_Event_Related_Protected(
144
-                $path_to_event_model
145
-            );
146
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     =
147
-            new EE_Restriction_Generator_Event_Related_Protected(
148
-                $path_to_event_model,
149
-                EEM_Base::caps_edit
150
-            );
151
-        parent::__construct($timezone);
152
-    }
153
-
154
-
155
-    /**
156
-     * create new blank datetime
157
-     *
158
-     * @access public
159
-     * @return EE_Datetime[] array on success, FALSE on fail
160
-     * @throws EE_Error
161
-     * @throws InvalidArgumentException
162
-     * @throws InvalidDataTypeException
163
-     * @throws ReflectionException
164
-     * @throws InvalidInterfaceException
165
-     */
166
-    public function create_new_blank_datetime()
167
-    {
168
-        // makes sure timezone is always set.
169
-        $timezone_string = $this->get_timezone();
170
-        /**
171
-         * Filters the initial start date for the new datetime.
172
-         * Any time included in this value will be overridden later so use additional filters to modify the time.
173
-         *
174
-         * @param int $start_date Unix timestamp representing now + 30 days in seconds.
175
-         * @return int Unix timestamp
176
-         */
177
-        $start_date = apply_filters(
178
-            'FHEE__EEM_Datetime__create_new_blank_datetime__start_date',
179
-            $this->current_time_for_query('DTT_EVT_start', true) + MONTH_IN_SECONDS
180
-        );
181
-        /**
182
-         * Filters the initial end date for the new datetime.
183
-         * Any time included in this value will be overridden later so use additional filters to modify the time.
184
-         *
185
-         * @param int $end_data Unix timestamp representing now + 30 days in seconds.
186
-         * @return int Unix timestamp
187
-         */
188
-        $end_date       = apply_filters(
189
-            'FHEE__EEM_Datetime__create_new_blank_datetime__end_date',
190
-            $this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS
191
-        );
192
-        $blank_datetime = EE_Datetime::new_instance(
193
-            [
194
-                'DTT_EVT_start' => $start_date,
195
-                'DTT_EVT_end'   => $end_date,
196
-                'DTT_order'     => 1,
197
-                'DTT_reg_limit' => EE_INF,
198
-            ],
199
-            $timezone_string
200
-        );
201
-        /**
202
-         * Filters the initial start time and format for the new EE_Datetime instance.
203
-         *
204
-         * @param array $start_time An array having size 2.  First element is the time, second element is the time
205
-         *                          format.
206
-         * @return array
207
-         */
208
-        $start_time = apply_filters(
209
-            'FHEE__EEM_Datetime__create_new_blank_datetime__start_time',
210
-            ['8am', 'ga']
211
-        );
212
-        /**
213
-         * Filters the initial end time and format for the new EE_Datetime instance.
214
-         *
215
-         * @param array $end_time An array having size 2.  First element is the time, second element is the time
216
-         *                        format
217
-         * @return array
218
-         */
219
-        $end_time = apply_filters(
220
-            'FHEE__EEM_Datetime__create_new_blank_datetime__end_time',
221
-            ['5pm', 'ga']
222
-        );
223
-        $this->validateStartAndEndTimeForBlankDate($start_time, $end_time);
224
-        $blank_datetime->set_start_time(
225
-            $this->convert_datetime_for_query(
226
-                'DTT_EVT_start',
227
-                $start_time[0],
228
-                $start_time[1],
229
-                $timezone_string
230
-            )
231
-        );
232
-        $blank_datetime->set_end_time(
233
-            $this->convert_datetime_for_query(
234
-                'DTT_EVT_end',
235
-                $end_time[0],
236
-                $end_time[1],
237
-                $timezone_string
238
-            )
239
-        );
240
-        return [$blank_datetime];
241
-    }
242
-
243
-
244
-    /**
245
-     * Validates whether the start_time and end_time are in the expected format.
246
-     *
247
-     * @param array $start_time
248
-     * @param array $end_time
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidDataTypeException
251
-     */
252
-    private function validateStartAndEndTimeForBlankDate(array $start_time, array $end_time)
253
-    {
254
-        if (! is_array($start_time)) {
255
-            throw new InvalidDataTypeException('start_time', $start_time, 'array');
256
-        }
257
-        if (! is_array($end_time)) {
258
-            throw new InvalidDataTypeException('end_time', $end_time, 'array');
259
-        }
260
-        if (count($start_time) !== 2) {
261
-            throw new InvalidArgumentException(
262
-                sprintf(
263
-                    'The variable %1$s is expected to be an array with two elements.  The first item in the '
264
-                    . 'array should be a valid time string, the second item in the array should be a valid time format',
265
-                    '$start_time'
266
-                )
267
-            );
268
-        }
269
-        if (count($end_time) !== 2) {
270
-            throw new InvalidArgumentException(
271
-                sprintf(
272
-                    'The variable %1$s is expected to be an array with two elements.  The first item in the '
273
-                    . 'array should be a valid time string, the second item in the array should be a valid time format',
274
-                    '$end_time'
275
-                )
276
-            );
277
-        }
278
-    }
279
-
280
-
281
-    /**
282
-     * get event start date from db
283
-     *
284
-     * @access public
285
-     * @param int $EVT_ID
286
-     * @return EE_Datetime[] array on success, FALSE on fail
287
-     * @throws EE_Error
288
-     * @throws ReflectionException
289
-     */
290
-    public function get_all_event_dates($EVT_ID = 0)
291
-    {
292
-        if (! $EVT_ID) { // on add_new_event event_id gets set to 0
293
-            return $this->create_new_blank_datetime();
294
-        }
295
-        $results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
296
-        if (empty($results)) {
297
-            return $this->create_new_blank_datetime();
298
-        }
299
-        return $results;
300
-    }
301
-
302
-
303
-    /**
304
-     * get all datetimes attached to an event ordered by the DTT_order field
305
-     *
306
-     * @public
307
-     * @param int     $EVT_ID     event id
308
-     * @param boolean $include_expired
309
-     * @param boolean $include_deleted
310
-     * @param int     $limit      If included then limit the count of results by
311
-     *                            the given number
312
-     * @return EE_Datetime[]
313
-     * @throws EE_Error
314
-     */
315
-    public function get_datetimes_for_event_ordered_by_DTT_order(
316
-        int $EVT_ID,
317
-        bool $include_expired = true,
318
-        bool $include_deleted = true,
319
-        $limit = 0
320
-    ) {
321
-        $prev_data_prep_value = $this->prepModelForQuery();
322
-        $where_params         = ['Event.EVT_ID' => absint($EVT_ID)];
323
-        $query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
324
-        $query_params         = $this->addDefaultWhereConditions($query_params);
325
-        $query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
326
-        return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
327
-    }
328
-
329
-
330
-    /**
331
-     * Gets the datetimes for the event (with the given limit), and orders them by "importance".
332
-     * By importance, we mean that the primary datetimes are most important (DEPRECATED FOR NOW),
333
-     * and then the earlier datetimes are the most important.
334
-     * Maybe we'll want this to take into account datetimes that haven't already passed, but we don't yet.
335
-     *
336
-     * @param int $EVT_ID
337
-     * @param int $limit
338
-     * @return EE_Datetime[]|EE_Base_Class[]
339
-     * @throws EE_Error
340
-     */
341
-    public function get_datetimes_for_event_ordered_by_importance(int $EVT_ID, $limit = 0)
342
-    {
343
-        $query_params[0] = ['Event.EVT_ID' => absint($EVT_ID)];
344
-        $query_params    = $this->addDefaultWhereConditions($query_params);
345
-        $query_params    = $this->addDefaultQueryParams($query_params, $limit);
346
-        return $this->get_all($query_params);
347
-    }
348
-
349
-
350
-    /**
351
-     * @param int     $EVT_ID
352
-     * @param boolean $include_expired
353
-     * @param boolean $include_deleted
354
-     * @return EE_Datetime
355
-     * @throws EE_Error
356
-     */
357
-    public function get_oldest_datetime_for_event(
358
-        int $EVT_ID,
359
-        bool $include_expired = false,
360
-        bool $include_deleted = false
361
-    ) {
362
-        $results = $this->get_datetimes_for_event_ordered_by_start_time(
363
-            $EVT_ID,
364
-            $include_expired,
365
-            $include_deleted,
366
-            1
367
-        );
368
-        if ($results) {
369
-            return array_shift($results);
370
-        }
371
-        return null;
372
-    }
373
-
374
-
375
-    /**
376
-     * Gets the 'primary' datetime for an event.
377
-     *
378
-     * @param int  $EVT_ID
379
-     * @param bool $try_to_exclude_expired
380
-     * @param bool $try_to_exclude_deleted
381
-     * @return EE_Datetime
382
-     * @throws EE_Error
383
-     */
384
-    public function get_primary_datetime_for_event(
385
-        int $EVT_ID,
386
-        bool $try_to_exclude_expired = true,
387
-        bool $try_to_exclude_deleted = true
388
-    ) {
389
-        if ($try_to_exclude_expired) {
390
-            $non_expired = $this->get_oldest_datetime_for_event($EVT_ID, false, false);
391
-            if ($non_expired) {
392
-                return $non_expired;
393
-            }
394
-        }
395
-        if ($try_to_exclude_deleted) {
396
-            $expired_even = $this->get_oldest_datetime_for_event($EVT_ID, true);
397
-            if ($expired_even) {
398
-                return $expired_even;
399
-            }
400
-        }
401
-        return $this->get_oldest_datetime_for_event($EVT_ID, true, true);
402
-    }
403
-
404
-
405
-    /**
406
-     * Gets ALL the datetimes for an event (including trashed ones, for now), ordered
407
-     * only by start date
408
-     *
409
-     * @param int     $EVT_ID
410
-     * @param boolean $include_expired
411
-     * @param boolean $include_deleted
412
-     * @param int     $limit
413
-     * @return EE_Datetime[]
414
-     * @throws EE_Error
415
-     */
416
-    public function get_datetimes_for_event_ordered_by_start_time(
417
-        int $EVT_ID,
418
-        bool $include_expired = true,
419
-        bool $include_deleted = true,
420
-        $limit = 0
421
-    ) {
422
-        $prev_data_prep_value = $this->prepModelForQuery();
423
-        $where_params         = ['Event.EVT_ID' => absint($EVT_ID)];
424
-        $query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
425
-        $query_params         = $this->addDefaultWhereConditions(
426
-            $query_params,
427
-            EEM_Base::default_where_conditions_this_only
428
-        );
429
-        $query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
430
-        return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
431
-    }
432
-
433
-
434
-    /**
435
-     * Gets ALL the datetimes for an ticket (including trashed ones, for now), ordered
436
-     * only by start date
437
-     *
438
-     * @param int     $TKT_ID
439
-     * @param boolean $include_expired
440
-     * @param boolean $include_deleted
441
-     * @param int     $limit
442
-     * @return EE_Datetime[]
443
-     * @throws EE_Error
444
-     */
445
-    public function get_datetimes_for_ticket_ordered_by_start_time(
446
-        int $TKT_ID,
447
-        bool $include_expired = true,
448
-        bool $include_deleted = true,
449
-        $limit = 0
450
-    ) {
451
-        $prev_data_prep_value = $this->prepModelForQuery();
452
-        $where_params         = ['Ticket.TKT_ID' => absint($TKT_ID)];
453
-        $query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
454
-        $query_params         = $this->addDefaultQueryParams($query_params, $limit);
455
-        return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
456
-    }
457
-
458
-
459
-    /**
460
-     * Gets all the datetimes for a ticket (including trashed ones, for now), ordered by the DTT_order for the
461
-     * datetimes.
462
-     *
463
-     * @param int      $TKT_ID           ID of ticket to retrieve the datetimes for
464
-     * @param boolean  $include_expired  whether to include expired datetimes or not
465
-     * @param boolean  $include_deleted  whether to include trashed datetimes or not.
466
-     * @param int|null $limit            if null, no limit, if int then limit results by
467
-     *                                   that number
468
-     * @return EE_Datetime[]
469
-     * @throws EE_Error
470
-     */
471
-    public function get_datetimes_for_ticket_ordered_by_DTT_order(
472
-        int $TKT_ID,
473
-        bool $include_expired = true,
474
-        bool $include_deleted = true,
475
-        $limit = 0
476
-    ) {
477
-        $prev_data_prep_value = $this->prepModelForQuery();
478
-        $where_params         = ['Ticket.TKT_ID' => absint($TKT_ID)];
479
-        $query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
480
-        $query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
481
-        return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
482
-    }
483
-
484
-
485
-    /**
486
-     * Gets the most important datetime for a particular event (ie, the primary event usually. But if for some WACK
487
-     * reason it doesn't exist, we consider the earliest event the most important)
488
-     *
489
-     * @param int $EVT_ID
490
-     * @return EE_Datetime
491
-     * @throws EE_Error
492
-     */
493
-    public function get_most_important_datetime_for_event(int $EVT_ID)
494
-    {
495
-        $results = $this->get_datetimes_for_event_ordered_by_importance($EVT_ID, 1);
496
-        if ($results) {
497
-            return array_shift($results);
498
-        }
499
-        return null;
500
-    }
501
-
502
-
503
-    /**
504
-     * This returns a wpdb->results        Array of all DTT month and years matching the incoming query params and
505
-     * grouped by month and year.
506
-     *
507
-     * @param array  $where_params       @see
508
-     *                                   https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
509
-     * @param string $evt_active_status  A string representing the evt active status to filter the months by.
510
-     *                                   Can be:
511
-     *                                   - '' = no filter
512
-     *                                   - upcoming = Published events with at least one upcoming datetime.
513
-     *                                   - expired = Events with all datetimes expired.
514
-     *                                   - active = Events that are published and have at least one datetime that
515
-     *                                   starts before now and ends after now.
516
-     *                                   - inactive = Events that are either not published.
517
-     * @return EE_Base_Class[]
518
-     * @throws EE_Error
519
-     * @throws InvalidArgumentException
520
-     * @throws InvalidArgumentException
521
-     */
522
-    public function get_dtt_months_and_years(array $where_params, $evt_active_status = '')
523
-    {
524
-        $current_time_for_DTT_EVT_start = $this->current_time_for_query('DTT_EVT_start');
525
-        $current_time_for_DTT_EVT_end   = $this->current_time_for_query('DTT_EVT_end');
526
-        switch ($evt_active_status) {
527
-            case 'upcoming':
528
-                $where_params['Event.status'] = 'publish';
529
-                // if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
530
-                if (isset($where_params['DTT_EVT_start'])) {
531
-                    $where_params['DTT_EVT_start*****'] = $where_params['DTT_EVT_start'];
532
-                }
533
-                $where_params['DTT_EVT_start'] = ['>', $current_time_for_DTT_EVT_start];
534
-                break;
535
-            case 'expired':
536
-                if (isset($where_params['Event.status'])) {
537
-                    unset($where_params['Event.status']);
538
-                }
539
-                // get events to exclude
540
-                $exclude_query[0] = array_merge(
541
-                    $where_params,
542
-                    ['DTT_EVT_end' => ['>', $current_time_for_DTT_EVT_end]]
543
-                );
544
-                // first get all events that have datetimes where its not expired.
545
-                $event_ids = $this->_get_all_wpdb_results(
546
-                    $exclude_query,
547
-                    OBJECT_K,
548
-                    'Datetime.EVT_ID'
549
-                );
550
-                $event_ids = array_keys($event_ids);
551
-                if (isset($where_params['DTT_EVT_end'])) {
552
-                    $where_params['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
553
-                }
554
-                $where_params['DTT_EVT_end']  = ['<', $current_time_for_DTT_EVT_end];
555
-                $where_params['Event.EVT_ID'] = ['NOT IN', $event_ids];
556
-                break;
557
-            case 'active':
558
-                $where_params['Event.status'] = 'publish';
559
-                if (isset($where_params['DTT_EVT_start'])) {
560
-                    $where_params['Datetime.DTT_EVT_start******'] = $where_params['DTT_EVT_start'];
561
-                }
562
-                if (isset($where_params['Datetime.DTT_EVT_end'])) {
563
-                    $where_params['Datetime.DTT_EVT_end*****'] = $where_params['DTT_EVT_end'];
564
-                }
565
-                $where_params['DTT_EVT_start'] = ['<', $current_time_for_DTT_EVT_start];
566
-                $where_params['DTT_EVT_end']   = ['>', $current_time_for_DTT_EVT_end];
567
-                break;
568
-            case 'inactive':
569
-                if (isset($where_params['Event.status'])) {
570
-                    unset($where_params['Event.status']);
571
-                }
572
-                if (isset($where_params['OR'])) {
573
-                    $where_params['AND']['OR'] = $where_params['OR'];
574
-                }
575
-                if (isset($where_params['DTT_EVT_end'])) {
576
-                    $where_params['AND']['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
577
-                    unset($where_params['DTT_EVT_end']);
578
-                }
579
-                if (isset($where_params['DTT_EVT_start'])) {
580
-                    $where_params['AND']['DTT_EVT_start'] = $where_params['DTT_EVT_start'];
581
-                    unset($where_params['DTT_EVT_start']);
582
-                }
583
-                $where_params['AND']['Event.status'] = ['!=', 'publish'];
584
-                break;
585
-        }
586
-        $query_params[0]          = $where_params;
587
-        $query_params['group_by'] = ['dtt_year', 'dtt_month'];
588
-        $query_params             = $this->addOrderByQueryParams($query_params, 'DTT_EVT_start', 'DESC');
589
-
590
-        $query_interval    = EEH_DTT_Helper::get_sql_query_interval_for_offset(
591
-            $this->get_timezone(),
592
-            'DTT_EVT_start'
593
-        );
594
-        $columns_to_select = [
595
-            'dtt_year'      => ['YEAR(' . $query_interval . ')', '%s'],
596
-            'dtt_month'     => ['MONTHNAME(' . $query_interval . ')', '%s'],
597
-            'dtt_month_num' => ['MONTH(' . $query_interval . ')', '%s'],
598
-        ];
599
-        return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
600
-    }
601
-
602
-
603
-    /**
604
-     * Updates the DTT_sold attribute on each datetime (based on the registrations
605
-     * for the tickets for each datetime)
606
-     *
607
-     * @param EE_Base_Class[]|EE_Datetime[] $datetimes
608
-     * @throws EE_Error
609
-     * @throws ReflectionException
610
-     */
611
-    public function update_sold(array $datetimes)
612
-    {
613
-        EE_Error::doing_it_wrong(
614
-            __FUNCTION__,
615
-            esc_html__(
616
-                'Please use \EEM_Ticket::update_tickets_sold() instead which will in turn correctly update both the Ticket AND Datetime counts.',
617
-                'event_espresso'
618
-            ),
619
-            '4.9.32.rc.005'
620
-        );
621
-        foreach ($datetimes as $datetime) {
622
-            $datetime->update_sold();
623
-        }
624
-    }
625
-
626
-
627
-    /**
628
-     *    Gets the total number of tickets available at a particular datetime
629
-     *    (does NOT take into account the datetime's spaces available)
630
-     *
631
-     * @param int   $DTT_ID
632
-     * @param array $query_params
633
-     * @return int of tickets available. If sold out, return less than 1. If infinite, returns EE_INF,  IF there are NO
634
-     *             tickets attached to datetime then FALSE is returned.
635
-     * @throws EE_Error
636
-     * @throws ReflectionException
637
-     */
638
-    public function sum_tickets_currently_available_at_datetime(int $DTT_ID, array $query_params = [])
639
-    {
640
-        $datetime = $this->get_one_by_ID($DTT_ID);
641
-        if ($datetime instanceof EE_Datetime) {
642
-            return $datetime->tickets_remaining($query_params);
643
-        }
644
-        return 0;
645
-    }
646
-
647
-
648
-    /**
649
-     * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
650
-     *
651
-     * @param array $stati_to_include  If included you can restrict the statuses we return counts for by including the
652
-     *                                 stati you want counts for as values in the array.  An empty array returns counts
653
-     *                                 for all valid stati.
654
-     * @param array $query_params      If included can be used to refine the conditions for returning the count (i.e.
655
-     *                                 only for Datetimes connected to a specific event, or specific ticket.
656
-     * @return array  The value returned is an array indexed by Datetime Status and the values are the counts.  The
657
-     * @throws EE_Error
658
-     *                                 stati used as index keys are: EE_Datetime::active EE_Datetime::upcoming
659
-     *                                 EE_Datetime::expired
660
-     */
661
-    public function get_datetime_counts_by_status(array $stati_to_include = [], array $query_params = [])
662
-    {
663
-        // only accept where conditions for this query.
664
-        $_where            = isset($query_params[0]) ? $query_params[0] : [];
665
-        $status_query_args = [
666
-            EE_Datetime::active   => array_merge(
667
-                $_where,
668
-                ['DTT_EVT_start' => ['<', time()], 'DTT_EVT_end' => ['>', time()]]
669
-            ),
670
-            EE_Datetime::upcoming => array_merge(
671
-                $_where,
672
-                ['DTT_EVT_start' => ['>', time()]]
673
-            ),
674
-            EE_Datetime::expired  => array_merge(
675
-                $_where,
676
-                ['DTT_EVT_end' => ['<', time()]]
677
-            ),
678
-        ];
679
-        if (! empty($stati_to_include)) {
680
-            foreach (array_keys($status_query_args) as $status) {
681
-                if (! in_array($status, $stati_to_include, true)) {
682
-                    unset($status_query_args[ $status ]);
683
-                }
684
-            }
685
-        }
686
-        // loop through and query counts for each stati.
687
-        $status_query_results = [];
688
-        foreach ($status_query_args as $status => $status_where_conditions) {
689
-            $status_query_results[ $status ] = EEM_Datetime::count(
690
-                [$status_where_conditions],
691
-                'DTT_ID',
692
-                true
693
-            );
694
-        }
695
-        return $status_query_results;
696
-    }
697
-
698
-
699
-    /**
700
-     * Returns the specific count for a given Datetime status matching any given query_params.
701
-     *
702
-     * @param string $status Valid string representation for Datetime status requested. (Defaults to Active).
703
-     * @param array  $query_params
704
-     * @return int
705
-     * @throws EE_Error
706
-     */
707
-    public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = [])
708
-    {
709
-        $count = $this->get_datetime_counts_by_status([$status], $query_params);
710
-        return ! empty($count[ $status ]) ? $count[ $status ] : 0;
711
-    }
712
-
713
-
714
-    /**
715
-     * @return bool|int
716
-     * @since   $VID:$
717
-     */
718
-    private function prepModelForQuery()
719
-    {
720
-        $prev_data_prep_value = $this->get_assumption_concerning_values_already_prepared_by_model_object();
721
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
722
-        return $prev_data_prep_value;
723
-    }
724
-
725
-
726
-    /**
727
-     * @param array    $query_params
728
-     * @param bool|int $prev_data_prep_value
729
-     * @return EE_Base_Class[]|EE_Datetime[]
730
-     * @throws EE_Error
731
-     * @since   $VID:$
732
-     */
733
-    private function getDatetimesAndRestoreModel(array $query_params, $prev_data_prep_value)
734
-    {
735
-        $result = $this->get_all($query_params);
736
-        $this->assume_values_already_prepared_by_model_object($prev_data_prep_value);
737
-        return $result;
738
-    }
739
-
740
-
741
-    /**
742
-     * @param array  $query_params
743
-     * @param int    $limit
744
-     * @param string $order_by
745
-     * @param string $order
746
-     * @return array
747
-     * @since   $VID:$
748
-     */
749
-    private function addDefaultQueryParams(array $query_params, $limit = 0, $order_by = 'DTT_EVT_start', $order = 'ASC')
750
-    {
751
-        $query_params = $this->addOrderByQueryParams($query_params, $order_by, $order);
752
-        $query_params = $this->addLimitQueryParams($query_params, $limit);
753
-        return $query_params;
754
-    }
755
-
756
-
757
-    /**
758
-     * @param array  $query_params
759
-     * @param string $default_where_conditions
760
-     * @return array
761
-     * @since   $VID:$
762
-     */
763
-    private function addDefaultWhereConditions(
764
-        array $query_params,
765
-        $default_where_conditions = EEM_Base::default_where_conditions_none
766
-    ) {
767
-        $query_params['default_where_conditions'] = $default_where_conditions;
768
-        return $query_params;
769
-    }
770
-
771
-
772
-    /**
773
-     * @param array $where_params
774
-     * @param bool  $include_deleted
775
-     * @param bool  $include_expired
776
-     * @return array
777
-     * @since   $VID:$
778
-     */
779
-    private function addDefaultWhereParams(array $where_params, bool $include_deleted = true, bool $include_expired = true)
780
-    {
781
-        $where_params = $this->addExpiredWhereParams($where_params, $include_expired);
782
-        $where_params = $this->addDeletedWhereParams($where_params, $include_deleted);
783
-        return $where_params;
784
-    }
785
-
786
-
787
-    /**
788
-     * @param array $where_params
789
-     * @param bool  $include_deleted
790
-     * @return array
791
-     * @since   $VID:$
792
-     */
793
-    private function addDeletedWhereParams(array $where_params, bool $include_deleted = true)
794
-    {
795
-        $deleted                     = $include_deleted ? [true, false] : [false];
796
-        $where_params['DTT_deleted'] = ['IN', $deleted];
797
-        return $where_params;
798
-    }
799
-
800
-
801
-    /**
802
-     * @param array $where_params
803
-     * @param bool  $include_expired
804
-     * @return array
805
-     * @since   $VID:$
806
-     */
807
-    private function addExpiredWhereParams(array $where_params, bool $include_expired = true)
808
-    {
809
-        if (! $include_expired) {
810
-            $where_params['DTT_EVT_end'] = ['>=', current_time('mysql', true)];
811
-        }
812
-        return $where_params;
813
-    }
814
-
815
-
816
-    /**
817
-     * @param array $query_params
818
-     * @param int   $limit
819
-     * @return array
820
-     * @since   $VID:$
821
-     */
822
-    private function addLimitQueryParams(array $query_params, $limit = 0)
823
-    {
824
-        if ($limit) {
825
-            $query_params['limit'] = $limit;
826
-        }
827
-        return $query_params;
828
-    }
829
-
830
-
831
-    /**
832
-     * @param array  $query_params
833
-     * @param string $order_by
834
-     * @param string $order
835
-     * @return array
836
-     * @since   $VID:$
837
-     */
838
-    private function addOrderByQueryParams(array $query_params, $order_by = 'DTT_EVT_start', $order = 'ASC')
839
-    {
840
-        $order                    = $order === 'ASC' ? 'ASC' : 'DESC';
841
-        $valid_order_columns      = ['DTT_ID', 'DTT_EVT_start', 'DTT_EVT_end', 'DTT_order'];
842
-        $order_by                 = in_array($order_by, $valid_order_columns, true) ? $order_by : 'DTT_EVT_start';
843
-        $query_params['order_by'] = [$order_by => $order];
844
-        return $query_params;
845
-    }
16
+	/**
17
+	 * @var EEM_Datetime $_instance
18
+	 */
19
+	protected static $_instance;
20
+
21
+
22
+	/**
23
+	 * private constructor to prevent direct creation
24
+	 *
25
+	 * @param string $timezone A string representing the timezone we want to set for returned Date Time Strings
26
+	 *                         (and any incoming timezone data that gets saved).
27
+	 *                         Note this just sends the timezone info to the date time model field objects.
28
+	 *                         Default is NULL
29
+	 *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
30
+	 * @throws EE_Error
31
+	 * @throws InvalidArgumentException
32
+	 * @throws InvalidArgumentException
33
+	 */
34
+	protected function __construct($timezone)
35
+	{
36
+		$this->singular_item           = esc_html__('Datetime', 'event_espresso');
37
+		$this->plural_item             = esc_html__('Datetimes', 'event_espresso');
38
+		$this->_tables                 = [
39
+			'Datetime' => new EE_Primary_Table('esp_datetime', 'DTT_ID'),
40
+		];
41
+		$this->_fields                 = [
42
+			'Datetime' => [
43
+				'DTT_ID'          => new EE_Primary_Key_Int_Field(
44
+					'DTT_ID',
45
+					esc_html__('Datetime ID', 'event_espresso')
46
+				),
47
+				'EVT_ID'          => new EE_Foreign_Key_Int_Field(
48
+					'EVT_ID',
49
+					esc_html__('Event ID', 'event_espresso'),
50
+					false,
51
+					0,
52
+					'Event'
53
+				),
54
+				'DTT_name'        => new EE_Plain_Text_Field(
55
+					'DTT_name',
56
+					esc_html__('Datetime Name', 'event_espresso'),
57
+					false,
58
+					''
59
+				),
60
+				'DTT_description' => new EE_Post_Content_Field(
61
+					'DTT_description',
62
+					esc_html__('Description for Datetime', 'event_espresso'),
63
+					false,
64
+					''
65
+				),
66
+				'DTT_EVT_start'   => new EE_Datetime_Field(
67
+					'DTT_EVT_start',
68
+					esc_html__('Start time/date of Event', 'event_espresso'),
69
+					false,
70
+					EE_Datetime_Field::now,
71
+					$timezone
72
+				),
73
+				'DTT_EVT_end'     => new EE_Datetime_Field(
74
+					'DTT_EVT_end',
75
+					esc_html__('End time/date of Event', 'event_espresso'),
76
+					false,
77
+					EE_Datetime_Field::now,
78
+					$timezone
79
+				),
80
+				'DTT_reg_limit'   => new EE_Infinite_Integer_Field(
81
+					'DTT_reg_limit',
82
+					esc_html__('Registration Limit for this time', 'event_espresso'),
83
+					true,
84
+					EE_INF
85
+				),
86
+				'DTT_sold'        => new EE_Integer_Field(
87
+					'DTT_sold',
88
+					esc_html__('How many sales for this Datetime that have occurred', 'event_espresso'),
89
+					true,
90
+					0
91
+				),
92
+				'DTT_reserved'    => new EE_Integer_Field(
93
+					'DTT_reserved',
94
+					esc_html__('Quantity of tickets reserved, but not yet fully purchased', 'event_espresso'),
95
+					false,
96
+					0
97
+				),
98
+				'DTT_is_primary'  => new EE_Boolean_Field(
99
+					'DTT_is_primary',
100
+					esc_html__('Flag indicating datetime is primary one for event', 'event_espresso'),
101
+					false,
102
+					false
103
+				),
104
+				'DTT_order'       => new EE_Integer_Field(
105
+					'DTT_order',
106
+					esc_html__('The order in which the Datetime is displayed', 'event_espresso'),
107
+					false,
108
+					0
109
+				),
110
+				'DTT_parent'      => new EE_Integer_Field(
111
+					'DTT_parent',
112
+					esc_html__('Indicates what DTT_ID is the parent of this DTT_ID', 'event_espresso'),
113
+					true,
114
+					0
115
+				),
116
+				'DTT_deleted'     => new EE_Trashed_Flag_Field(
117
+					'DTT_deleted',
118
+					esc_html__('Flag indicating datetime is archived', 'event_espresso'),
119
+					false,
120
+					false
121
+				),
122
+			],
123
+		];
124
+		$this->_model_relations        = [
125
+			'Ticket'          => new EE_HABTM_Relation('Datetime_Ticket'),
126
+			'Event'           => new EE_Belongs_To_Relation(),
127
+			'Checkin'         => new EE_Has_Many_Relation(),
128
+			'Datetime_Ticket' => new EE_Has_Many_Relation(),
129
+		];
130
+		$path_to_event_model           = 'Event';
131
+		$this->model_chain_to_password = $path_to_event_model;
132
+		$this->_model_chain_to_wp_user = $path_to_event_model;
133
+		// this model is generally available for reading
134
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ]       =
135
+			new EE_Restriction_Generator_Event_Related_Public(
136
+				$path_to_event_model
137
+			);
138
+		$this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
139
+			new EE_Restriction_Generator_Event_Related_Protected(
140
+				$path_to_event_model
141
+			);
142
+		$this->_cap_restriction_generators[ EEM_Base::caps_edit ]       =
143
+			new EE_Restriction_Generator_Event_Related_Protected(
144
+				$path_to_event_model
145
+			);
146
+		$this->_cap_restriction_generators[ EEM_Base::caps_delete ]     =
147
+			new EE_Restriction_Generator_Event_Related_Protected(
148
+				$path_to_event_model,
149
+				EEM_Base::caps_edit
150
+			);
151
+		parent::__construct($timezone);
152
+	}
153
+
154
+
155
+	/**
156
+	 * create new blank datetime
157
+	 *
158
+	 * @access public
159
+	 * @return EE_Datetime[] array on success, FALSE on fail
160
+	 * @throws EE_Error
161
+	 * @throws InvalidArgumentException
162
+	 * @throws InvalidDataTypeException
163
+	 * @throws ReflectionException
164
+	 * @throws InvalidInterfaceException
165
+	 */
166
+	public function create_new_blank_datetime()
167
+	{
168
+		// makes sure timezone is always set.
169
+		$timezone_string = $this->get_timezone();
170
+		/**
171
+		 * Filters the initial start date for the new datetime.
172
+		 * Any time included in this value will be overridden later so use additional filters to modify the time.
173
+		 *
174
+		 * @param int $start_date Unix timestamp representing now + 30 days in seconds.
175
+		 * @return int Unix timestamp
176
+		 */
177
+		$start_date = apply_filters(
178
+			'FHEE__EEM_Datetime__create_new_blank_datetime__start_date',
179
+			$this->current_time_for_query('DTT_EVT_start', true) + MONTH_IN_SECONDS
180
+		);
181
+		/**
182
+		 * Filters the initial end date for the new datetime.
183
+		 * Any time included in this value will be overridden later so use additional filters to modify the time.
184
+		 *
185
+		 * @param int $end_data Unix timestamp representing now + 30 days in seconds.
186
+		 * @return int Unix timestamp
187
+		 */
188
+		$end_date       = apply_filters(
189
+			'FHEE__EEM_Datetime__create_new_blank_datetime__end_date',
190
+			$this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS
191
+		);
192
+		$blank_datetime = EE_Datetime::new_instance(
193
+			[
194
+				'DTT_EVT_start' => $start_date,
195
+				'DTT_EVT_end'   => $end_date,
196
+				'DTT_order'     => 1,
197
+				'DTT_reg_limit' => EE_INF,
198
+			],
199
+			$timezone_string
200
+		);
201
+		/**
202
+		 * Filters the initial start time and format for the new EE_Datetime instance.
203
+		 *
204
+		 * @param array $start_time An array having size 2.  First element is the time, second element is the time
205
+		 *                          format.
206
+		 * @return array
207
+		 */
208
+		$start_time = apply_filters(
209
+			'FHEE__EEM_Datetime__create_new_blank_datetime__start_time',
210
+			['8am', 'ga']
211
+		);
212
+		/**
213
+		 * Filters the initial end time and format for the new EE_Datetime instance.
214
+		 *
215
+		 * @param array $end_time An array having size 2.  First element is the time, second element is the time
216
+		 *                        format
217
+		 * @return array
218
+		 */
219
+		$end_time = apply_filters(
220
+			'FHEE__EEM_Datetime__create_new_blank_datetime__end_time',
221
+			['5pm', 'ga']
222
+		);
223
+		$this->validateStartAndEndTimeForBlankDate($start_time, $end_time);
224
+		$blank_datetime->set_start_time(
225
+			$this->convert_datetime_for_query(
226
+				'DTT_EVT_start',
227
+				$start_time[0],
228
+				$start_time[1],
229
+				$timezone_string
230
+			)
231
+		);
232
+		$blank_datetime->set_end_time(
233
+			$this->convert_datetime_for_query(
234
+				'DTT_EVT_end',
235
+				$end_time[0],
236
+				$end_time[1],
237
+				$timezone_string
238
+			)
239
+		);
240
+		return [$blank_datetime];
241
+	}
242
+
243
+
244
+	/**
245
+	 * Validates whether the start_time and end_time are in the expected format.
246
+	 *
247
+	 * @param array $start_time
248
+	 * @param array $end_time
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidDataTypeException
251
+	 */
252
+	private function validateStartAndEndTimeForBlankDate(array $start_time, array $end_time)
253
+	{
254
+		if (! is_array($start_time)) {
255
+			throw new InvalidDataTypeException('start_time', $start_time, 'array');
256
+		}
257
+		if (! is_array($end_time)) {
258
+			throw new InvalidDataTypeException('end_time', $end_time, 'array');
259
+		}
260
+		if (count($start_time) !== 2) {
261
+			throw new InvalidArgumentException(
262
+				sprintf(
263
+					'The variable %1$s is expected to be an array with two elements.  The first item in the '
264
+					. 'array should be a valid time string, the second item in the array should be a valid time format',
265
+					'$start_time'
266
+				)
267
+			);
268
+		}
269
+		if (count($end_time) !== 2) {
270
+			throw new InvalidArgumentException(
271
+				sprintf(
272
+					'The variable %1$s is expected to be an array with two elements.  The first item in the '
273
+					. 'array should be a valid time string, the second item in the array should be a valid time format',
274
+					'$end_time'
275
+				)
276
+			);
277
+		}
278
+	}
279
+
280
+
281
+	/**
282
+	 * get event start date from db
283
+	 *
284
+	 * @access public
285
+	 * @param int $EVT_ID
286
+	 * @return EE_Datetime[] array on success, FALSE on fail
287
+	 * @throws EE_Error
288
+	 * @throws ReflectionException
289
+	 */
290
+	public function get_all_event_dates($EVT_ID = 0)
291
+	{
292
+		if (! $EVT_ID) { // on add_new_event event_id gets set to 0
293
+			return $this->create_new_blank_datetime();
294
+		}
295
+		$results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
296
+		if (empty($results)) {
297
+			return $this->create_new_blank_datetime();
298
+		}
299
+		return $results;
300
+	}
301
+
302
+
303
+	/**
304
+	 * get all datetimes attached to an event ordered by the DTT_order field
305
+	 *
306
+	 * @public
307
+	 * @param int     $EVT_ID     event id
308
+	 * @param boolean $include_expired
309
+	 * @param boolean $include_deleted
310
+	 * @param int     $limit      If included then limit the count of results by
311
+	 *                            the given number
312
+	 * @return EE_Datetime[]
313
+	 * @throws EE_Error
314
+	 */
315
+	public function get_datetimes_for_event_ordered_by_DTT_order(
316
+		int $EVT_ID,
317
+		bool $include_expired = true,
318
+		bool $include_deleted = true,
319
+		$limit = 0
320
+	) {
321
+		$prev_data_prep_value = $this->prepModelForQuery();
322
+		$where_params         = ['Event.EVT_ID' => absint($EVT_ID)];
323
+		$query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
324
+		$query_params         = $this->addDefaultWhereConditions($query_params);
325
+		$query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
326
+		return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
327
+	}
328
+
329
+
330
+	/**
331
+	 * Gets the datetimes for the event (with the given limit), and orders them by "importance".
332
+	 * By importance, we mean that the primary datetimes are most important (DEPRECATED FOR NOW),
333
+	 * and then the earlier datetimes are the most important.
334
+	 * Maybe we'll want this to take into account datetimes that haven't already passed, but we don't yet.
335
+	 *
336
+	 * @param int $EVT_ID
337
+	 * @param int $limit
338
+	 * @return EE_Datetime[]|EE_Base_Class[]
339
+	 * @throws EE_Error
340
+	 */
341
+	public function get_datetimes_for_event_ordered_by_importance(int $EVT_ID, $limit = 0)
342
+	{
343
+		$query_params[0] = ['Event.EVT_ID' => absint($EVT_ID)];
344
+		$query_params    = $this->addDefaultWhereConditions($query_params);
345
+		$query_params    = $this->addDefaultQueryParams($query_params, $limit);
346
+		return $this->get_all($query_params);
347
+	}
348
+
349
+
350
+	/**
351
+	 * @param int     $EVT_ID
352
+	 * @param boolean $include_expired
353
+	 * @param boolean $include_deleted
354
+	 * @return EE_Datetime
355
+	 * @throws EE_Error
356
+	 */
357
+	public function get_oldest_datetime_for_event(
358
+		int $EVT_ID,
359
+		bool $include_expired = false,
360
+		bool $include_deleted = false
361
+	) {
362
+		$results = $this->get_datetimes_for_event_ordered_by_start_time(
363
+			$EVT_ID,
364
+			$include_expired,
365
+			$include_deleted,
366
+			1
367
+		);
368
+		if ($results) {
369
+			return array_shift($results);
370
+		}
371
+		return null;
372
+	}
373
+
374
+
375
+	/**
376
+	 * Gets the 'primary' datetime for an event.
377
+	 *
378
+	 * @param int  $EVT_ID
379
+	 * @param bool $try_to_exclude_expired
380
+	 * @param bool $try_to_exclude_deleted
381
+	 * @return EE_Datetime
382
+	 * @throws EE_Error
383
+	 */
384
+	public function get_primary_datetime_for_event(
385
+		int $EVT_ID,
386
+		bool $try_to_exclude_expired = true,
387
+		bool $try_to_exclude_deleted = true
388
+	) {
389
+		if ($try_to_exclude_expired) {
390
+			$non_expired = $this->get_oldest_datetime_for_event($EVT_ID, false, false);
391
+			if ($non_expired) {
392
+				return $non_expired;
393
+			}
394
+		}
395
+		if ($try_to_exclude_deleted) {
396
+			$expired_even = $this->get_oldest_datetime_for_event($EVT_ID, true);
397
+			if ($expired_even) {
398
+				return $expired_even;
399
+			}
400
+		}
401
+		return $this->get_oldest_datetime_for_event($EVT_ID, true, true);
402
+	}
403
+
404
+
405
+	/**
406
+	 * Gets ALL the datetimes for an event (including trashed ones, for now), ordered
407
+	 * only by start date
408
+	 *
409
+	 * @param int     $EVT_ID
410
+	 * @param boolean $include_expired
411
+	 * @param boolean $include_deleted
412
+	 * @param int     $limit
413
+	 * @return EE_Datetime[]
414
+	 * @throws EE_Error
415
+	 */
416
+	public function get_datetimes_for_event_ordered_by_start_time(
417
+		int $EVT_ID,
418
+		bool $include_expired = true,
419
+		bool $include_deleted = true,
420
+		$limit = 0
421
+	) {
422
+		$prev_data_prep_value = $this->prepModelForQuery();
423
+		$where_params         = ['Event.EVT_ID' => absint($EVT_ID)];
424
+		$query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
425
+		$query_params         = $this->addDefaultWhereConditions(
426
+			$query_params,
427
+			EEM_Base::default_where_conditions_this_only
428
+		);
429
+		$query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
430
+		return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
431
+	}
432
+
433
+
434
+	/**
435
+	 * Gets ALL the datetimes for an ticket (including trashed ones, for now), ordered
436
+	 * only by start date
437
+	 *
438
+	 * @param int     $TKT_ID
439
+	 * @param boolean $include_expired
440
+	 * @param boolean $include_deleted
441
+	 * @param int     $limit
442
+	 * @return EE_Datetime[]
443
+	 * @throws EE_Error
444
+	 */
445
+	public function get_datetimes_for_ticket_ordered_by_start_time(
446
+		int $TKT_ID,
447
+		bool $include_expired = true,
448
+		bool $include_deleted = true,
449
+		$limit = 0
450
+	) {
451
+		$prev_data_prep_value = $this->prepModelForQuery();
452
+		$where_params         = ['Ticket.TKT_ID' => absint($TKT_ID)];
453
+		$query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
454
+		$query_params         = $this->addDefaultQueryParams($query_params, $limit);
455
+		return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
456
+	}
457
+
458
+
459
+	/**
460
+	 * Gets all the datetimes for a ticket (including trashed ones, for now), ordered by the DTT_order for the
461
+	 * datetimes.
462
+	 *
463
+	 * @param int      $TKT_ID           ID of ticket to retrieve the datetimes for
464
+	 * @param boolean  $include_expired  whether to include expired datetimes or not
465
+	 * @param boolean  $include_deleted  whether to include trashed datetimes or not.
466
+	 * @param int|null $limit            if null, no limit, if int then limit results by
467
+	 *                                   that number
468
+	 * @return EE_Datetime[]
469
+	 * @throws EE_Error
470
+	 */
471
+	public function get_datetimes_for_ticket_ordered_by_DTT_order(
472
+		int $TKT_ID,
473
+		bool $include_expired = true,
474
+		bool $include_deleted = true,
475
+		$limit = 0
476
+	) {
477
+		$prev_data_prep_value = $this->prepModelForQuery();
478
+		$where_params         = ['Ticket.TKT_ID' => absint($TKT_ID)];
479
+		$query_params[0]      = $this->addDefaultWhereParams($where_params, $include_deleted, $include_expired);
480
+		$query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
481
+		return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
482
+	}
483
+
484
+
485
+	/**
486
+	 * Gets the most important datetime for a particular event (ie, the primary event usually. But if for some WACK
487
+	 * reason it doesn't exist, we consider the earliest event the most important)
488
+	 *
489
+	 * @param int $EVT_ID
490
+	 * @return EE_Datetime
491
+	 * @throws EE_Error
492
+	 */
493
+	public function get_most_important_datetime_for_event(int $EVT_ID)
494
+	{
495
+		$results = $this->get_datetimes_for_event_ordered_by_importance($EVT_ID, 1);
496
+		if ($results) {
497
+			return array_shift($results);
498
+		}
499
+		return null;
500
+	}
501
+
502
+
503
+	/**
504
+	 * This returns a wpdb->results        Array of all DTT month and years matching the incoming query params and
505
+	 * grouped by month and year.
506
+	 *
507
+	 * @param array  $where_params       @see
508
+	 *                                   https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
509
+	 * @param string $evt_active_status  A string representing the evt active status to filter the months by.
510
+	 *                                   Can be:
511
+	 *                                   - '' = no filter
512
+	 *                                   - upcoming = Published events with at least one upcoming datetime.
513
+	 *                                   - expired = Events with all datetimes expired.
514
+	 *                                   - active = Events that are published and have at least one datetime that
515
+	 *                                   starts before now and ends after now.
516
+	 *                                   - inactive = Events that are either not published.
517
+	 * @return EE_Base_Class[]
518
+	 * @throws EE_Error
519
+	 * @throws InvalidArgumentException
520
+	 * @throws InvalidArgumentException
521
+	 */
522
+	public function get_dtt_months_and_years(array $where_params, $evt_active_status = '')
523
+	{
524
+		$current_time_for_DTT_EVT_start = $this->current_time_for_query('DTT_EVT_start');
525
+		$current_time_for_DTT_EVT_end   = $this->current_time_for_query('DTT_EVT_end');
526
+		switch ($evt_active_status) {
527
+			case 'upcoming':
528
+				$where_params['Event.status'] = 'publish';
529
+				// if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
530
+				if (isset($where_params['DTT_EVT_start'])) {
531
+					$where_params['DTT_EVT_start*****'] = $where_params['DTT_EVT_start'];
532
+				}
533
+				$where_params['DTT_EVT_start'] = ['>', $current_time_for_DTT_EVT_start];
534
+				break;
535
+			case 'expired':
536
+				if (isset($where_params['Event.status'])) {
537
+					unset($where_params['Event.status']);
538
+				}
539
+				// get events to exclude
540
+				$exclude_query[0] = array_merge(
541
+					$where_params,
542
+					['DTT_EVT_end' => ['>', $current_time_for_DTT_EVT_end]]
543
+				);
544
+				// first get all events that have datetimes where its not expired.
545
+				$event_ids = $this->_get_all_wpdb_results(
546
+					$exclude_query,
547
+					OBJECT_K,
548
+					'Datetime.EVT_ID'
549
+				);
550
+				$event_ids = array_keys($event_ids);
551
+				if (isset($where_params['DTT_EVT_end'])) {
552
+					$where_params['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
553
+				}
554
+				$where_params['DTT_EVT_end']  = ['<', $current_time_for_DTT_EVT_end];
555
+				$where_params['Event.EVT_ID'] = ['NOT IN', $event_ids];
556
+				break;
557
+			case 'active':
558
+				$where_params['Event.status'] = 'publish';
559
+				if (isset($where_params['DTT_EVT_start'])) {
560
+					$where_params['Datetime.DTT_EVT_start******'] = $where_params['DTT_EVT_start'];
561
+				}
562
+				if (isset($where_params['Datetime.DTT_EVT_end'])) {
563
+					$where_params['Datetime.DTT_EVT_end*****'] = $where_params['DTT_EVT_end'];
564
+				}
565
+				$where_params['DTT_EVT_start'] = ['<', $current_time_for_DTT_EVT_start];
566
+				$where_params['DTT_EVT_end']   = ['>', $current_time_for_DTT_EVT_end];
567
+				break;
568
+			case 'inactive':
569
+				if (isset($where_params['Event.status'])) {
570
+					unset($where_params['Event.status']);
571
+				}
572
+				if (isset($where_params['OR'])) {
573
+					$where_params['AND']['OR'] = $where_params['OR'];
574
+				}
575
+				if (isset($where_params['DTT_EVT_end'])) {
576
+					$where_params['AND']['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
577
+					unset($where_params['DTT_EVT_end']);
578
+				}
579
+				if (isset($where_params['DTT_EVT_start'])) {
580
+					$where_params['AND']['DTT_EVT_start'] = $where_params['DTT_EVT_start'];
581
+					unset($where_params['DTT_EVT_start']);
582
+				}
583
+				$where_params['AND']['Event.status'] = ['!=', 'publish'];
584
+				break;
585
+		}
586
+		$query_params[0]          = $where_params;
587
+		$query_params['group_by'] = ['dtt_year', 'dtt_month'];
588
+		$query_params             = $this->addOrderByQueryParams($query_params, 'DTT_EVT_start', 'DESC');
589
+
590
+		$query_interval    = EEH_DTT_Helper::get_sql_query_interval_for_offset(
591
+			$this->get_timezone(),
592
+			'DTT_EVT_start'
593
+		);
594
+		$columns_to_select = [
595
+			'dtt_year'      => ['YEAR(' . $query_interval . ')', '%s'],
596
+			'dtt_month'     => ['MONTHNAME(' . $query_interval . ')', '%s'],
597
+			'dtt_month_num' => ['MONTH(' . $query_interval . ')', '%s'],
598
+		];
599
+		return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
600
+	}
601
+
602
+
603
+	/**
604
+	 * Updates the DTT_sold attribute on each datetime (based on the registrations
605
+	 * for the tickets for each datetime)
606
+	 *
607
+	 * @param EE_Base_Class[]|EE_Datetime[] $datetimes
608
+	 * @throws EE_Error
609
+	 * @throws ReflectionException
610
+	 */
611
+	public function update_sold(array $datetimes)
612
+	{
613
+		EE_Error::doing_it_wrong(
614
+			__FUNCTION__,
615
+			esc_html__(
616
+				'Please use \EEM_Ticket::update_tickets_sold() instead which will in turn correctly update both the Ticket AND Datetime counts.',
617
+				'event_espresso'
618
+			),
619
+			'4.9.32.rc.005'
620
+		);
621
+		foreach ($datetimes as $datetime) {
622
+			$datetime->update_sold();
623
+		}
624
+	}
625
+
626
+
627
+	/**
628
+	 *    Gets the total number of tickets available at a particular datetime
629
+	 *    (does NOT take into account the datetime's spaces available)
630
+	 *
631
+	 * @param int   $DTT_ID
632
+	 * @param array $query_params
633
+	 * @return int of tickets available. If sold out, return less than 1. If infinite, returns EE_INF,  IF there are NO
634
+	 *             tickets attached to datetime then FALSE is returned.
635
+	 * @throws EE_Error
636
+	 * @throws ReflectionException
637
+	 */
638
+	public function sum_tickets_currently_available_at_datetime(int $DTT_ID, array $query_params = [])
639
+	{
640
+		$datetime = $this->get_one_by_ID($DTT_ID);
641
+		if ($datetime instanceof EE_Datetime) {
642
+			return $datetime->tickets_remaining($query_params);
643
+		}
644
+		return 0;
645
+	}
646
+
647
+
648
+	/**
649
+	 * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
650
+	 *
651
+	 * @param array $stati_to_include  If included you can restrict the statuses we return counts for by including the
652
+	 *                                 stati you want counts for as values in the array.  An empty array returns counts
653
+	 *                                 for all valid stati.
654
+	 * @param array $query_params      If included can be used to refine the conditions for returning the count (i.e.
655
+	 *                                 only for Datetimes connected to a specific event, or specific ticket.
656
+	 * @return array  The value returned is an array indexed by Datetime Status and the values are the counts.  The
657
+	 * @throws EE_Error
658
+	 *                                 stati used as index keys are: EE_Datetime::active EE_Datetime::upcoming
659
+	 *                                 EE_Datetime::expired
660
+	 */
661
+	public function get_datetime_counts_by_status(array $stati_to_include = [], array $query_params = [])
662
+	{
663
+		// only accept where conditions for this query.
664
+		$_where            = isset($query_params[0]) ? $query_params[0] : [];
665
+		$status_query_args = [
666
+			EE_Datetime::active   => array_merge(
667
+				$_where,
668
+				['DTT_EVT_start' => ['<', time()], 'DTT_EVT_end' => ['>', time()]]
669
+			),
670
+			EE_Datetime::upcoming => array_merge(
671
+				$_where,
672
+				['DTT_EVT_start' => ['>', time()]]
673
+			),
674
+			EE_Datetime::expired  => array_merge(
675
+				$_where,
676
+				['DTT_EVT_end' => ['<', time()]]
677
+			),
678
+		];
679
+		if (! empty($stati_to_include)) {
680
+			foreach (array_keys($status_query_args) as $status) {
681
+				if (! in_array($status, $stati_to_include, true)) {
682
+					unset($status_query_args[ $status ]);
683
+				}
684
+			}
685
+		}
686
+		// loop through and query counts for each stati.
687
+		$status_query_results = [];
688
+		foreach ($status_query_args as $status => $status_where_conditions) {
689
+			$status_query_results[ $status ] = EEM_Datetime::count(
690
+				[$status_where_conditions],
691
+				'DTT_ID',
692
+				true
693
+			);
694
+		}
695
+		return $status_query_results;
696
+	}
697
+
698
+
699
+	/**
700
+	 * Returns the specific count for a given Datetime status matching any given query_params.
701
+	 *
702
+	 * @param string $status Valid string representation for Datetime status requested. (Defaults to Active).
703
+	 * @param array  $query_params
704
+	 * @return int
705
+	 * @throws EE_Error
706
+	 */
707
+	public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = [])
708
+	{
709
+		$count = $this->get_datetime_counts_by_status([$status], $query_params);
710
+		return ! empty($count[ $status ]) ? $count[ $status ] : 0;
711
+	}
712
+
713
+
714
+	/**
715
+	 * @return bool|int
716
+	 * @since   $VID:$
717
+	 */
718
+	private function prepModelForQuery()
719
+	{
720
+		$prev_data_prep_value = $this->get_assumption_concerning_values_already_prepared_by_model_object();
721
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
722
+		return $prev_data_prep_value;
723
+	}
724
+
725
+
726
+	/**
727
+	 * @param array    $query_params
728
+	 * @param bool|int $prev_data_prep_value
729
+	 * @return EE_Base_Class[]|EE_Datetime[]
730
+	 * @throws EE_Error
731
+	 * @since   $VID:$
732
+	 */
733
+	private function getDatetimesAndRestoreModel(array $query_params, $prev_data_prep_value)
734
+	{
735
+		$result = $this->get_all($query_params);
736
+		$this->assume_values_already_prepared_by_model_object($prev_data_prep_value);
737
+		return $result;
738
+	}
739
+
740
+
741
+	/**
742
+	 * @param array  $query_params
743
+	 * @param int    $limit
744
+	 * @param string $order_by
745
+	 * @param string $order
746
+	 * @return array
747
+	 * @since   $VID:$
748
+	 */
749
+	private function addDefaultQueryParams(array $query_params, $limit = 0, $order_by = 'DTT_EVT_start', $order = 'ASC')
750
+	{
751
+		$query_params = $this->addOrderByQueryParams($query_params, $order_by, $order);
752
+		$query_params = $this->addLimitQueryParams($query_params, $limit);
753
+		return $query_params;
754
+	}
755
+
756
+
757
+	/**
758
+	 * @param array  $query_params
759
+	 * @param string $default_where_conditions
760
+	 * @return array
761
+	 * @since   $VID:$
762
+	 */
763
+	private function addDefaultWhereConditions(
764
+		array $query_params,
765
+		$default_where_conditions = EEM_Base::default_where_conditions_none
766
+	) {
767
+		$query_params['default_where_conditions'] = $default_where_conditions;
768
+		return $query_params;
769
+	}
770
+
771
+
772
+	/**
773
+	 * @param array $where_params
774
+	 * @param bool  $include_deleted
775
+	 * @param bool  $include_expired
776
+	 * @return array
777
+	 * @since   $VID:$
778
+	 */
779
+	private function addDefaultWhereParams(array $where_params, bool $include_deleted = true, bool $include_expired = true)
780
+	{
781
+		$where_params = $this->addExpiredWhereParams($where_params, $include_expired);
782
+		$where_params = $this->addDeletedWhereParams($where_params, $include_deleted);
783
+		return $where_params;
784
+	}
785
+
786
+
787
+	/**
788
+	 * @param array $where_params
789
+	 * @param bool  $include_deleted
790
+	 * @return array
791
+	 * @since   $VID:$
792
+	 */
793
+	private function addDeletedWhereParams(array $where_params, bool $include_deleted = true)
794
+	{
795
+		$deleted                     = $include_deleted ? [true, false] : [false];
796
+		$where_params['DTT_deleted'] = ['IN', $deleted];
797
+		return $where_params;
798
+	}
799
+
800
+
801
+	/**
802
+	 * @param array $where_params
803
+	 * @param bool  $include_expired
804
+	 * @return array
805
+	 * @since   $VID:$
806
+	 */
807
+	private function addExpiredWhereParams(array $where_params, bool $include_expired = true)
808
+	{
809
+		if (! $include_expired) {
810
+			$where_params['DTT_EVT_end'] = ['>=', current_time('mysql', true)];
811
+		}
812
+		return $where_params;
813
+	}
814
+
815
+
816
+	/**
817
+	 * @param array $query_params
818
+	 * @param int   $limit
819
+	 * @return array
820
+	 * @since   $VID:$
821
+	 */
822
+	private function addLimitQueryParams(array $query_params, $limit = 0)
823
+	{
824
+		if ($limit) {
825
+			$query_params['limit'] = $limit;
826
+		}
827
+		return $query_params;
828
+	}
829
+
830
+
831
+	/**
832
+	 * @param array  $query_params
833
+	 * @param string $order_by
834
+	 * @param string $order
835
+	 * @return array
836
+	 * @since   $VID:$
837
+	 */
838
+	private function addOrderByQueryParams(array $query_params, $order_by = 'DTT_EVT_start', $order = 'ASC')
839
+	{
840
+		$order                    = $order === 'ASC' ? 'ASC' : 'DESC';
841
+		$valid_order_columns      = ['DTT_ID', 'DTT_EVT_start', 'DTT_EVT_end', 'DTT_order'];
842
+		$order_by                 = in_array($order_by, $valid_order_columns, true) ? $order_by : 'DTT_EVT_start';
843
+		$query_params['order_by'] = [$order_by => $order];
844
+		return $query_params;
845
+	}
846 846
 }
Please login to merge, or discard this patch.
Spacing   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
                 ),
122 122
             ],
123 123
         ];
124
-        $this->_model_relations        = [
124
+        $this->_model_relations = [
125 125
             'Ticket'          => new EE_HABTM_Relation('Datetime_Ticket'),
126 126
             'Event'           => new EE_Belongs_To_Relation(),
127 127
             'Checkin'         => new EE_Has_Many_Relation(),
@@ -131,19 +131,19 @@  discard block
 block discarded – undo
131 131
         $this->model_chain_to_password = $path_to_event_model;
132 132
         $this->_model_chain_to_wp_user = $path_to_event_model;
133 133
         // this model is generally available for reading
134
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       =
134
+        $this->_cap_restriction_generators[EEM_Base::caps_read] =
135 135
             new EE_Restriction_Generator_Event_Related_Public(
136 136
                 $path_to_event_model
137 137
             );
138
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
138
+        $this->_cap_restriction_generators[EEM_Base::caps_read_admin] =
139 139
             new EE_Restriction_Generator_Event_Related_Protected(
140 140
                 $path_to_event_model
141 141
             );
142
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       =
142
+        $this->_cap_restriction_generators[EEM_Base::caps_edit] =
143 143
             new EE_Restriction_Generator_Event_Related_Protected(
144 144
                 $path_to_event_model
145 145
             );
146
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     =
146
+        $this->_cap_restriction_generators[EEM_Base::caps_delete] =
147 147
             new EE_Restriction_Generator_Event_Related_Protected(
148 148
                 $path_to_event_model,
149 149
                 EEM_Base::caps_edit
@@ -185,7 +185,7 @@  discard block
 block discarded – undo
185 185
          * @param int $end_data Unix timestamp representing now + 30 days in seconds.
186 186
          * @return int Unix timestamp
187 187
          */
188
-        $end_date       = apply_filters(
188
+        $end_date = apply_filters(
189 189
             'FHEE__EEM_Datetime__create_new_blank_datetime__end_date',
190 190
             $this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS
191 191
         );
@@ -251,10 +251,10 @@  discard block
 block discarded – undo
251 251
      */
252 252
     private function validateStartAndEndTimeForBlankDate(array $start_time, array $end_time)
253 253
     {
254
-        if (! is_array($start_time)) {
254
+        if ( ! is_array($start_time)) {
255 255
             throw new InvalidDataTypeException('start_time', $start_time, 'array');
256 256
         }
257
-        if (! is_array($end_time)) {
257
+        if ( ! is_array($end_time)) {
258 258
             throw new InvalidDataTypeException('end_time', $end_time, 'array');
259 259
         }
260 260
         if (count($start_time) !== 2) {
@@ -289,7 +289,7 @@  discard block
 block discarded – undo
289 289
      */
290 290
     public function get_all_event_dates($EVT_ID = 0)
291 291
     {
292
-        if (! $EVT_ID) { // on add_new_event event_id gets set to 0
292
+        if ( ! $EVT_ID) { // on add_new_event event_id gets set to 0
293 293
             return $this->create_new_blank_datetime();
294 294
         }
295 295
         $results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
@@ -426,7 +426,7 @@  discard block
 block discarded – undo
426 426
             $query_params,
427 427
             EEM_Base::default_where_conditions_this_only
428 428
         );
429
-        $query_params         = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
429
+        $query_params = $this->addDefaultQueryParams($query_params, $limit, 'DTT_order');
430 430
         return $this->getDatetimesAndRestoreModel($query_params, $prev_data_prep_value);
431 431
     }
432 432
 
@@ -587,14 +587,14 @@  discard block
 block discarded – undo
587 587
         $query_params['group_by'] = ['dtt_year', 'dtt_month'];
588 588
         $query_params             = $this->addOrderByQueryParams($query_params, 'DTT_EVT_start', 'DESC');
589 589
 
590
-        $query_interval    = EEH_DTT_Helper::get_sql_query_interval_for_offset(
590
+        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset(
591 591
             $this->get_timezone(),
592 592
             'DTT_EVT_start'
593 593
         );
594 594
         $columns_to_select = [
595
-            'dtt_year'      => ['YEAR(' . $query_interval . ')', '%s'],
596
-            'dtt_month'     => ['MONTHNAME(' . $query_interval . ')', '%s'],
597
-            'dtt_month_num' => ['MONTH(' . $query_interval . ')', '%s'],
595
+            'dtt_year'      => ['YEAR('.$query_interval.')', '%s'],
596
+            'dtt_month'     => ['MONTHNAME('.$query_interval.')', '%s'],
597
+            'dtt_month_num' => ['MONTH('.$query_interval.')', '%s'],
598 598
         ];
599 599
         return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
600 600
     }
@@ -676,17 +676,17 @@  discard block
 block discarded – undo
676 676
                 ['DTT_EVT_end' => ['<', time()]]
677 677
             ),
678 678
         ];
679
-        if (! empty($stati_to_include)) {
679
+        if ( ! empty($stati_to_include)) {
680 680
             foreach (array_keys($status_query_args) as $status) {
681
-                if (! in_array($status, $stati_to_include, true)) {
682
-                    unset($status_query_args[ $status ]);
681
+                if ( ! in_array($status, $stati_to_include, true)) {
682
+                    unset($status_query_args[$status]);
683 683
                 }
684 684
             }
685 685
         }
686 686
         // loop through and query counts for each stati.
687 687
         $status_query_results = [];
688 688
         foreach ($status_query_args as $status => $status_where_conditions) {
689
-            $status_query_results[ $status ] = EEM_Datetime::count(
689
+            $status_query_results[$status] = EEM_Datetime::count(
690 690
                 [$status_where_conditions],
691 691
                 'DTT_ID',
692 692
                 true
@@ -707,7 +707,7 @@  discard block
 block discarded – undo
707 707
     public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = [])
708 708
     {
709 709
         $count = $this->get_datetime_counts_by_status([$status], $query_params);
710
-        return ! empty($count[ $status ]) ? $count[ $status ] : 0;
710
+        return ! empty($count[$status]) ? $count[$status] : 0;
711 711
     }
712 712
 
713 713
 
@@ -806,7 +806,7 @@  discard block
 block discarded – undo
806 806
      */
807 807
     private function addExpiredWhereParams(array $where_params, bool $include_expired = true)
808 808
     {
809
-        if (! $include_expired) {
809
+        if ( ! $include_expired) {
810 810
             $where_params['DTT_EVT_end'] = ['>=', current_time('mysql', true)];
811 811
         }
812 812
         return $where_params;
Please login to merge, or discard this patch.
core/helpers/EEH_File.helper.php 2 patches
Indentation   +901 added lines, -901 removed lines patch added patch discarded remove patch
@@ -25,905 +25,905 @@
 block discarded – undo
25 25
 class EEH_File extends EEH_Base implements EEHI_File
26 26
 {
27 27
 
28
-    /**
29
-     * @var string $_credentials_form
30
-     */
31
-    private static $_credentials_form;
32
-
33
-    /**
34
-     * @var WP_Filesystem_Base $_wp_filesystem
35
-     */
36
-    protected static $_wp_filesystem;
37
-
38
-
39
-    /**
40
-     * @param string|null $filepath the filepath we want to work in. If its in the
41
-     *                              wp uploads directory, we'll want to just use the filesystem directly.
42
-     *                              If not provided, we have to assume its not in the uploads directory
43
-     * @return WP_Filesystem_Base
44
-     */
45
-    private static function _get_wp_filesystem($filepath = null)
46
-    {
47
-        if (apply_filters(
48
-            'FHEE__EEH_File___get_wp_filesystem__allow_using_filesystem_direct',
49
-            $filepath && EEH_File::is_in_uploads_folder($filepath),
50
-            $filepath
51
-        )) {
52
-            return EEH_File::loadAlternateWpFileSystem();
53
-        }
54
-        return EEH_File::loadWpFileSystem();
55
-    }
56
-
57
-
58
-    /**
59
-     * @return WP_Filesystem_Base
60
-     */
61
-    private static function loadAlternateWpFileSystem()
62
-    {
63
-        if (! EEH_File::$_wp_filesystem instanceof WP_Filesystem_Base) {
64
-            require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
65
-            $method             = 'direct';
66
-            $wp_filesystem_file =
67
-                apply_filters(
68
-                    'filesystem_method_file',
69
-                    ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php',
70
-                    $method
71
-                );
72
-            // added the following validation logic
73
-            // because we allow the filesystem filepath to be filtered,
74
-            // and are loading whatever file the path pointed to,
75
-            // but we were not validating things in any way :scream_emoji:
76
-            $valid_wp_filesystem_types = [
77
-                'direct'     => 'WP_Filesystem_Direct',
78
-                'ftpext'     => 'WP_Filesystem_FTPext',
79
-                'ftpsockets' => 'WP_Filesystem_ftpsockets',
80
-                'ssh2'       => 'WP_Filesystem_SSH2',
81
-            ];
82
-            $valid                     = false;
83
-            $wp_filesystem_class       = '';
84
-            foreach ($valid_wp_filesystem_types as $method => $filesystem_class) {
85
-                // if file path matches for one of valid types, then toggle $valid to true
86
-                if (strpos($wp_filesystem_file, $method) > 0) {
87
-                    $valid               = true;
88
-                    $wp_filesystem_class = $filesystem_class;
89
-                }
90
-            }
91
-            if (! $valid || ! file_exists($wp_filesystem_file)) {
92
-                EE_Error::add_error(
93
-                    sprintf(
94
-                        __(
95
-                            'The supplied WP Filesystem filepath "%1$s" is either missing or invalid.',
96
-                            'event_espresso'
97
-                        ),
98
-                        $wp_filesystem_file
99
-                    ),
100
-                    __FILE__,
101
-                    __FUNCTION__,
102
-                    __LINE__
103
-                );
104
-            }
105
-            // check constants defined, just like in the wp-admin/includes/file.php WP_Filesystem()
106
-            if (! defined('FS_CHMOD_DIR')) {
107
-                define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0775 | 0755));
108
-            }
109
-            if (! defined('FS_CHMOD_FILE')) {
110
-                define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0775 | 0644));
111
-            }
112
-            require_once($wp_filesystem_file);
113
-            EEH_File::$_wp_filesystem = new $wp_filesystem_class([]);
114
-        }
115
-        return EEH_File::$_wp_filesystem;
116
-    }
117
-
118
-
119
-    /**
120
-     * @return WP_Filesystem_Base
121
-     */
122
-    private static function loadWpFileSystem()
123
-    {
124
-        global $wp_filesystem;
125
-        // no filesystem setup ???
126
-        if (! $wp_filesystem instanceof WP_Filesystem_Base) {
127
-            // if some eager beaver's just trying to get in there too early...
128
-            // let them do it, because we are one of those eager beavers! :P
129
-            /**
130
-             * more explanations are probably merited. http://codex.wordpress.org/Filesystem_API#Initializing_WP_Filesystem_Base
131
-             * says WP_Filesystem should be used after 'wp_loaded', but currently EE's activation process
132
-             * is setup to mostly happen on 'init', and refactoring to have it happen on
133
-             * 'wp_loaded' is too much work on a BETA milestone.
134
-             * So this fix is expected to work if the WP files are owned by the server user,
135
-             * but probably not if the user needs to enter their FTP credentials to modify files
136
-             * and there may be troubles if the WP files are owned by a different user
137
-             * than the server user. But both of these issues should exist in 4.4 and earlier too
138
-             */
139
-            if (false && ! did_action('wp_loaded')) {
140
-                $msg =
141
-                    __(
142
-                        'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.',
143
-                        'event_espresso'
144
-                    );
145
-                if (WP_DEBUG) {
146
-                    $msg .= '<br />' . __(
147
-                        'The WP Filesystem can not be accessed until after the "wp_loaded" hook has run, so it\'s best not to attempt access until the "admin_init" hookpoint.',
148
-                        'event_espresso'
149
-                    );
150
-                }
151
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
152
-            }
153
-            // should be loaded if we are past the wp_loaded hook...
154
-            if (! function_exists('WP_Filesystem') || ! function_exists('submit_button')) {
155
-                require_once(ABSPATH . 'wp-admin/includes/file.php');
156
-                require_once(ABSPATH . 'wp-admin/includes/template.php');
157
-            }
158
-            // turn on output buffering so that we can capture the credentials form
159
-            ob_start();
160
-            $credentials = request_filesystem_credentials(false);
161
-            // store credentials form for the time being
162
-            EEH_File::$_credentials_form = ob_get_clean();
163
-            // if credentials do NOT exist
164
-            if ($credentials === false) {
165
-                add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999);
166
-                EE_Error::add_error(
167
-                    __(
168
-                        'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.',
169
-                        'event_espresso'
170
-                    ),
171
-                    __FILE__,
172
-                    __FUNCTION__,
173
-                    __LINE__
174
-                );
175
-            }
176
-            // basically check for direct or previously configured access
177
-            if (! WP_Filesystem($credentials)
178
-                && is_wp_error($wp_filesystem->errors)
179
-                && $wp_filesystem->errors->get_error_code()
180
-            ) {
181
-                add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999);
182
-                EE_Error::add_error(
183
-                    sprintf(
184
-                        __('WP Filesystem Error: $1%s', 'event_espresso'),
185
-                        $wp_filesystem->errors->get_error_message()
186
-                    ),
187
-                    __FILE__,
188
-                    __FUNCTION__,
189
-                    __LINE__
190
-                );
191
-            }
192
-        }
193
-        return $wp_filesystem;
194
-    }
195
-
196
-
197
-    /**
198
-     * display_request_filesystem_credentials_form
199
-     */
200
-    public static function display_request_filesystem_credentials_form()
201
-    {
202
-        if (! empty(EEH_File::$_credentials_form)) {
203
-            echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>';
204
-        }
205
-    }
206
-
207
-
208
-    /**
209
-     *    verify_filepath_and_permissions
210
-     *    checks that a file is readable and has sufficient file permissions set to access
211
-     *
212
-     * @access public
213
-     * @param string $full_file_path - full server path to the folder or file
214
-     * @param string $file_name      - name of file if checking a file
215
-     * @param string $file_ext       - file extension (ie: "php") if checking a file
216
-     * @param string $type_of_file   - general type of file (ie: "module"), this is only used to improve error messages
217
-     * @return bool
218
-     */
219
-    public static function verify_filepath_and_permissions(
220
-        $full_file_path = '',
221
-        $file_name = '',
222
-        $file_ext = '',
223
-        $type_of_file = ''
224
-    ) {
225
-        // load WP_Filesystem and set file permissions
226
-        $wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
227
-        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
228
-        if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
229
-            $file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name;
230
-            $file_name .= ! empty($file_ext) ? ' file' : ' folder';
231
-            $msg       = sprintf(
232
-                __(
233
-                    'The requested %1$s could not be found or is not readable, possibly due to an incorrect filepath, or incorrect file permissions.%2$s',
234
-                    'event_espresso'
235
-                ),
236
-                $file_name,
237
-                '<br />'
238
-            );
239
-            if (EEH_File::exists($full_file_path)) {
240
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, $type_of_file);
241
-            } else {
242
-                // no file permissions means the file was not found
243
-                $msg .= sprintf(
244
-                    __('Please ensure the following path is correct: "%s".', 'event_espresso'),
245
-                    $full_file_path
246
-                );
247
-            }
248
-            if (defined('WP_DEBUG') && WP_DEBUG) {
249
-                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
250
-            }
251
-            return false;
252
-        }
253
-        return true;
254
-    }
255
-
256
-
257
-    /**
258
-     * _permissions_error_for_unreadable_filepath - attempts to determine why permissions are set incorrectly for a
259
-     * file or folder
260
-     *
261
-     * @access private
262
-     * @param string $full_file_path - full server path to the folder or file
263
-     * @param string $type_of_file   - general type of file (ie: "module"), this is only used to improve error messages
264
-     * @return string
265
-     */
266
-    private static function _permissions_error_for_unreadable_filepath($full_file_path = '', $type_of_file = '')
267
-    {
268
-        // load WP_Filesystem and set file permissions
269
-        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
270
-        // check file permissions
271
-        $perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
272
-        if ($perms) {
273
-            // file permissions exist, but way be set incorrectly
274
-            $type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : '';
275
-            $type_of_file .= ! empty($type_of_file) ? 'file' : 'folder';
276
-            return ' ' . sprintf(
277
-                __(
278
-                    'File permissions for the requested %1$s are currently set at "%2$s". The recommended permissions are 644 for files and 755 for folders.',
279
-                    'event_espresso'
280
-                ),
281
-                $type_of_file,
282
-                $perms
283
-            );
284
-        } else {
285
-            // file exists but file permissions could not be read ?!?!
286
-            return ' ' . sprintf(
287
-                __(
288
-                    'Please ensure that the server and/or PHP configuration allows the current process to access the following file: "%s".',
289
-                    'event_espresso'
290
-                ),
291
-                $full_file_path
292
-            );
293
-        }
294
-    }
295
-
296
-
297
-    /**
298
-     * ensure_folder_exists_and_is_writable
299
-     * ensures that a folder exists and is writable, will attempt to create folder if it does not exist
300
-     * Also ensures all the parent folders exist, and if not tries to create them.
301
-     * Also, if this function creates the folder, adds a .htaccess file and index.html file
302
-     *
303
-     * @param string $folder
304
-     * @return bool false if folder isn't writable; true if it exists and is writeable,
305
-     */
306
-    public static function ensure_folder_exists_and_is_writable($folder = '')
307
-    {
308
-        if (empty($folder)) {
309
-            return false;
310
-        }
311
-        // remove ending /
312
-        $folder        = EEH_File::standardise_directory_separators(rtrim($folder, '/\\'));
313
-        $parent_folder = EEH_File::get_parent_folder($folder);
314
-        // add / to folder
315
-        $folder        = EEH_File::end_with_directory_separator($folder);
316
-        $wp_filesystem = EEH_File::_get_wp_filesystem($folder);
317
-        $remote_dir    = EEH_File::convert_local_filepath_to_remote_filepath($folder);
318
-        if (! $wp_filesystem->is_dir($remote_dir)) {
319
-            // ok so it doesn't exist. Does its parent? Can we write to it?
320
-            if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
321
-                return false;
322
-            }
323
-            if (! EEH_File::verify_is_writable($parent_folder, 'folder')) {
324
-                return false;
325
-            }
326
-            if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) {
327
-                if (defined('WP_DEBUG') && WP_DEBUG) {
328
-                    $msg = sprintf(__('"%s" could not be created.', 'event_espresso'), $folder);
329
-                    $msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder);
330
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
331
-                }
332
-                return false;
333
-            }
334
-            EEH_File::add_index_file($folder);
335
-        } elseif (! EEH_File::verify_is_writable($folder, 'folder')) {
336
-            return false;
337
-        }
338
-        return true;
339
-    }
340
-
341
-
342
-    /**
343
-     * verify_is_writable - checks if a file or folder is writable
344
-     *
345
-     * @param string $full_path      - full server path to file or folder
346
-     * @param string $file_or_folder - whether checking a file or folder
347
-     * @return bool
348
-     */
349
-    public static function verify_is_writable($full_path = '', $file_or_folder = 'folder')
350
-    {
351
-        // load WP_Filesystem and set file permissions
352
-        $wp_filesystem = EEH_File::_get_wp_filesystem($full_path);
353
-        $full_path     = EEH_File::standardise_directory_separators($full_path);
354
-        $remote_path   = EEH_File::convert_local_filepath_to_remote_filepath($full_path);
355
-        $remote_path   = rtrim($remote_path, '/\\');
356
-        if (! $wp_filesystem->is_writable($remote_path)) {
357
-            if (defined('WP_DEBUG') && WP_DEBUG) {
358
-                $msg = sprintf(__('The "%1$s" %2$s is not writable.', 'event_espresso'), $full_path, $file_or_folder);
359
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_path);
360
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
361
-            }
362
-            return false;
363
-        }
364
-        return true;
365
-    }
366
-
367
-
368
-    /**
369
-     * ensure_file_exists_and_is_writable
370
-     * ensures that a file exists and is writable, will attempt to create file if it does not exist.
371
-     * Also ensures all the parent folders exist, and if not tries to create them.
372
-     *
373
-     * @param string $full_file_path
374
-     * @return bool
375
-     */
376
-    public static function ensure_file_exists_and_is_writable($full_file_path = '')
377
-    {
378
-        // load WP_Filesystem and set file permissions
379
-        $wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
380
-        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
381
-        $parent_folder  = EEH_File::get_parent_folder($full_file_path);
382
-        if (! EEH_File::exists($full_file_path)) {
383
-            if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
384
-                return false;
385
-            }
386
-            if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
387
-                if (defined('WP_DEBUG') && WP_DEBUG) {
388
-                    $msg = sprintf(__('The "%s" file could not be created.', 'event_espresso'), $full_file_path);
389
-                    $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
390
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
391
-                }
392
-                return false;
393
-            }
394
-        }
395
-        if (! EEH_File::verify_is_writable($full_file_path, 'file')) {
396
-            return false;
397
-        }
398
-        return true;
399
-    }
400
-
401
-
402
-    /**
403
-     * Gets the parent folder. If provided with file, gets the folder that contains it.
404
-     * If provided a folder, gets its parent folder.
405
-     *
406
-     * @param string $file_or_folder_path
407
-     * @return string parent folder, ENDING with a directory separator
408
-     */
409
-    public static function get_parent_folder($file_or_folder_path)
410
-    {
411
-        // find the last /, ignoring a / on the very end
412
-        // eg if given "/var/something/somewhere/", we want to get "somewhere"'s
413
-        // parent folder, "/var/something/"
414
-        $ds = strlen($file_or_folder_path) > 1
415
-            ? strrpos($file_or_folder_path, '/', -2)
416
-            : strlen($file_or_folder_path);
417
-        return substr($file_or_folder_path, 0, $ds + 1);
418
-    }
419
-
420
-
421
-    /**
422
-     * get_file_contents
423
-     *
424
-     * @param string $full_file_path
425
-     * @return string
426
-     */
427
-    public static function get_file_contents($full_file_path = '')
428
-    {
429
-        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
430
-        if (EEH_File::verify_filepath_and_permissions(
431
-            $full_file_path,
432
-            EEH_File::get_filename_from_filepath($full_file_path),
433
-            EEH_File::get_file_extension($full_file_path)
434
-        )) {
435
-            // load WP_Filesystem and set file permissions
436
-            $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
437
-            return $wp_filesystem->get_contents(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
438
-        }
439
-        return '';
440
-    }
441
-
442
-
443
-    /**
444
-     * write_file
445
-     *
446
-     * @param string $full_file_path
447
-     * @param string $file_contents - the content to be written to the file
448
-     * @param string $file_type
449
-     * @return bool
450
-     */
451
-    public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '')
452
-    {
453
-        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
454
-        $file_type      = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : '';
455
-        $folder         = EEH_File::remove_filename_from_filepath($full_file_path);
456
-        if (! EEH_File::verify_is_writable($folder, 'folder')) {
457
-            if (defined('WP_DEBUG') && WP_DEBUG) {
458
-                $msg =
459
-                    sprintf(
460
-                        __('The %1$sfile located at "%2$s" is not writable.', 'event_espresso'),
461
-                        $file_type,
462
-                        $full_file_path
463
-                    );
464
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
465
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
466
-            }
467
-            return false;
468
-        }
469
-        // load WP_Filesystem and set file permissions
470
-        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
471
-        // write the file
472
-        if (! $wp_filesystem->put_contents(
473
-            EEH_File::convert_local_filepath_to_remote_filepath($full_file_path),
474
-            $file_contents
475
-        )) {
476
-            if (defined('WP_DEBUG') && WP_DEBUG) {
477
-                $msg =
478
-                    sprintf(
479
-                        __('The %1$sfile located at "%2$s" could not be written to.', 'event_espresso'),
480
-                        $file_type,
481
-                        $full_file_path
482
-                    );
483
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, 'f');
484
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
485
-            }
486
-            return false;
487
-        }
488
-        return true;
489
-    }
490
-
491
-
492
-    /**
493
-     * Wrapper for WP_Filesystem_Base::delete
494
-     *
495
-     * @param string         $filepath
496
-     * @param boolean        $recursive
497
-     * @param boolean|string $type 'd' for directory, 'f' for file
498
-     * @return boolean
499
-     */
500
-    public static function delete($filepath, $recursive = false, $type = false)
501
-    {
502
-        $wp_filesystem = EEH_File::_get_wp_filesystem();
503
-        return $wp_filesystem->delete($filepath, $recursive, $type);
504
-    }
505
-
506
-
507
-    /**
508
-     * exists
509
-     * checks if a file exists using the WP filesystem
510
-     *
511
-     * @param string $full_file_path
512
-     * @return bool
513
-     */
514
-    public static function exists($full_file_path = '')
515
-    {
516
-        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
517
-        return $wp_filesystem->exists(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
518
-    }
519
-
520
-
521
-    /**
522
-     * is_readable
523
-     * checks if a file is_readable using the WP filesystem
524
-     *
525
-     * @param string $full_file_path
526
-     * @return bool
527
-     */
528
-    public static function is_readable($full_file_path = '')
529
-    {
530
-        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
531
-        return $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
532
-    }
533
-
534
-
535
-    /**
536
-     * remove_filename_from_filepath
537
-     * given a full path to a file including the filename itself, this removes  the filename and returns the path, up
538
-     * to, but NOT including the filename OR slash
539
-     *
540
-     * @param string $full_file_path
541
-     * @return string
542
-     */
543
-    public static function remove_filename_from_filepath($full_file_path = '')
544
-    {
545
-        return pathinfo($full_file_path, PATHINFO_DIRNAME);
546
-    }
547
-
548
-
549
-    /**
550
-     * get_filename_from_filepath. Arguably the same as basename()
551
-     *
552
-     * @param string $full_file_path
553
-     * @return string
554
-     */
555
-    public static function get_filename_from_filepath($full_file_path = '')
556
-    {
557
-        return pathinfo($full_file_path, PATHINFO_BASENAME);
558
-    }
559
-
560
-
561
-    /**
562
-     * get_file_extension
563
-     *
564
-     * @param string $full_file_path
565
-     * @return string
566
-     */
567
-    public static function get_file_extension($full_file_path = '')
568
-    {
569
-        return pathinfo($full_file_path, PATHINFO_EXTENSION);
570
-    }
571
-
572
-
573
-    /**
574
-     * add_htaccess_deny_from_all so the web server cannot access this folder
575
-     *
576
-     * @param string $folder
577
-     * @return bool
578
-     */
579
-    public static function add_htaccess_deny_from_all($folder = '')
580
-    {
581
-        $folder = EEH_File::standardise_and_end_with_directory_separator($folder);
582
-        if (! EEH_File::exists($folder . '.htaccess')) {
583
-            if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) {
584
-                return false;
585
-            }
586
-        }
587
-
588
-        return true;
589
-    }
590
-
591
-
592
-    /**
593
-     * Adds an index file to this folder, so folks can't list all the file's contents
594
-     *
595
-     * @param string $folder
596
-     * @return boolean
597
-     */
598
-    public static function add_index_file($folder)
599
-    {
600
-        $folder = EEH_File::standardise_and_end_with_directory_separator($folder);
601
-        if (! EEH_File::exists($folder . 'index.php')) {
602
-            if (! EEH_File::write_to_file(
603
-                $folder . 'index.php',
604
-                'You are not permitted to read from this folder',
605
-                '.php'
606
-            )) {
607
-                return false;
608
-            }
609
-        }
610
-        return true;
611
-    }
612
-
613
-
614
-    /**
615
-     * Given that the file in $file_path has the normal name, (ie, CLASSNAME.whatever.php),
616
-     * extract that classname.
617
-     *
618
-     * @param string $file_path
619
-     * @return string
620
-     */
621
-    public static function get_classname_from_filepath_with_standard_filename($file_path)
622
-    {
623
-        // extract file from path
624
-        $filename = basename($file_path);
625
-        // now remove the first period and everything after
626
-        $pos_of_first_period = strpos($filename, '.');
627
-        return substr($filename, 0, $pos_of_first_period);
628
-    }
629
-
630
-
631
-    /**
632
-     * standardise_directory_separators
633
-     *  convert all directory separators in a file path.
634
-     *
635
-     * @param string $file_path
636
-     * @param bool   $rtrim will remove trailing backslash
637
-     * @return string
638
-     */
639
-    public static function standardise_directory_separators($file_path, $rtrim = false)
640
-    {
641
-        $file_path = $rtrim ? rtrim($file_path, '/\\') : $file_path;
642
-        return str_replace(['\\', '/'], '/', $file_path);
643
-    }
644
-
645
-
646
-    /**
647
-     * end_with_directory_separator
648
-     *  ensures that file path ends with '/'
649
-     *
650
-     * @param string $file_path
651
-     * @return string
652
-     */
653
-    public static function end_with_directory_separator($file_path)
654
-    {
655
-        return rtrim($file_path, '/\\') . '/';
656
-    }
657
-
658
-
659
-    /**
660
-     * shorthand for both EEH_FIle::end_with_directory_separator AND EEH_File::standardise_directory_separators
661
-     *
662
-     * @param $file_path
663
-     * @return string
664
-     */
665
-    public static function standardise_and_end_with_directory_separator($file_path)
666
-    {
667
-        return self::end_with_directory_separator(self::standardise_directory_separators($file_path));
668
-    }
669
-
670
-
671
-    /**
672
-     * takes the folder name (with or without trailing slash) and finds the files it in,
673
-     * and what the class's name inside of each should be.
674
-     *
675
-     * @param array   $folder_paths
676
-     * @param boolean $index_numerically if TRUE, the returned array will be indexed numerically;
677
-     *                                   if FALSE (Default), returned array will be indexed by the filenames minus
678
-     *                                   extensions. Set it TRUE if you know there are files in the directory with the
679
-     *                                   same name but different extensions
680
-     * @return array if $index_numerically == TRUE keys are numeric ,
681
-     *                                   if $index_numerically == FALSE (Default) keys are what the class names SHOULD
682
-     *                                   be; and values are their file paths
683
-     */
684
-    public static function get_contents_of_folders($folder_paths = [], $index_numerically = false)
685
-    {
686
-        $class_to_folder_path = [];
687
-        foreach ($folder_paths as $folder_path) {
688
-            $folder_path = self::standardise_and_end_with_directory_separator($folder_path);
689
-            // load WP_Filesystem and set file permissions
690
-            $files_in_folder = glob($folder_path . '*.php');
691
-            $class_to_folder_path = [];
692
-            if ($files_in_folder) {
693
-                foreach ($files_in_folder as $file_path) {
694
-                    // only add files, not folders
695
-                    if (! is_dir($file_path)) {
696
-                        if ($index_numerically) {
697
-                            $class_to_folder_path[] = $file_path;
698
-                        } else {
699
-                            $classname =
700
-                                self::get_classname_from_filepath_with_standard_filename($file_path);
701
-                            $class_to_folder_path[ $classname ] = $file_path;
702
-                        }
703
-                    }
704
-                }
705
-            }
706
-        }
707
-        return $class_to_folder_path;
708
-    }
709
-
710
-
711
-    /**
712
-     * Copies a file. Mostly a wrapper of WP_Filesystem::copy
713
-     *
714
-     * @param string  $source_file
715
-     * @param string  $destination_file
716
-     * @param boolean $overwrite
717
-     * @return boolean success
718
-     */
719
-    public static function copy($source_file, $destination_file, $overwrite = false)
720
-    {
721
-        $source_file      = EEH_File::validateFileForCopyOrMove($source_file);
722
-        $destination_file = EEH_File::validateFolderForCopyOrMove($destination_file);
723
-        if (! $source_file || ! $destination_file) {
724
-            return false;
725
-        }
726
-        // load WP_Filesystem and set file permissions
727
-        $wp_filesystem = EEH_File::_get_wp_filesystem($destination_file);
728
-        // write the file
729
-        $copied = $wp_filesystem->copy(
730
-            EEH_File::convert_local_filepath_to_remote_filepath($source_file),
731
-            EEH_File::convert_local_filepath_to_remote_filepath($destination_file),
732
-            $overwrite
733
-        );
734
-        if (! $copied) {
735
-            if (defined('WP_DEBUG') && WP_DEBUG) {
736
-                $msg = sprintf(
737
-                    __(
738
-                        'Attempted writing to file %1$s, but could not, probably because of permissions issues',
739
-                        'event_espresso'
740
-                    ),
741
-                    $source_file
742
-                );
743
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($source_file, 'f');
744
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
745
-            }
746
-            return false;
747
-        }
748
-        return true;
749
-    }
750
-
751
-
752
-    /**
753
-     * Reports whether or not the filepath is in the EE uploads folder or not
754
-     *
755
-     * @param string $filepath
756
-     * @return boolean
757
-     */
758
-    public static function is_in_uploads_folder($filepath)
759
-    {
760
-        $uploads = wp_upload_dir();
761
-        return strpos($filepath, $uploads['basedir']) === 0;
762
-    }
763
-
764
-
765
-    /**
766
-     * Given a "local" filepath (what you probably thought was the only filepath),
767
-     * converts it into a "remote" filepath (the filepath the currently-in-use
768
-     * $wp_filesystem needs to use access the folder or file).
769
-     * See http://wordpress.stackexchange.com/questions/124900/using-wp-filesystem-in-plugins
770
-     *
771
-     * @param string $local_filepath the filepath to the folder/file locally
772
-     * @return string the remote filepath (eg the filepath the filesystem method, eg
773
-     *                               ftp or ssh, will use to access the folder
774
-     */
775
-    public static function convert_local_filepath_to_remote_filepath($local_filepath)
776
-    {
777
-        $wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath);
778
-        return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath);
779
-    }
780
-
781
-
782
-    /**
783
-     * wrapper for WP_Filesystem::chmod()
784
-     *
785
-     * @param string    $file      Path to the file.
786
-     * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
787
-     *                             0755 for directories. Default false.
788
-     * @param bool      $recursive Optional. If set to true, changes file permissions recursively.
789
-     *                             Default false.
790
-     * @return bool True on success, false on failure.
791
-     */
792
-    public static function chmod($file, $mode = false, $recursive = false)
793
-    {
794
-        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
795
-        return $wp_filesystem->chmod($file, $mode, $recursive);
796
-    }
797
-
798
-
799
-    /**
800
-     * wrapper for WP_Filesystem::getchmod()
801
-     *
802
-     * @param string $file Path to the file.
803
-     * @return string Mode of the file (the last 3 digits).
804
-     */
805
-    public static function permissions($file)
806
-    {
807
-        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
808
-        return $wp_filesystem->getchmod($file);
809
-    }
810
-
811
-
812
-    /**
813
-     * wrapper for WP_Filesystem::owner()
814
-     *
815
-     * @param string $file Path to the file.
816
-     * @return string|false Username of the owner on success, false on failure.
817
-     */
818
-    public static function owner($file)
819
-    {
820
-        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
821
-        return $wp_filesystem->owner($file);
822
-    }
823
-
824
-
825
-    /**
826
-     * wrapper for WP_Filesystem::group()
827
-     *
828
-     * @param string $file Path to the file.
829
-     * @return string|false The group on success, false on failure.
830
-     */
831
-    public static function group($file)
832
-    {
833
-        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
834
-        return $wp_filesystem->group($file);
835
-    }
836
-
837
-
838
-    /**
839
-     * wrapper for WP_Filesystem::move()
840
-     *
841
-     * @param string $source      Path to the source file.
842
-     * @param string $destination Path to the destination file.
843
-     * @param bool   $overwrite   Optional. Whether to overwrite the destination file if it exists.
844
-     *                            Default false.
845
-     * @return bool True on success, false on failure.
846
-     */
847
-    public static function move($source, $destination, $overwrite = false)
848
-    {
849
-        // throw new RuntimeException("source: {$source} && destination: {$destination}");
850
-        $source      = EEH_File::validateFileForCopyOrMove($source);
851
-        $destination = EEH_File::validateFolderForCopyOrMove($destination);
852
-        if (! $source || ! $destination) {
853
-            return false;
854
-        }
855
-        $wp_filesystem = EEH_File::_get_wp_filesystem($source);
856
-        if ($wp_filesystem->move($source, $destination, $overwrite)) {
857
-            return true;
858
-        }
859
-        if (defined('WP_DEBUG') && WP_DEBUG) {
860
-            $file        = EEH_File::convert_local_filepath_to_remote_filepath($source);
861
-            $owner       = EEH_File::owner($file);
862
-            $group       = EEH_File::group($file);
863
-            $permissions = EEH_File::permissions($file);
864
-            EE_Error::add_error(
865
-                sprintf(
866
-                    esc_html__(
867
-                        'Unable to move the file "%1$s" to new location (possible permissions errors). The existing "owner:group permissions" for the file are: "%2$s"',
868
-                        'event_espresso'
869
-                    ),
870
-                    $destination,
871
-                    "{$owner}:{$group} $permissions"
872
-                ),
873
-                __FILE__,
874
-                __FUNCTION__,
875
-                __LINE__
876
-            );
877
-        }
878
-        return false;
879
-    }
880
-
881
-
882
-    /**
883
-     * @param string $source_file
884
-     * @return string
885
-     */
886
-    private static function validateFileForCopyOrMove($source_file)
887
-    {
888
-        $full_source_path = EEH_File::standardise_directory_separators($source_file);
889
-        if (! EEH_File::exists($full_source_path)) {
890
-            if (defined('WP_DEBUG') && WP_DEBUG) {
891
-                $msg =
892
-                    sprintf(
893
-                        __('The file located at "%2$s" is not readable or doesn\'t exist.', 'event_espresso'),
894
-                        '',
895
-                        $full_source_path
896
-                    );
897
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path);
898
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
899
-            }
900
-            return '';
901
-        }
902
-        return $full_source_path;
903
-    }
904
-
905
-
906
-    /**
907
-     * @param string $destination_file
908
-     * @return string
909
-     */
910
-    private static function validateFolderForCopyOrMove($destination_file)
911
-    {
912
-        $full_dest_path = EEH_File::standardise_directory_separators($destination_file);
913
-        $folder         = EEH_File::remove_filename_from_filepath($full_dest_path);
914
-        EEH_File::ensure_folder_exists_and_is_writable($folder);
915
-        if (! EEH_File::verify_is_writable($folder, 'folder')) {
916
-            if (defined('WP_DEBUG') && WP_DEBUG) {
917
-                $msg = sprintf(
918
-                    __('The file located at "%2$s" is not writable.', 'event_espresso'),
919
-                    '',
920
-                    $full_dest_path
921
-                );
922
-                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_dest_path);
923
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
924
-            }
925
-            return '';
926
-        }
927
-        return $full_dest_path;
928
-    }
28
+	/**
29
+	 * @var string $_credentials_form
30
+	 */
31
+	private static $_credentials_form;
32
+
33
+	/**
34
+	 * @var WP_Filesystem_Base $_wp_filesystem
35
+	 */
36
+	protected static $_wp_filesystem;
37
+
38
+
39
+	/**
40
+	 * @param string|null $filepath the filepath we want to work in. If its in the
41
+	 *                              wp uploads directory, we'll want to just use the filesystem directly.
42
+	 *                              If not provided, we have to assume its not in the uploads directory
43
+	 * @return WP_Filesystem_Base
44
+	 */
45
+	private static function _get_wp_filesystem($filepath = null)
46
+	{
47
+		if (apply_filters(
48
+			'FHEE__EEH_File___get_wp_filesystem__allow_using_filesystem_direct',
49
+			$filepath && EEH_File::is_in_uploads_folder($filepath),
50
+			$filepath
51
+		)) {
52
+			return EEH_File::loadAlternateWpFileSystem();
53
+		}
54
+		return EEH_File::loadWpFileSystem();
55
+	}
56
+
57
+
58
+	/**
59
+	 * @return WP_Filesystem_Base
60
+	 */
61
+	private static function loadAlternateWpFileSystem()
62
+	{
63
+		if (! EEH_File::$_wp_filesystem instanceof WP_Filesystem_Base) {
64
+			require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
65
+			$method             = 'direct';
66
+			$wp_filesystem_file =
67
+				apply_filters(
68
+					'filesystem_method_file',
69
+					ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php',
70
+					$method
71
+				);
72
+			// added the following validation logic
73
+			// because we allow the filesystem filepath to be filtered,
74
+			// and are loading whatever file the path pointed to,
75
+			// but we were not validating things in any way :scream_emoji:
76
+			$valid_wp_filesystem_types = [
77
+				'direct'     => 'WP_Filesystem_Direct',
78
+				'ftpext'     => 'WP_Filesystem_FTPext',
79
+				'ftpsockets' => 'WP_Filesystem_ftpsockets',
80
+				'ssh2'       => 'WP_Filesystem_SSH2',
81
+			];
82
+			$valid                     = false;
83
+			$wp_filesystem_class       = '';
84
+			foreach ($valid_wp_filesystem_types as $method => $filesystem_class) {
85
+				// if file path matches for one of valid types, then toggle $valid to true
86
+				if (strpos($wp_filesystem_file, $method) > 0) {
87
+					$valid               = true;
88
+					$wp_filesystem_class = $filesystem_class;
89
+				}
90
+			}
91
+			if (! $valid || ! file_exists($wp_filesystem_file)) {
92
+				EE_Error::add_error(
93
+					sprintf(
94
+						__(
95
+							'The supplied WP Filesystem filepath "%1$s" is either missing or invalid.',
96
+							'event_espresso'
97
+						),
98
+						$wp_filesystem_file
99
+					),
100
+					__FILE__,
101
+					__FUNCTION__,
102
+					__LINE__
103
+				);
104
+			}
105
+			// check constants defined, just like in the wp-admin/includes/file.php WP_Filesystem()
106
+			if (! defined('FS_CHMOD_DIR')) {
107
+				define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0775 | 0755));
108
+			}
109
+			if (! defined('FS_CHMOD_FILE')) {
110
+				define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0775 | 0644));
111
+			}
112
+			require_once($wp_filesystem_file);
113
+			EEH_File::$_wp_filesystem = new $wp_filesystem_class([]);
114
+		}
115
+		return EEH_File::$_wp_filesystem;
116
+	}
117
+
118
+
119
+	/**
120
+	 * @return WP_Filesystem_Base
121
+	 */
122
+	private static function loadWpFileSystem()
123
+	{
124
+		global $wp_filesystem;
125
+		// no filesystem setup ???
126
+		if (! $wp_filesystem instanceof WP_Filesystem_Base) {
127
+			// if some eager beaver's just trying to get in there too early...
128
+			// let them do it, because we are one of those eager beavers! :P
129
+			/**
130
+			 * more explanations are probably merited. http://codex.wordpress.org/Filesystem_API#Initializing_WP_Filesystem_Base
131
+			 * says WP_Filesystem should be used after 'wp_loaded', but currently EE's activation process
132
+			 * is setup to mostly happen on 'init', and refactoring to have it happen on
133
+			 * 'wp_loaded' is too much work on a BETA milestone.
134
+			 * So this fix is expected to work if the WP files are owned by the server user,
135
+			 * but probably not if the user needs to enter their FTP credentials to modify files
136
+			 * and there may be troubles if the WP files are owned by a different user
137
+			 * than the server user. But both of these issues should exist in 4.4 and earlier too
138
+			 */
139
+			if (false && ! did_action('wp_loaded')) {
140
+				$msg =
141
+					__(
142
+						'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.',
143
+						'event_espresso'
144
+					);
145
+				if (WP_DEBUG) {
146
+					$msg .= '<br />' . __(
147
+						'The WP Filesystem can not be accessed until after the "wp_loaded" hook has run, so it\'s best not to attempt access until the "admin_init" hookpoint.',
148
+						'event_espresso'
149
+					);
150
+				}
151
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
152
+			}
153
+			// should be loaded if we are past the wp_loaded hook...
154
+			if (! function_exists('WP_Filesystem') || ! function_exists('submit_button')) {
155
+				require_once(ABSPATH . 'wp-admin/includes/file.php');
156
+				require_once(ABSPATH . 'wp-admin/includes/template.php');
157
+			}
158
+			// turn on output buffering so that we can capture the credentials form
159
+			ob_start();
160
+			$credentials = request_filesystem_credentials(false);
161
+			// store credentials form for the time being
162
+			EEH_File::$_credentials_form = ob_get_clean();
163
+			// if credentials do NOT exist
164
+			if ($credentials === false) {
165
+				add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999);
166
+				EE_Error::add_error(
167
+					__(
168
+						'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.',
169
+						'event_espresso'
170
+					),
171
+					__FILE__,
172
+					__FUNCTION__,
173
+					__LINE__
174
+				);
175
+			}
176
+			// basically check for direct or previously configured access
177
+			if (! WP_Filesystem($credentials)
178
+				&& is_wp_error($wp_filesystem->errors)
179
+				&& $wp_filesystem->errors->get_error_code()
180
+			) {
181
+				add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999);
182
+				EE_Error::add_error(
183
+					sprintf(
184
+						__('WP Filesystem Error: $1%s', 'event_espresso'),
185
+						$wp_filesystem->errors->get_error_message()
186
+					),
187
+					__FILE__,
188
+					__FUNCTION__,
189
+					__LINE__
190
+				);
191
+			}
192
+		}
193
+		return $wp_filesystem;
194
+	}
195
+
196
+
197
+	/**
198
+	 * display_request_filesystem_credentials_form
199
+	 */
200
+	public static function display_request_filesystem_credentials_form()
201
+	{
202
+		if (! empty(EEH_File::$_credentials_form)) {
203
+			echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>';
204
+		}
205
+	}
206
+
207
+
208
+	/**
209
+	 *    verify_filepath_and_permissions
210
+	 *    checks that a file is readable and has sufficient file permissions set to access
211
+	 *
212
+	 * @access public
213
+	 * @param string $full_file_path - full server path to the folder or file
214
+	 * @param string $file_name      - name of file if checking a file
215
+	 * @param string $file_ext       - file extension (ie: "php") if checking a file
216
+	 * @param string $type_of_file   - general type of file (ie: "module"), this is only used to improve error messages
217
+	 * @return bool
218
+	 */
219
+	public static function verify_filepath_and_permissions(
220
+		$full_file_path = '',
221
+		$file_name = '',
222
+		$file_ext = '',
223
+		$type_of_file = ''
224
+	) {
225
+		// load WP_Filesystem and set file permissions
226
+		$wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
227
+		$full_file_path = EEH_File::standardise_directory_separators($full_file_path);
228
+		if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
229
+			$file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name;
230
+			$file_name .= ! empty($file_ext) ? ' file' : ' folder';
231
+			$msg       = sprintf(
232
+				__(
233
+					'The requested %1$s could not be found or is not readable, possibly due to an incorrect filepath, or incorrect file permissions.%2$s',
234
+					'event_espresso'
235
+				),
236
+				$file_name,
237
+				'<br />'
238
+			);
239
+			if (EEH_File::exists($full_file_path)) {
240
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, $type_of_file);
241
+			} else {
242
+				// no file permissions means the file was not found
243
+				$msg .= sprintf(
244
+					__('Please ensure the following path is correct: "%s".', 'event_espresso'),
245
+					$full_file_path
246
+				);
247
+			}
248
+			if (defined('WP_DEBUG') && WP_DEBUG) {
249
+				EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
250
+			}
251
+			return false;
252
+		}
253
+		return true;
254
+	}
255
+
256
+
257
+	/**
258
+	 * _permissions_error_for_unreadable_filepath - attempts to determine why permissions are set incorrectly for a
259
+	 * file or folder
260
+	 *
261
+	 * @access private
262
+	 * @param string $full_file_path - full server path to the folder or file
263
+	 * @param string $type_of_file   - general type of file (ie: "module"), this is only used to improve error messages
264
+	 * @return string
265
+	 */
266
+	private static function _permissions_error_for_unreadable_filepath($full_file_path = '', $type_of_file = '')
267
+	{
268
+		// load WP_Filesystem and set file permissions
269
+		$wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
270
+		// check file permissions
271
+		$perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
272
+		if ($perms) {
273
+			// file permissions exist, but way be set incorrectly
274
+			$type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : '';
275
+			$type_of_file .= ! empty($type_of_file) ? 'file' : 'folder';
276
+			return ' ' . sprintf(
277
+				__(
278
+					'File permissions for the requested %1$s are currently set at "%2$s". The recommended permissions are 644 for files and 755 for folders.',
279
+					'event_espresso'
280
+				),
281
+				$type_of_file,
282
+				$perms
283
+			);
284
+		} else {
285
+			// file exists but file permissions could not be read ?!?!
286
+			return ' ' . sprintf(
287
+				__(
288
+					'Please ensure that the server and/or PHP configuration allows the current process to access the following file: "%s".',
289
+					'event_espresso'
290
+				),
291
+				$full_file_path
292
+			);
293
+		}
294
+	}
295
+
296
+
297
+	/**
298
+	 * ensure_folder_exists_and_is_writable
299
+	 * ensures that a folder exists and is writable, will attempt to create folder if it does not exist
300
+	 * Also ensures all the parent folders exist, and if not tries to create them.
301
+	 * Also, if this function creates the folder, adds a .htaccess file and index.html file
302
+	 *
303
+	 * @param string $folder
304
+	 * @return bool false if folder isn't writable; true if it exists and is writeable,
305
+	 */
306
+	public static function ensure_folder_exists_and_is_writable($folder = '')
307
+	{
308
+		if (empty($folder)) {
309
+			return false;
310
+		}
311
+		// remove ending /
312
+		$folder        = EEH_File::standardise_directory_separators(rtrim($folder, '/\\'));
313
+		$parent_folder = EEH_File::get_parent_folder($folder);
314
+		// add / to folder
315
+		$folder        = EEH_File::end_with_directory_separator($folder);
316
+		$wp_filesystem = EEH_File::_get_wp_filesystem($folder);
317
+		$remote_dir    = EEH_File::convert_local_filepath_to_remote_filepath($folder);
318
+		if (! $wp_filesystem->is_dir($remote_dir)) {
319
+			// ok so it doesn't exist. Does its parent? Can we write to it?
320
+			if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
321
+				return false;
322
+			}
323
+			if (! EEH_File::verify_is_writable($parent_folder, 'folder')) {
324
+				return false;
325
+			}
326
+			if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) {
327
+				if (defined('WP_DEBUG') && WP_DEBUG) {
328
+					$msg = sprintf(__('"%s" could not be created.', 'event_espresso'), $folder);
329
+					$msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder);
330
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
331
+				}
332
+				return false;
333
+			}
334
+			EEH_File::add_index_file($folder);
335
+		} elseif (! EEH_File::verify_is_writable($folder, 'folder')) {
336
+			return false;
337
+		}
338
+		return true;
339
+	}
340
+
341
+
342
+	/**
343
+	 * verify_is_writable - checks if a file or folder is writable
344
+	 *
345
+	 * @param string $full_path      - full server path to file or folder
346
+	 * @param string $file_or_folder - whether checking a file or folder
347
+	 * @return bool
348
+	 */
349
+	public static function verify_is_writable($full_path = '', $file_or_folder = 'folder')
350
+	{
351
+		// load WP_Filesystem and set file permissions
352
+		$wp_filesystem = EEH_File::_get_wp_filesystem($full_path);
353
+		$full_path     = EEH_File::standardise_directory_separators($full_path);
354
+		$remote_path   = EEH_File::convert_local_filepath_to_remote_filepath($full_path);
355
+		$remote_path   = rtrim($remote_path, '/\\');
356
+		if (! $wp_filesystem->is_writable($remote_path)) {
357
+			if (defined('WP_DEBUG') && WP_DEBUG) {
358
+				$msg = sprintf(__('The "%1$s" %2$s is not writable.', 'event_espresso'), $full_path, $file_or_folder);
359
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_path);
360
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
361
+			}
362
+			return false;
363
+		}
364
+		return true;
365
+	}
366
+
367
+
368
+	/**
369
+	 * ensure_file_exists_and_is_writable
370
+	 * ensures that a file exists and is writable, will attempt to create file if it does not exist.
371
+	 * Also ensures all the parent folders exist, and if not tries to create them.
372
+	 *
373
+	 * @param string $full_file_path
374
+	 * @return bool
375
+	 */
376
+	public static function ensure_file_exists_and_is_writable($full_file_path = '')
377
+	{
378
+		// load WP_Filesystem and set file permissions
379
+		$wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
380
+		$full_file_path = EEH_File::standardise_directory_separators($full_file_path);
381
+		$parent_folder  = EEH_File::get_parent_folder($full_file_path);
382
+		if (! EEH_File::exists($full_file_path)) {
383
+			if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
384
+				return false;
385
+			}
386
+			if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
387
+				if (defined('WP_DEBUG') && WP_DEBUG) {
388
+					$msg = sprintf(__('The "%s" file could not be created.', 'event_espresso'), $full_file_path);
389
+					$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
390
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
391
+				}
392
+				return false;
393
+			}
394
+		}
395
+		if (! EEH_File::verify_is_writable($full_file_path, 'file')) {
396
+			return false;
397
+		}
398
+		return true;
399
+	}
400
+
401
+
402
+	/**
403
+	 * Gets the parent folder. If provided with file, gets the folder that contains it.
404
+	 * If provided a folder, gets its parent folder.
405
+	 *
406
+	 * @param string $file_or_folder_path
407
+	 * @return string parent folder, ENDING with a directory separator
408
+	 */
409
+	public static function get_parent_folder($file_or_folder_path)
410
+	{
411
+		// find the last /, ignoring a / on the very end
412
+		// eg if given "/var/something/somewhere/", we want to get "somewhere"'s
413
+		// parent folder, "/var/something/"
414
+		$ds = strlen($file_or_folder_path) > 1
415
+			? strrpos($file_or_folder_path, '/', -2)
416
+			: strlen($file_or_folder_path);
417
+		return substr($file_or_folder_path, 0, $ds + 1);
418
+	}
419
+
420
+
421
+	/**
422
+	 * get_file_contents
423
+	 *
424
+	 * @param string $full_file_path
425
+	 * @return string
426
+	 */
427
+	public static function get_file_contents($full_file_path = '')
428
+	{
429
+		$full_file_path = EEH_File::standardise_directory_separators($full_file_path);
430
+		if (EEH_File::verify_filepath_and_permissions(
431
+			$full_file_path,
432
+			EEH_File::get_filename_from_filepath($full_file_path),
433
+			EEH_File::get_file_extension($full_file_path)
434
+		)) {
435
+			// load WP_Filesystem and set file permissions
436
+			$wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
437
+			return $wp_filesystem->get_contents(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
438
+		}
439
+		return '';
440
+	}
441
+
442
+
443
+	/**
444
+	 * write_file
445
+	 *
446
+	 * @param string $full_file_path
447
+	 * @param string $file_contents - the content to be written to the file
448
+	 * @param string $file_type
449
+	 * @return bool
450
+	 */
451
+	public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '')
452
+	{
453
+		$full_file_path = EEH_File::standardise_directory_separators($full_file_path);
454
+		$file_type      = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : '';
455
+		$folder         = EEH_File::remove_filename_from_filepath($full_file_path);
456
+		if (! EEH_File::verify_is_writable($folder, 'folder')) {
457
+			if (defined('WP_DEBUG') && WP_DEBUG) {
458
+				$msg =
459
+					sprintf(
460
+						__('The %1$sfile located at "%2$s" is not writable.', 'event_espresso'),
461
+						$file_type,
462
+						$full_file_path
463
+					);
464
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
465
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
466
+			}
467
+			return false;
468
+		}
469
+		// load WP_Filesystem and set file permissions
470
+		$wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
471
+		// write the file
472
+		if (! $wp_filesystem->put_contents(
473
+			EEH_File::convert_local_filepath_to_remote_filepath($full_file_path),
474
+			$file_contents
475
+		)) {
476
+			if (defined('WP_DEBUG') && WP_DEBUG) {
477
+				$msg =
478
+					sprintf(
479
+						__('The %1$sfile located at "%2$s" could not be written to.', 'event_espresso'),
480
+						$file_type,
481
+						$full_file_path
482
+					);
483
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, 'f');
484
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
485
+			}
486
+			return false;
487
+		}
488
+		return true;
489
+	}
490
+
491
+
492
+	/**
493
+	 * Wrapper for WP_Filesystem_Base::delete
494
+	 *
495
+	 * @param string         $filepath
496
+	 * @param boolean        $recursive
497
+	 * @param boolean|string $type 'd' for directory, 'f' for file
498
+	 * @return boolean
499
+	 */
500
+	public static function delete($filepath, $recursive = false, $type = false)
501
+	{
502
+		$wp_filesystem = EEH_File::_get_wp_filesystem();
503
+		return $wp_filesystem->delete($filepath, $recursive, $type);
504
+	}
505
+
506
+
507
+	/**
508
+	 * exists
509
+	 * checks if a file exists using the WP filesystem
510
+	 *
511
+	 * @param string $full_file_path
512
+	 * @return bool
513
+	 */
514
+	public static function exists($full_file_path = '')
515
+	{
516
+		$wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
517
+		return $wp_filesystem->exists(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
518
+	}
519
+
520
+
521
+	/**
522
+	 * is_readable
523
+	 * checks if a file is_readable using the WP filesystem
524
+	 *
525
+	 * @param string $full_file_path
526
+	 * @return bool
527
+	 */
528
+	public static function is_readable($full_file_path = '')
529
+	{
530
+		$wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
531
+		return $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
532
+	}
533
+
534
+
535
+	/**
536
+	 * remove_filename_from_filepath
537
+	 * given a full path to a file including the filename itself, this removes  the filename and returns the path, up
538
+	 * to, but NOT including the filename OR slash
539
+	 *
540
+	 * @param string $full_file_path
541
+	 * @return string
542
+	 */
543
+	public static function remove_filename_from_filepath($full_file_path = '')
544
+	{
545
+		return pathinfo($full_file_path, PATHINFO_DIRNAME);
546
+	}
547
+
548
+
549
+	/**
550
+	 * get_filename_from_filepath. Arguably the same as basename()
551
+	 *
552
+	 * @param string $full_file_path
553
+	 * @return string
554
+	 */
555
+	public static function get_filename_from_filepath($full_file_path = '')
556
+	{
557
+		return pathinfo($full_file_path, PATHINFO_BASENAME);
558
+	}
559
+
560
+
561
+	/**
562
+	 * get_file_extension
563
+	 *
564
+	 * @param string $full_file_path
565
+	 * @return string
566
+	 */
567
+	public static function get_file_extension($full_file_path = '')
568
+	{
569
+		return pathinfo($full_file_path, PATHINFO_EXTENSION);
570
+	}
571
+
572
+
573
+	/**
574
+	 * add_htaccess_deny_from_all so the web server cannot access this folder
575
+	 *
576
+	 * @param string $folder
577
+	 * @return bool
578
+	 */
579
+	public static function add_htaccess_deny_from_all($folder = '')
580
+	{
581
+		$folder = EEH_File::standardise_and_end_with_directory_separator($folder);
582
+		if (! EEH_File::exists($folder . '.htaccess')) {
583
+			if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) {
584
+				return false;
585
+			}
586
+		}
587
+
588
+		return true;
589
+	}
590
+
591
+
592
+	/**
593
+	 * Adds an index file to this folder, so folks can't list all the file's contents
594
+	 *
595
+	 * @param string $folder
596
+	 * @return boolean
597
+	 */
598
+	public static function add_index_file($folder)
599
+	{
600
+		$folder = EEH_File::standardise_and_end_with_directory_separator($folder);
601
+		if (! EEH_File::exists($folder . 'index.php')) {
602
+			if (! EEH_File::write_to_file(
603
+				$folder . 'index.php',
604
+				'You are not permitted to read from this folder',
605
+				'.php'
606
+			)) {
607
+				return false;
608
+			}
609
+		}
610
+		return true;
611
+	}
612
+
613
+
614
+	/**
615
+	 * Given that the file in $file_path has the normal name, (ie, CLASSNAME.whatever.php),
616
+	 * extract that classname.
617
+	 *
618
+	 * @param string $file_path
619
+	 * @return string
620
+	 */
621
+	public static function get_classname_from_filepath_with_standard_filename($file_path)
622
+	{
623
+		// extract file from path
624
+		$filename = basename($file_path);
625
+		// now remove the first period and everything after
626
+		$pos_of_first_period = strpos($filename, '.');
627
+		return substr($filename, 0, $pos_of_first_period);
628
+	}
629
+
630
+
631
+	/**
632
+	 * standardise_directory_separators
633
+	 *  convert all directory separators in a file path.
634
+	 *
635
+	 * @param string $file_path
636
+	 * @param bool   $rtrim will remove trailing backslash
637
+	 * @return string
638
+	 */
639
+	public static function standardise_directory_separators($file_path, $rtrim = false)
640
+	{
641
+		$file_path = $rtrim ? rtrim($file_path, '/\\') : $file_path;
642
+		return str_replace(['\\', '/'], '/', $file_path);
643
+	}
644
+
645
+
646
+	/**
647
+	 * end_with_directory_separator
648
+	 *  ensures that file path ends with '/'
649
+	 *
650
+	 * @param string $file_path
651
+	 * @return string
652
+	 */
653
+	public static function end_with_directory_separator($file_path)
654
+	{
655
+		return rtrim($file_path, '/\\') . '/';
656
+	}
657
+
658
+
659
+	/**
660
+	 * shorthand for both EEH_FIle::end_with_directory_separator AND EEH_File::standardise_directory_separators
661
+	 *
662
+	 * @param $file_path
663
+	 * @return string
664
+	 */
665
+	public static function standardise_and_end_with_directory_separator($file_path)
666
+	{
667
+		return self::end_with_directory_separator(self::standardise_directory_separators($file_path));
668
+	}
669
+
670
+
671
+	/**
672
+	 * takes the folder name (with or without trailing slash) and finds the files it in,
673
+	 * and what the class's name inside of each should be.
674
+	 *
675
+	 * @param array   $folder_paths
676
+	 * @param boolean $index_numerically if TRUE, the returned array will be indexed numerically;
677
+	 *                                   if FALSE (Default), returned array will be indexed by the filenames minus
678
+	 *                                   extensions. Set it TRUE if you know there are files in the directory with the
679
+	 *                                   same name but different extensions
680
+	 * @return array if $index_numerically == TRUE keys are numeric ,
681
+	 *                                   if $index_numerically == FALSE (Default) keys are what the class names SHOULD
682
+	 *                                   be; and values are their file paths
683
+	 */
684
+	public static function get_contents_of_folders($folder_paths = [], $index_numerically = false)
685
+	{
686
+		$class_to_folder_path = [];
687
+		foreach ($folder_paths as $folder_path) {
688
+			$folder_path = self::standardise_and_end_with_directory_separator($folder_path);
689
+			// load WP_Filesystem and set file permissions
690
+			$files_in_folder = glob($folder_path . '*.php');
691
+			$class_to_folder_path = [];
692
+			if ($files_in_folder) {
693
+				foreach ($files_in_folder as $file_path) {
694
+					// only add files, not folders
695
+					if (! is_dir($file_path)) {
696
+						if ($index_numerically) {
697
+							$class_to_folder_path[] = $file_path;
698
+						} else {
699
+							$classname =
700
+								self::get_classname_from_filepath_with_standard_filename($file_path);
701
+							$class_to_folder_path[ $classname ] = $file_path;
702
+						}
703
+					}
704
+				}
705
+			}
706
+		}
707
+		return $class_to_folder_path;
708
+	}
709
+
710
+
711
+	/**
712
+	 * Copies a file. Mostly a wrapper of WP_Filesystem::copy
713
+	 *
714
+	 * @param string  $source_file
715
+	 * @param string  $destination_file
716
+	 * @param boolean $overwrite
717
+	 * @return boolean success
718
+	 */
719
+	public static function copy($source_file, $destination_file, $overwrite = false)
720
+	{
721
+		$source_file      = EEH_File::validateFileForCopyOrMove($source_file);
722
+		$destination_file = EEH_File::validateFolderForCopyOrMove($destination_file);
723
+		if (! $source_file || ! $destination_file) {
724
+			return false;
725
+		}
726
+		// load WP_Filesystem and set file permissions
727
+		$wp_filesystem = EEH_File::_get_wp_filesystem($destination_file);
728
+		// write the file
729
+		$copied = $wp_filesystem->copy(
730
+			EEH_File::convert_local_filepath_to_remote_filepath($source_file),
731
+			EEH_File::convert_local_filepath_to_remote_filepath($destination_file),
732
+			$overwrite
733
+		);
734
+		if (! $copied) {
735
+			if (defined('WP_DEBUG') && WP_DEBUG) {
736
+				$msg = sprintf(
737
+					__(
738
+						'Attempted writing to file %1$s, but could not, probably because of permissions issues',
739
+						'event_espresso'
740
+					),
741
+					$source_file
742
+				);
743
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($source_file, 'f');
744
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
745
+			}
746
+			return false;
747
+		}
748
+		return true;
749
+	}
750
+
751
+
752
+	/**
753
+	 * Reports whether or not the filepath is in the EE uploads folder or not
754
+	 *
755
+	 * @param string $filepath
756
+	 * @return boolean
757
+	 */
758
+	public static function is_in_uploads_folder($filepath)
759
+	{
760
+		$uploads = wp_upload_dir();
761
+		return strpos($filepath, $uploads['basedir']) === 0;
762
+	}
763
+
764
+
765
+	/**
766
+	 * Given a "local" filepath (what you probably thought was the only filepath),
767
+	 * converts it into a "remote" filepath (the filepath the currently-in-use
768
+	 * $wp_filesystem needs to use access the folder or file).
769
+	 * See http://wordpress.stackexchange.com/questions/124900/using-wp-filesystem-in-plugins
770
+	 *
771
+	 * @param string $local_filepath the filepath to the folder/file locally
772
+	 * @return string the remote filepath (eg the filepath the filesystem method, eg
773
+	 *                               ftp or ssh, will use to access the folder
774
+	 */
775
+	public static function convert_local_filepath_to_remote_filepath($local_filepath)
776
+	{
777
+		$wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath);
778
+		return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath);
779
+	}
780
+
781
+
782
+	/**
783
+	 * wrapper for WP_Filesystem::chmod()
784
+	 *
785
+	 * @param string    $file      Path to the file.
786
+	 * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
787
+	 *                             0755 for directories. Default false.
788
+	 * @param bool      $recursive Optional. If set to true, changes file permissions recursively.
789
+	 *                             Default false.
790
+	 * @return bool True on success, false on failure.
791
+	 */
792
+	public static function chmod($file, $mode = false, $recursive = false)
793
+	{
794
+		$wp_filesystem = EEH_File::_get_wp_filesystem($file);
795
+		return $wp_filesystem->chmod($file, $mode, $recursive);
796
+	}
797
+
798
+
799
+	/**
800
+	 * wrapper for WP_Filesystem::getchmod()
801
+	 *
802
+	 * @param string $file Path to the file.
803
+	 * @return string Mode of the file (the last 3 digits).
804
+	 */
805
+	public static function permissions($file)
806
+	{
807
+		$wp_filesystem = EEH_File::_get_wp_filesystem($file);
808
+		return $wp_filesystem->getchmod($file);
809
+	}
810
+
811
+
812
+	/**
813
+	 * wrapper for WP_Filesystem::owner()
814
+	 *
815
+	 * @param string $file Path to the file.
816
+	 * @return string|false Username of the owner on success, false on failure.
817
+	 */
818
+	public static function owner($file)
819
+	{
820
+		$wp_filesystem = EEH_File::_get_wp_filesystem($file);
821
+		return $wp_filesystem->owner($file);
822
+	}
823
+
824
+
825
+	/**
826
+	 * wrapper for WP_Filesystem::group()
827
+	 *
828
+	 * @param string $file Path to the file.
829
+	 * @return string|false The group on success, false on failure.
830
+	 */
831
+	public static function group($file)
832
+	{
833
+		$wp_filesystem = EEH_File::_get_wp_filesystem($file);
834
+		return $wp_filesystem->group($file);
835
+	}
836
+
837
+
838
+	/**
839
+	 * wrapper for WP_Filesystem::move()
840
+	 *
841
+	 * @param string $source      Path to the source file.
842
+	 * @param string $destination Path to the destination file.
843
+	 * @param bool   $overwrite   Optional. Whether to overwrite the destination file if it exists.
844
+	 *                            Default false.
845
+	 * @return bool True on success, false on failure.
846
+	 */
847
+	public static function move($source, $destination, $overwrite = false)
848
+	{
849
+		// throw new RuntimeException("source: {$source} && destination: {$destination}");
850
+		$source      = EEH_File::validateFileForCopyOrMove($source);
851
+		$destination = EEH_File::validateFolderForCopyOrMove($destination);
852
+		if (! $source || ! $destination) {
853
+			return false;
854
+		}
855
+		$wp_filesystem = EEH_File::_get_wp_filesystem($source);
856
+		if ($wp_filesystem->move($source, $destination, $overwrite)) {
857
+			return true;
858
+		}
859
+		if (defined('WP_DEBUG') && WP_DEBUG) {
860
+			$file        = EEH_File::convert_local_filepath_to_remote_filepath($source);
861
+			$owner       = EEH_File::owner($file);
862
+			$group       = EEH_File::group($file);
863
+			$permissions = EEH_File::permissions($file);
864
+			EE_Error::add_error(
865
+				sprintf(
866
+					esc_html__(
867
+						'Unable to move the file "%1$s" to new location (possible permissions errors). The existing "owner:group permissions" for the file are: "%2$s"',
868
+						'event_espresso'
869
+					),
870
+					$destination,
871
+					"{$owner}:{$group} $permissions"
872
+				),
873
+				__FILE__,
874
+				__FUNCTION__,
875
+				__LINE__
876
+			);
877
+		}
878
+		return false;
879
+	}
880
+
881
+
882
+	/**
883
+	 * @param string $source_file
884
+	 * @return string
885
+	 */
886
+	private static function validateFileForCopyOrMove($source_file)
887
+	{
888
+		$full_source_path = EEH_File::standardise_directory_separators($source_file);
889
+		if (! EEH_File::exists($full_source_path)) {
890
+			if (defined('WP_DEBUG') && WP_DEBUG) {
891
+				$msg =
892
+					sprintf(
893
+						__('The file located at "%2$s" is not readable or doesn\'t exist.', 'event_espresso'),
894
+						'',
895
+						$full_source_path
896
+					);
897
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path);
898
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
899
+			}
900
+			return '';
901
+		}
902
+		return $full_source_path;
903
+	}
904
+
905
+
906
+	/**
907
+	 * @param string $destination_file
908
+	 * @return string
909
+	 */
910
+	private static function validateFolderForCopyOrMove($destination_file)
911
+	{
912
+		$full_dest_path = EEH_File::standardise_directory_separators($destination_file);
913
+		$folder         = EEH_File::remove_filename_from_filepath($full_dest_path);
914
+		EEH_File::ensure_folder_exists_and_is_writable($folder);
915
+		if (! EEH_File::verify_is_writable($folder, 'folder')) {
916
+			if (defined('WP_DEBUG') && WP_DEBUG) {
917
+				$msg = sprintf(
918
+					__('The file located at "%2$s" is not writable.', 'event_espresso'),
919
+					'',
920
+					$full_dest_path
921
+				);
922
+				$msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_dest_path);
923
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
924
+			}
925
+			return '';
926
+		}
927
+		return $full_dest_path;
928
+	}
929 929
 }
Please login to merge, or discard this patch.
Spacing   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -60,13 +60,13 @@  discard block
 block discarded – undo
60 60
      */
61 61
     private static function loadAlternateWpFileSystem()
62 62
     {
63
-        if (! EEH_File::$_wp_filesystem instanceof WP_Filesystem_Base) {
64
-            require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
63
+        if ( ! EEH_File::$_wp_filesystem instanceof WP_Filesystem_Base) {
64
+            require_once(ABSPATH.'wp-admin/includes/class-wp-filesystem-base.php');
65 65
             $method             = 'direct';
66 66
             $wp_filesystem_file =
67 67
                 apply_filters(
68 68
                     'filesystem_method_file',
69
-                    ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php',
69
+                    ABSPATH.'wp-admin/includes/class-wp-filesystem-'.$method.'.php',
70 70
                     $method
71 71
                 );
72 72
             // added the following validation logic
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
                     $wp_filesystem_class = $filesystem_class;
89 89
                 }
90 90
             }
91
-            if (! $valid || ! file_exists($wp_filesystem_file)) {
91
+            if ( ! $valid || ! file_exists($wp_filesystem_file)) {
92 92
                 EE_Error::add_error(
93 93
                     sprintf(
94 94
                         __(
@@ -103,11 +103,11 @@  discard block
 block discarded – undo
103 103
                 );
104 104
             }
105 105
             // check constants defined, just like in the wp-admin/includes/file.php WP_Filesystem()
106
-            if (! defined('FS_CHMOD_DIR')) {
106
+            if ( ! defined('FS_CHMOD_DIR')) {
107 107
                 define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0775 | 0755));
108 108
             }
109
-            if (! defined('FS_CHMOD_FILE')) {
110
-                define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0775 | 0644));
109
+            if ( ! defined('FS_CHMOD_FILE')) {
110
+                define('FS_CHMOD_FILE', (fileperms(ABSPATH.'index.php') & 0775 | 0644));
111 111
             }
112 112
             require_once($wp_filesystem_file);
113 113
             EEH_File::$_wp_filesystem = new $wp_filesystem_class([]);
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
     {
124 124
         global $wp_filesystem;
125 125
         // no filesystem setup ???
126
-        if (! $wp_filesystem instanceof WP_Filesystem_Base) {
126
+        if ( ! $wp_filesystem instanceof WP_Filesystem_Base) {
127 127
             // if some eager beaver's just trying to get in there too early...
128 128
             // let them do it, because we are one of those eager beavers! :P
129 129
             /**
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
                         'event_espresso'
144 144
                     );
145 145
                 if (WP_DEBUG) {
146
-                    $msg .= '<br />' . __(
146
+                    $msg .= '<br />'.__(
147 147
                         'The WP Filesystem can not be accessed until after the "wp_loaded" hook has run, so it\'s best not to attempt access until the "admin_init" hookpoint.',
148 148
                         'event_espresso'
149 149
                     );
@@ -151,9 +151,9 @@  discard block
 block discarded – undo
151 151
                 EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
152 152
             }
153 153
             // should be loaded if we are past the wp_loaded hook...
154
-            if (! function_exists('WP_Filesystem') || ! function_exists('submit_button')) {
155
-                require_once(ABSPATH . 'wp-admin/includes/file.php');
156
-                require_once(ABSPATH . 'wp-admin/includes/template.php');
154
+            if ( ! function_exists('WP_Filesystem') || ! function_exists('submit_button')) {
155
+                require_once(ABSPATH.'wp-admin/includes/file.php');
156
+                require_once(ABSPATH.'wp-admin/includes/template.php');
157 157
             }
158 158
             // turn on output buffering so that we can capture the credentials form
159 159
             ob_start();
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
                 );
175 175
             }
176 176
             // basically check for direct or previously configured access
177
-            if (! WP_Filesystem($credentials)
177
+            if ( ! WP_Filesystem($credentials)
178 178
                 && is_wp_error($wp_filesystem->errors)
179 179
                 && $wp_filesystem->errors->get_error_code()
180 180
             ) {
@@ -199,8 +199,8 @@  discard block
 block discarded – undo
199 199
      */
200 200
     public static function display_request_filesystem_credentials_form()
201 201
     {
202
-        if (! empty(EEH_File::$_credentials_form)) {
203
-            echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>';
202
+        if ( ! empty(EEH_File::$_credentials_form)) {
203
+            echo '<div class="updated espresso-notices-attention"><p>'.EEH_File::$_credentials_form.'</p></div>';
204 204
         }
205 205
     }
206 206
 
@@ -225,8 +225,8 @@  discard block
 block discarded – undo
225 225
         // load WP_Filesystem and set file permissions
226 226
         $wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
227 227
         $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
228
-        if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
229
-            $file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name;
228
+        if ( ! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
229
+            $file_name = ! empty($type_of_file) ? $file_name.' '.$type_of_file : $file_name;
230 230
             $file_name .= ! empty($file_ext) ? ' file' : ' folder';
231 231
             $msg       = sprintf(
232 232
                 __(
@@ -246,7 +246,7 @@  discard block
 block discarded – undo
246 246
                 );
247 247
             }
248 248
             if (defined('WP_DEBUG') && WP_DEBUG) {
249
-                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
249
+                EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
250 250
             }
251 251
             return false;
252 252
         }
@@ -271,9 +271,9 @@  discard block
 block discarded – undo
271 271
         $perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
272 272
         if ($perms) {
273 273
             // file permissions exist, but way be set incorrectly
274
-            $type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : '';
274
+            $type_of_file = ! empty($type_of_file) ? $type_of_file.' ' : '';
275 275
             $type_of_file .= ! empty($type_of_file) ? 'file' : 'folder';
276
-            return ' ' . sprintf(
276
+            return ' '.sprintf(
277 277
                 __(
278 278
                     'File permissions for the requested %1$s are currently set at "%2$s". The recommended permissions are 644 for files and 755 for folders.',
279 279
                     'event_espresso'
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
             );
284 284
         } else {
285 285
             // file exists but file permissions could not be read ?!?!
286
-            return ' ' . sprintf(
286
+            return ' '.sprintf(
287 287
                 __(
288 288
                     'Please ensure that the server and/or PHP configuration allows the current process to access the following file: "%s".',
289 289
                     'event_espresso'
@@ -315,15 +315,15 @@  discard block
 block discarded – undo
315 315
         $folder        = EEH_File::end_with_directory_separator($folder);
316 316
         $wp_filesystem = EEH_File::_get_wp_filesystem($folder);
317 317
         $remote_dir    = EEH_File::convert_local_filepath_to_remote_filepath($folder);
318
-        if (! $wp_filesystem->is_dir($remote_dir)) {
318
+        if ( ! $wp_filesystem->is_dir($remote_dir)) {
319 319
             // ok so it doesn't exist. Does its parent? Can we write to it?
320
-            if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
320
+            if ( ! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
321 321
                 return false;
322 322
             }
323
-            if (! EEH_File::verify_is_writable($parent_folder, 'folder')) {
323
+            if ( ! EEH_File::verify_is_writable($parent_folder, 'folder')) {
324 324
                 return false;
325 325
             }
326
-            if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) {
326
+            if ( ! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) {
327 327
                 if (defined('WP_DEBUG') && WP_DEBUG) {
328 328
                     $msg = sprintf(__('"%s" could not be created.', 'event_espresso'), $folder);
329 329
                     $msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder);
@@ -332,7 +332,7 @@  discard block
 block discarded – undo
332 332
                 return false;
333 333
             }
334 334
             EEH_File::add_index_file($folder);
335
-        } elseif (! EEH_File::verify_is_writable($folder, 'folder')) {
335
+        } elseif ( ! EEH_File::verify_is_writable($folder, 'folder')) {
336 336
             return false;
337 337
         }
338 338
         return true;
@@ -353,7 +353,7 @@  discard block
 block discarded – undo
353 353
         $full_path     = EEH_File::standardise_directory_separators($full_path);
354 354
         $remote_path   = EEH_File::convert_local_filepath_to_remote_filepath($full_path);
355 355
         $remote_path   = rtrim($remote_path, '/\\');
356
-        if (! $wp_filesystem->is_writable($remote_path)) {
356
+        if ( ! $wp_filesystem->is_writable($remote_path)) {
357 357
             if (defined('WP_DEBUG') && WP_DEBUG) {
358 358
                 $msg = sprintf(__('The "%1$s" %2$s is not writable.', 'event_espresso'), $full_path, $file_or_folder);
359 359
                 $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_path);
@@ -379,11 +379,11 @@  discard block
 block discarded – undo
379 379
         $wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
380 380
         $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
381 381
         $parent_folder  = EEH_File::get_parent_folder($full_file_path);
382
-        if (! EEH_File::exists($full_file_path)) {
383
-            if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
382
+        if ( ! EEH_File::exists($full_file_path)) {
383
+            if ( ! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
384 384
                 return false;
385 385
             }
386
-            if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
386
+            if ( ! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
387 387
                 if (defined('WP_DEBUG') && WP_DEBUG) {
388 388
                     $msg = sprintf(__('The "%s" file could not be created.', 'event_espresso'), $full_file_path);
389 389
                     $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
                 return false;
393 393
             }
394 394
         }
395
-        if (! EEH_File::verify_is_writable($full_file_path, 'file')) {
395
+        if ( ! EEH_File::verify_is_writable($full_file_path, 'file')) {
396 396
             return false;
397 397
         }
398 398
         return true;
@@ -451,9 +451,9 @@  discard block
 block discarded – undo
451 451
     public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '')
452 452
     {
453 453
         $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
454
-        $file_type      = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : '';
454
+        $file_type      = ! empty($file_type) ? rtrim($file_type, ' ').' ' : '';
455 455
         $folder         = EEH_File::remove_filename_from_filepath($full_file_path);
456
-        if (! EEH_File::verify_is_writable($folder, 'folder')) {
456
+        if ( ! EEH_File::verify_is_writable($folder, 'folder')) {
457 457
             if (defined('WP_DEBUG') && WP_DEBUG) {
458 458
                 $msg =
459 459
                     sprintf(
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
         // load WP_Filesystem and set file permissions
470 470
         $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
471 471
         // write the file
472
-        if (! $wp_filesystem->put_contents(
472
+        if ( ! $wp_filesystem->put_contents(
473 473
             EEH_File::convert_local_filepath_to_remote_filepath($full_file_path),
474 474
             $file_contents
475 475
         )) {
@@ -579,8 +579,8 @@  discard block
 block discarded – undo
579 579
     public static function add_htaccess_deny_from_all($folder = '')
580 580
     {
581 581
         $folder = EEH_File::standardise_and_end_with_directory_separator($folder);
582
-        if (! EEH_File::exists($folder . '.htaccess')) {
583
-            if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) {
582
+        if ( ! EEH_File::exists($folder.'.htaccess')) {
583
+            if ( ! EEH_File::write_to_file($folder.'.htaccess', 'deny from all', '.htaccess')) {
584 584
                 return false;
585 585
             }
586 586
         }
@@ -598,9 +598,9 @@  discard block
 block discarded – undo
598 598
     public static function add_index_file($folder)
599 599
     {
600 600
         $folder = EEH_File::standardise_and_end_with_directory_separator($folder);
601
-        if (! EEH_File::exists($folder . 'index.php')) {
602
-            if (! EEH_File::write_to_file(
603
-                $folder . 'index.php',
601
+        if ( ! EEH_File::exists($folder.'index.php')) {
602
+            if ( ! EEH_File::write_to_file(
603
+                $folder.'index.php',
604 604
                 'You are not permitted to read from this folder',
605 605
                 '.php'
606 606
             )) {
@@ -652,7 +652,7 @@  discard block
 block discarded – undo
652 652
      */
653 653
     public static function end_with_directory_separator($file_path)
654 654
     {
655
-        return rtrim($file_path, '/\\') . '/';
655
+        return rtrim($file_path, '/\\').'/';
656 656
     }
657 657
 
658 658
 
@@ -687,18 +687,18 @@  discard block
 block discarded – undo
687 687
         foreach ($folder_paths as $folder_path) {
688 688
             $folder_path = self::standardise_and_end_with_directory_separator($folder_path);
689 689
             // load WP_Filesystem and set file permissions
690
-            $files_in_folder = glob($folder_path . '*.php');
690
+            $files_in_folder = glob($folder_path.'*.php');
691 691
             $class_to_folder_path = [];
692 692
             if ($files_in_folder) {
693 693
                 foreach ($files_in_folder as $file_path) {
694 694
                     // only add files, not folders
695
-                    if (! is_dir($file_path)) {
695
+                    if ( ! is_dir($file_path)) {
696 696
                         if ($index_numerically) {
697 697
                             $class_to_folder_path[] = $file_path;
698 698
                         } else {
699 699
                             $classname =
700 700
                                 self::get_classname_from_filepath_with_standard_filename($file_path);
701
-                            $class_to_folder_path[ $classname ] = $file_path;
701
+                            $class_to_folder_path[$classname] = $file_path;
702 702
                         }
703 703
                     }
704 704
                 }
@@ -720,7 +720,7 @@  discard block
 block discarded – undo
720 720
     {
721 721
         $source_file      = EEH_File::validateFileForCopyOrMove($source_file);
722 722
         $destination_file = EEH_File::validateFolderForCopyOrMove($destination_file);
723
-        if (! $source_file || ! $destination_file) {
723
+        if ( ! $source_file || ! $destination_file) {
724 724
             return false;
725 725
         }
726 726
         // load WP_Filesystem and set file permissions
@@ -731,7 +731,7 @@  discard block
 block discarded – undo
731 731
             EEH_File::convert_local_filepath_to_remote_filepath($destination_file),
732 732
             $overwrite
733 733
         );
734
-        if (! $copied) {
734
+        if ( ! $copied) {
735 735
             if (defined('WP_DEBUG') && WP_DEBUG) {
736 736
                 $msg = sprintf(
737 737
                     __(
@@ -775,7 +775,7 @@  discard block
 block discarded – undo
775 775
     public static function convert_local_filepath_to_remote_filepath($local_filepath)
776 776
     {
777 777
         $wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath);
778
-        return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath);
778
+        return str_replace(WP_CONTENT_DIR.'/', $wp_filesystem->wp_content_dir(), $local_filepath);
779 779
     }
780 780
 
781 781
 
@@ -849,7 +849,7 @@  discard block
 block discarded – undo
849 849
         // throw new RuntimeException("source: {$source} && destination: {$destination}");
850 850
         $source      = EEH_File::validateFileForCopyOrMove($source);
851 851
         $destination = EEH_File::validateFolderForCopyOrMove($destination);
852
-        if (! $source || ! $destination) {
852
+        if ( ! $source || ! $destination) {
853 853
             return false;
854 854
         }
855 855
         $wp_filesystem = EEH_File::_get_wp_filesystem($source);
@@ -886,7 +886,7 @@  discard block
 block discarded – undo
886 886
     private static function validateFileForCopyOrMove($source_file)
887 887
     {
888 888
         $full_source_path = EEH_File::standardise_directory_separators($source_file);
889
-        if (! EEH_File::exists($full_source_path)) {
889
+        if ( ! EEH_File::exists($full_source_path)) {
890 890
             if (defined('WP_DEBUG') && WP_DEBUG) {
891 891
                 $msg =
892 892
                     sprintf(
@@ -912,7 +912,7 @@  discard block
 block discarded – undo
912 912
         $full_dest_path = EEH_File::standardise_directory_separators($destination_file);
913 913
         $folder         = EEH_File::remove_filename_from_filepath($full_dest_path);
914 914
         EEH_File::ensure_folder_exists_and_is_writable($folder);
915
-        if (! EEH_File::verify_is_writable($folder, 'folder')) {
915
+        if ( ! EEH_File::verify_is_writable($folder, 'folder')) {
916 916
             if (defined('WP_DEBUG') && WP_DEBUG) {
917 917
                 $msg = sprintf(
918 918
                     __('The file located at "%2$s" is not writable.', 'event_espresso'),
Please login to merge, or discard this patch.