Completed
Branch FET-10724-event-editor-cleanup (955656)
by
unknown
149:38 queued 136:37
created

EE_Capabilities::init_role_caps()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 10
nc 10
nop 2
dl 0
loc 20
rs 7.7777
c 0
b 0
f 0
1
<?php
2
defined('EVENT_ESPRESSO_VERSION') || exit('No direct script access allowed');
3
4
5
6
/**
7
 * This class contains all the code related to Event Espresso capabilities.
8
 * Assigned to the EE_Registry::instance()->CAP property.
9
 *
10
 * @link       https://github.com/eventespresso/event-espresso-core/tree/master/docs/K--Capability-System
11
 * @since      4.5.0
12
 * @package    Event Espresso
13
 * @subpackage core, capabilities
14
 * @author     Darren Ethier
15
 */
16
final class EE_Capabilities extends EE_Base
17
{
18
19
    /**
20
     * the name of the wp option used to store caps previously initialized
21
     */
22
    const option_name = 'ee_caps_initialized';
23
24
    /**
25
     * instance of EE_Capabilities object
26
     *
27
     * @var EE_Capabilities
28
     */
29
    private static $_instance;
30
31
    /**
32
     * This is a map of caps that correspond to a default WP_Role.
33
     * Array is indexed by Role and values are ee capabilities.
34
     *
35
     * @since 4.5.0
36
     * @var array
37
     */
38
    private $_caps_map = array();
39
40
    /**
41
     * This used to hold an array of EE_Meta_Capability_Map objects that define the granular capabilities mapped to for
42
     * a user depending on context.
43
     *
44
     * @var EE_Meta_Capability_Map[]
45
     */
46
    private $_meta_caps = array();
47
48
49
    /**
50
     * singleton method used to instantiate class object
51
     *
52
     * @since 4.5.0
53
     * @return EE_Capabilities
54
     */
55
    public static function instance()
56
    {
57
        //check if instantiated, and if not do so.
58
        if (! self::$_instance instanceof EE_Capabilities) {
59
            self::$_instance = new self();
60
        }
61
        return self::$_instance;
62
    }
63
64
65
    /**
66
     * private constructor
67
     *
68
     * @since 4.5.0
69
     */
70
    private function __construct()
71
    {
72
        if (is_admin()) {
73
            add_filter(
74
                'FHEE__EE_Capabilities__init_caps_map__caps',
75
                array($this, 'register_additional_capabilities'),
76
                10
77
            );
78
        }
79
    }
80
81
82
    /**
83
     * This delays the initialization of the capabilities class until EE_System core is loaded and ready.
84
     *
85
     * @param bool $reset allows for resetting the default capabilities saved on roles.  Note that this doesn't
86
     *                    actually REMOVE any capabilities from existing roles, it just resaves defaults roles and
87
     *                    ensures that they are up to date.
88
     * @since 4.5.0
89
     * @return void
90
     */
91
    public function init_caps($reset = false)
92
    {
93
        /**
94
         * Note, this means that caps can only initialized on the default roles when:
95
         * - models are queryable
96
         * - All addons have been registered  (which happens at plugins_loaded priority 1)
97
         * In practice, currently this method is usually called around `init`.
98
         */
99
        if (
100
            did_action('AHEE__EE_System__load_espresso_addons__complete')
101
            && EE_Maintenance_Mode::instance()->models_can_query()
102
        ) {
103
            $this->_caps_map = $this->_init_caps_map();
104
            $this->init_role_caps($reset);
105
            $this->_set_meta_caps();
106
        }
107
    }
108
109
110
    /**
111
     * This sets the meta caps property.
112
     *
113
     * @since 4.5.0
114
     * @return void
115
     */
116
    private function _set_meta_caps()
117
    {
118
        //make sure we're only ever initializing the default _meta_caps array once if it's empty.
119
        $this->_meta_caps = $this->_get_default_meta_caps_array();
120
        $this->_meta_caps = apply_filters('FHEE__EE_Capabilities___set_meta_caps__meta_caps', $this->_meta_caps);
121
        //add filter for map_meta_caps but only if models can query.
122
        if (! has_filter('map_meta_cap', array($this, 'map_meta_caps'))) {
123
            add_filter('map_meta_cap', array($this, 'map_meta_caps'), 10, 4);
124
        }
125
    }
126
127
128
    /**
129
     * This builds and returns the default meta_caps array only once.
130
     *
131
     * @since  4.8.28.rc.012
132
     * @return array
133
     * @throws \EE_Error
134
     */
135
    private function _get_default_meta_caps_array()
136
    {
137
        static $default_meta_caps = array();
138
        if (empty($default_meta_caps)) {
139
            $default_meta_caps = array(
140
                //edits
141
                new EE_Meta_Capability_Map_Edit(
142
                    'ee_edit_event',
143
                    array('Event', 'ee_edit_published_events', 'ee_edit_others_events', 'ee_edit_private_events')
144
                ),
145
                new EE_Meta_Capability_Map_Edit(
146
                    'ee_edit_venue',
147
                    array('Venue', 'ee_edit_published_venues', 'ee_edit_others_venues', 'ee_edit_private_venues')
148
                ),
149
                new EE_Meta_Capability_Map_Edit(
150
                    'ee_edit_registration',
151
                    array('Registration', '', 'ee_edit_others_registrations', '')
152
                ),
153
                new EE_Meta_Capability_Map_Edit(
154
                    'ee_edit_checkin',
155
                    array('Registration', '', 'ee_edit_others_checkins', '')
156
                ),
157
                new EE_Meta_Capability_Map_Messages_Cap(
158
                    'ee_edit_message',
159
                    array('Message_Template_Group', '', 'ee_edit_others_messages', 'ee_edit_global_messages')
160
                ),
161
                new EE_Meta_Capability_Map_Edit(
162
                    'ee_edit_default_ticket',
163
                    array('Ticket', '', 'ee_edit_others_default_tickets', '')
164
                ),
165
                new EE_Meta_Capability_Map_Registration_Form_Cap(
166
                    'ee_edit_question',
167
                    array('Question', '', '', 'ee_edit_system_questions')
168
                ),
169
                new EE_Meta_Capability_Map_Registration_Form_Cap(
170
                    'ee_edit_question_group',
171
                    array('Question_Group', '', '', 'ee_edit_system_question_groups')
172
                ),
173
                new EE_Meta_Capability_Map_Edit(
174
                    'ee_edit_payment_method',
175
                    array('Payment_Method', '', 'ee_edit_others_payment_methods', '')
176
                ),
177
                //reads
178
                new EE_Meta_Capability_Map_Read(
179
                    'ee_read_event',
180
                    array('Event', '', 'ee_read_others_events', 'ee_read_private_events')
181
                ),
182
                new EE_Meta_Capability_Map_Read(
183
                    'ee_read_venue',
184
                    array('Venue', '', 'ee_read_others_venues', 'ee_read_private_venues')
185
                ),
186
                new EE_Meta_Capability_Map_Read(
187
                    'ee_read_registration',
188
                    array('Registration', '', '', 'ee_edit_others_registrations')
189
                ),
190
                new EE_Meta_Capability_Map_Read(
191
                    'ee_read_checkin',
192
                    array('Registration', '', '', 'ee_read_others_checkins')
193
                ),
194
                new EE_Meta_Capability_Map_Messages_Cap(
195
                    'ee_read_message',
196
                    array('Message_Template_Group', '', 'ee_read_others_messages', 'ee_read_global_messages')
197
                ),
198
                new EE_Meta_Capability_Map_Read(
199
                    'ee_read_default_ticket',
200
                    array('Ticket', '', '', 'ee_read_others_default_tickets')
201
                ),
202
                new EE_Meta_Capability_Map_Read(
203
                    'ee_read_payment_method',
204
                    array('Payment_Method', '', '', 'ee_read_others_payment_methods')
205
                ),
206
                //deletes
207
                new EE_Meta_Capability_Map_Delete(
208
                    'ee_delete_event',
209
                    array(
210
                        'Event',
211
                        'ee_delete_published_events',
212
                        'ee_delete_others_events',
213
                        'ee_delete_private_events',
214
                    )
215
                ),
216
                new EE_Meta_Capability_Map_Delete(
217
                    'ee_delete_venue',
218
                    array(
219
                        'Venue',
220
                        'ee_delete_published_venues',
221
                        'ee_delete_others_venues',
222
                        'ee_delete_private_venues',
223
                    )
224
                ),
225
                new EE_Meta_Capability_Map_Delete(
226
                    'ee_delete_registration',
227
                    array('Registration', '', 'ee_delete_others_registrations', '')
228
                ),
229
                new EE_Meta_Capability_Map_Delete(
230
                    'ee_delete_checkin',
231
                    array('Registration', '', 'ee_delete_others_checkins', '')
232
                ),
233
                new EE_Meta_Capability_Map_Messages_Cap(
234
                    'ee_delete_message',
235
                    array('Message_Template_Group', '', 'ee_delete_others_messages', 'ee_delete_global_messages')
236
                ),
237
                new EE_Meta_Capability_Map_Delete(
238
                    'ee_delete_default_ticket',
239
                    array('Ticket', '', 'ee_delete_others_default_tickets', '')
240
                ),
241
                new EE_Meta_Capability_Map_Registration_Form_Cap(
242
                    'ee_delete_question',
243
                    array('Question', '', '', 'delete_system_questions')
244
                ),
245
                new EE_Meta_Capability_Map_Registration_Form_Cap(
246
                    'ee_delete_question_group',
247
                    array('Question_Group', '', '', 'delete_system_question_groups')
248
                ),
249
                new EE_Meta_Capability_Map_Delete(
250
                    'ee_delete_payment_method',
251
                    array('Payment_Method', '', 'ee_delete_others_payment_methods', '')
252
                ),
253
            );
254
        }
255
        return $default_meta_caps;
256
    }
257
258
259
    /**
260
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
261
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
262
     * The actual logic is carried out by implementer classes in their definition of _map_meta_caps.
263
     *
264
     * @since 4.5.0
265
     * @see   wp-includes/capabilities.php
266
     * @param array  $caps    actual users capabilities
267
     * @param string $cap     initial capability name that is being checked (the "map" key)
268
     * @param int    $user_id The user id
269
     * @param array  $args    Adds context to the cap. Typically the object ID.
270
     * @return array actual users capabilities
271
     * @throws EE_Error
272
     */
273
    public function map_meta_caps($caps, $cap, $user_id, $args)
274
    {
275
        if (did_action('AHEE__EE_System__load_espresso_addons__complete')) {
276
            //loop through our _meta_caps array
277
            foreach ($this->_meta_caps as $meta_map) {
278
                if (! $meta_map instanceof EE_Meta_Capability_Map) {
279
                    continue;
280
                }
281
                $meta_map->ensure_is_model();
282
                $caps = $meta_map->map_meta_caps($caps, $cap, $user_id, $args);
283
            }
284
        }
285
        return $caps;
286
    }
287
288
289
    /**
290
     * This sets up and returns the initial capabilities map for Event Espresso
291
     *
292
     * @since 4.5.0
293
     * @return array
294
     */
295
    private function _init_caps_map()
296
    {
297
        $caps = array(
298
            'administrator'           => array(
299
                //basic access
300
                'ee_read_ee',
301
                //gateways
302
                /**
303
                 * note that with payment method capabilities, although we've implemented
304
                 * capability mapping which will be used for accessing payment methods owned by
305
                 * other users.  This is not fully implemented yet in the payment method ui.
306
                 * Currently only the "plural" caps are in active use.
307
                 * (Specific payment method caps are in use as well).
308
                 **/
309
                'ee_manage_gateways',
310
                'ee_read_payment_method',
311
                'ee_read_payment_methods',
312
                'ee_read_others_payment_methods',
313
                'ee_edit_payment_method',
314
                'ee_edit_payment_methods',
315
                'ee_edit_others_payment_methods',
316
                'ee_delete_payment_method',
317
                'ee_delete_payment_methods',
318
                //events
319
                'ee_publish_events',
320
                'ee_read_private_events',
321
                'ee_read_others_events',
322
                'ee_read_event',
323
                'ee_read_events',
324
                'ee_edit_event',
325
                'ee_edit_events',
326
                'ee_edit_published_events',
327
                'ee_edit_others_events',
328
                'ee_edit_private_events',
329
                'ee_delete_published_events',
330
                'ee_delete_private_events',
331
                'ee_delete_event',
332
                'ee_delete_events',
333
                'ee_delete_others_events',
334
                //event categories
335
                'ee_manage_event_categories',
336
                'ee_edit_event_category',
337
                'ee_delete_event_category',
338
                'ee_assign_event_category',
339
                //venues
340
                'ee_publish_venues',
341
                'ee_read_venue',
342
                'ee_read_venues',
343
                'ee_read_others_venues',
344
                'ee_read_private_venues',
345
                'ee_edit_venue',
346
                'ee_edit_venues',
347
                'ee_edit_others_venues',
348
                'ee_edit_published_venues',
349
                'ee_edit_private_venues',
350
                'ee_delete_venue',
351
                'ee_delete_venues',
352
                'ee_delete_others_venues',
353
                'ee_delete_private_venues',
354
                'ee_delete_published_venues',
355
                //venue categories
356
                'ee_manage_venue_categories',
357
                'ee_edit_venue_category',
358
                'ee_delete_venue_category',
359
                'ee_assign_venue_category',
360
                //contacts
361
                'ee_read_contact',
362
                'ee_read_contacts',
363
                'ee_edit_contact',
364
                'ee_edit_contacts',
365
                'ee_delete_contact',
366
                'ee_delete_contacts',
367
                //registrations
368
                'ee_read_registration',
369
                'ee_read_registrations',
370
                'ee_read_others_registrations',
371
                'ee_edit_registration',
372
                'ee_edit_registrations',
373
                'ee_edit_others_registrations',
374
                'ee_delete_registration',
375
                'ee_delete_registrations',
376
                //checkins
377
                'ee_read_checkin',
378
                'ee_read_others_checkins',
379
                'ee_read_checkins',
380
                'ee_edit_checkin',
381
                'ee_edit_checkins',
382
                'ee_edit_others_checkins',
383
                'ee_delete_checkin',
384
                'ee_delete_checkins',
385
                'ee_delete_others_checkins',
386
                //transactions && payments
387
                'ee_read_transaction',
388
                'ee_read_transactions',
389
                'ee_edit_payments',
390
                'ee_delete_payments',
391
                //messages
392
                'ee_read_message',
393
                'ee_read_messages',
394
                'ee_read_others_messages',
395
                'ee_read_global_messages',
396
                'ee_edit_global_messages',
397
                'ee_edit_message',
398
                'ee_edit_messages',
399
                'ee_edit_others_messages',
400
                'ee_delete_message',
401
                'ee_delete_messages',
402
                'ee_delete_others_messages',
403
                'ee_delete_global_messages',
404
                'ee_send_message',
405
                //tickets
406
                'ee_read_default_ticket',
407
                'ee_read_default_tickets',
408
                'ee_read_others_default_tickets',
409
                'ee_edit_default_ticket',
410
                'ee_edit_default_tickets',
411
                'ee_edit_others_default_tickets',
412
                'ee_delete_default_ticket',
413
                'ee_delete_default_tickets',
414
                'ee_delete_others_default_tickets',
415
                //prices
416
                'ee_edit_default_price',
417
                'ee_edit_default_prices',
418
                'ee_delete_default_price',
419
                'ee_delete_default_prices',
420
                'ee_edit_default_price_type',
421
                'ee_edit_default_price_types',
422
                'ee_delete_default_price_type',
423
                'ee_delete_default_price_types',
424
                'ee_read_default_prices',
425
                'ee_read_default_price_types',
426
                //registration form
427
                'ee_edit_question',
428
                'ee_edit_questions',
429
                'ee_edit_system_questions',
430
                'ee_read_questions',
431
                'ee_delete_question',
432
                'ee_delete_questions',
433
                'ee_edit_question_group',
434
                'ee_edit_question_groups',
435
                'ee_read_question_groups',
436
                'ee_edit_system_question_groups',
437
                'ee_delete_question_group',
438
                'ee_delete_question_groups',
439
                //event_type taxonomy
440
                'ee_assign_event_type',
441
                'ee_manage_event_types',
442
                'ee_edit_event_type',
443
                'ee_delete_event_type',
444
            ),
445
            'ee_events_administrator' => array(
446
                //core wp caps
447
                'read',
448
                'read_private_pages',
449
                'read_private_posts',
450
                'edit_users',
451
                'edit_posts',
452
                'edit_pages',
453
                'edit_published_posts',
454
                'edit_published_pages',
455
                'edit_private_pages',
456
                'edit_private_posts',
457
                'edit_others_posts',
458
                'edit_others_pages',
459
                'publish_posts',
460
                'publish_pages',
461
                'delete_posts',
462
                'delete_pages',
463
                'delete_private_pages',
464
                'delete_private_posts',
465
                'delete_published_pages',
466
                'delete_published_posts',
467
                'delete_others_posts',
468
                'delete_others_pages',
469
                'manage_categories',
470
                'manage_links',
471
                'moderate_comments',
472
                'unfiltered_html',
473
                'upload_files',
474
                'export',
475
                'import',
476
                'list_users',
477
                'level_1', //required if user with this role shows up in author dropdowns
478
                //basic ee access
479
                'ee_read_ee',
480
                //events
481
                'ee_publish_events',
482
                'ee_read_private_events',
483
                'ee_read_others_events',
484
                'ee_read_event',
485
                'ee_read_events',
486
                'ee_edit_event',
487
                'ee_edit_events',
488
                'ee_edit_published_events',
489
                'ee_edit_others_events',
490
                'ee_edit_private_events',
491
                'ee_delete_published_events',
492
                'ee_delete_private_events',
493
                'ee_delete_event',
494
                'ee_delete_events',
495
                'ee_delete_others_events',
496
                //event categories
497
                'ee_manage_event_categories',
498
                'ee_edit_event_category',
499
                'ee_delete_event_category',
500
                'ee_assign_event_category',
501
                //venues
502
                'ee_publish_venues',
503
                'ee_read_venue',
504
                'ee_read_venues',
505
                'ee_read_others_venues',
506
                'ee_read_private_venues',
507
                'ee_edit_venue',
508
                'ee_edit_venues',
509
                'ee_edit_others_venues',
510
                'ee_edit_published_venues',
511
                'ee_edit_private_venues',
512
                'ee_delete_venue',
513
                'ee_delete_venues',
514
                'ee_delete_others_venues',
515
                'ee_delete_private_venues',
516
                'ee_delete_published_venues',
517
                //venue categories
518
                'ee_manage_venue_categories',
519
                'ee_edit_venue_category',
520
                'ee_delete_venue_category',
521
                'ee_assign_venue_category',
522
                //contacts
523
                'ee_read_contact',
524
                'ee_read_contacts',
525
                'ee_edit_contact',
526
                'ee_edit_contacts',
527
                'ee_delete_contact',
528
                'ee_delete_contacts',
529
                //registrations
530
                'ee_read_registration',
531
                'ee_read_registrations',
532
                'ee_read_others_registrations',
533
                'ee_edit_registration',
534
                'ee_edit_registrations',
535
                'ee_edit_others_registrations',
536
                'ee_delete_registration',
537
                'ee_delete_registrations',
538
                //checkins
539
                'ee_read_checkin',
540
                'ee_read_others_checkins',
541
                'ee_read_checkins',
542
                'ee_edit_checkin',
543
                'ee_edit_checkins',
544
                'ee_edit_others_checkins',
545
                'ee_delete_checkin',
546
                'ee_delete_checkins',
547
                'ee_delete_others_checkins',
548
                //transactions && payments
549
                'ee_read_transaction',
550
                'ee_read_transactions',
551
                'ee_edit_payments',
552
                'ee_delete_payments',
553
                //messages
554
                'ee_read_message',
555
                'ee_read_messages',
556
                'ee_read_others_messages',
557
                'ee_read_global_messages',
558
                'ee_edit_global_messages',
559
                'ee_edit_message',
560
                'ee_edit_messages',
561
                'ee_edit_others_messages',
562
                'ee_delete_message',
563
                'ee_delete_messages',
564
                'ee_delete_others_messages',
565
                'ee_delete_global_messages',
566
                'ee_send_message',
567
                //tickets
568
                'ee_read_default_ticket',
569
                'ee_read_default_tickets',
570
                'ee_read_others_default_tickets',
571
                'ee_edit_default_ticket',
572
                'ee_edit_default_tickets',
573
                'ee_edit_others_default_tickets',
574
                'ee_delete_default_ticket',
575
                'ee_delete_default_tickets',
576
                'ee_delete_others_default_tickets',
577
                //prices
578
                'ee_edit_default_price',
579
                'ee_edit_default_prices',
580
                'ee_delete_default_price',
581
                'ee_delete_default_prices',
582
                'ee_edit_default_price_type',
583
                'ee_edit_default_price_types',
584
                'ee_delete_default_price_type',
585
                'ee_delete_default_price_types',
586
                'ee_read_default_prices',
587
                'ee_read_default_price_types',
588
                //registration form
589
                'ee_edit_question',
590
                'ee_edit_questions',
591
                'ee_edit_system_questions',
592
                'ee_read_questions',
593
                'ee_delete_question',
594
                'ee_delete_questions',
595
                'ee_edit_question_group',
596
                'ee_edit_question_groups',
597
                'ee_read_question_groups',
598
                'ee_edit_system_question_groups',
599
                'ee_delete_question_group',
600
                'ee_delete_question_groups',
601
                //event_type taxonomy
602
                'ee_assign_event_type',
603
                'ee_manage_event_types',
604
                'ee_edit_event_type',
605
                'ee_delete_event_type',
606
            ),
607
        );
608
        $caps = apply_filters('FHEE__EE_Capabilities__init_caps_map__caps', $caps);
609
        return $caps;
610
    }
611
612
613
    /**
614
     * Callback for FHEE__EE_Capabilities__init_caps_map__caps that is used for registering additional core
615
     * capabilities that get added.
616
     * This is typically done for more dynamic cap additions such as what is registered via the
617
     * `EE_Payment_Method_Manager`
618
     *
619
     * @param array $caps The existing $role=>$capability array.
620
     * @return array.
621
     */
622
    public function register_additional_capabilities($caps)
623
    {
624
        //take care of dynamic capabilities for payment methods
625
        EE_Registry::instance()->load_lib('Payment_Method_Manager');
626
        $caps = EE_Payment_Method_Manager::instance()->add_payment_method_caps($caps);
627
        return $caps;
628
    }
629
630
631
    /**
632
     * This adds all the default caps to roles as registered in the _caps_map property.
633
     *
634
     * @since 4.5.0
635
     * @param bool  $reset      allows for resetting the default capabilities saved on roles.  Note that this doesn't
636
     *                          actually REMOVE any capabilities from existing roles, it just resaves defaults roles
637
     *                          and ensures that they are up to date.
638
     * @param array $custom_map Optional.  Can be used to send a custom map of roles and capabilities for setting them
639
     *                          up.  Note that this should ONLY be called on activation hook or some other one-time
640
     *                          task otherwise the caps will be added on every request.
641
     * @return void
642
     */
643
    public function init_role_caps($reset = false, $custom_map = array())
644
    {
645
        $caps_map = empty($custom_map) ? $this->_caps_map : $custom_map;
646
        //first let's determine if these caps have already been set.
647
        $caps_set_before = get_option(self::option_name, array());
648
        //if not reset, see what caps are new for each role. if they're new, add them.
649
        foreach ($caps_map as $role => $caps_for_role) {
650
            foreach ($caps_for_role as $cap) {
651
                //first check we haven't already added this cap before, or it's a reset
652
                if ($reset || ! isset($caps_set_before[ $role ]) || ! in_array($cap, $caps_set_before[ $role ])) {
653
                    if ($this->add_cap_to_role($role, $cap)) {
654
                        $caps_set_before[ $role ][] = $cap;
655
                    }
656
                }
657
            }
658
        }
659
        //now let's just save the cap that has been set.
660
        update_option(self::option_name, $caps_set_before);
661
        do_action('AHEE__EE_Capabilities__init_role_caps__complete', $caps_set_before);
662
    }
663
664
665
    /**
666
     * This method sets a capability on a role.  Note this should only be done on activation, or if you have something
667
     * specific to prevent the cap from being added on every page load (adding caps are persistent to the db). Note.
668
     * this is a wrapper for $wp_role->add_cap()
669
     *
670
     * @see   wp-includes/capabilities.php
671
     * @since 4.5.0
672
     * @param string $role  A WordPress role the capability is being added to
673
     * @param string $cap   The capability being added to the role
674
     * @param bool   $grant Whether to grant access to this cap on this role.
675
     * @return bool
676
     */
677
    public function add_cap_to_role($role, $cap, $grant = true)
678
    {
679
        $role_object = get_role($role);
680
        //if the role isn't available then we create it.
681
        if (! $role_object instanceof WP_Role) {
682
            //if a plugin wants to create a specific role name then they should create the role before
683
            //EE_Capabilities does.  Otherwise this function will create the role name from the slug:
684
            // - removes any `ee_` namespacing from the start of the slug.
685
            // - replaces `_` with ` ` (empty space).
686
            // - sentence case on the resulting string.
687
            $role_label = ucwords(str_replace('_', ' ', str_replace('ee_', '', $role)));
688
            $role_object = add_role($role, $role_label);
689
        }
690
        if ($role_object instanceof WP_Role) {
691
            $role_object->add_cap($cap, $grant);
692
            return true;
693
        }
694
        return false;
695
    }
696
697
698
    /**
699
     * Functions similarly to add_cap_to_role except removes cap from given role.
700
     * Wrapper for $wp_role->remove_cap()
701
     *
702
     * @see   wp-includes/capabilities.php
703
     * @since 4.5.0
704
     * @param string $role A WordPress role the capability is being removed from.
705
     * @param string $cap  The capability being removed
706
     * @return void
707
     */
708
    public function remove_cap_from_role($role, $cap)
709
    {
710
        $role = get_role($role);
711
        if ($role instanceof WP_Role) {
712
            $role->remove_cap($cap);
713
        }
714
    }
715
716
717
    /**
718
     * Wrapper for the native WP current_user_can() method.
719
     * This is provided as a handy method for a couple things:
720
     * 1. Using the context string it allows for targeted filtering by addons for a specific check (without having to
721
     * write those filters wherever current_user_can is called).
722
     * 2. Explicit passing of $id from a given context ( useful in the cases of map_meta_cap filters )
723
     *
724
     * @since 4.5.0
725
     * @param string $cap     The cap being checked.
726
     * @param string $context The context where the current_user_can is being called from.
727
     * @param int    $id      Optional. Id for item where current_user_can is being called from (used in map_meta_cap()
728
     *                        filters.
729
     * @return bool  Whether user can or not.
730
     */
731
    public function current_user_can($cap, $context, $id = 0)
732
    {
733
        //apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
734
        $filtered_cap = apply_filters('FHEE__EE_Capabilities__current_user_can__cap__' . $context, $cap, $id);
735
        $filtered_cap = apply_filters('FHEE__EE_Capabilities__current_user_can__cap', $filtered_cap, $context, $cap,
736
            $id);
737
        return ! empty($id) ? current_user_can($filtered_cap, $id) : current_user_can($filtered_cap);
738
    }
739
740
741
    /**
742
     * This is a wrapper for the WP user_can() function and follows the same style as the other wrappers in this class.
743
     *
744
     * @param int|WP_User $user    Either the user_id or a WP_User object
745
     * @param string      $cap     The capability string being checked
746
     * @param string      $context The context where the user_can is being called from (used in filters).
747
     * @param int         $id      Optional. Id for item where user_can is being called from ( used in map_meta_cap()
748
     *                             filters)
749
     * @return bool Whether user can or not.
750
     */
751
    public function user_can($user, $cap, $context, $id = 0)
752
    {
753
        //apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
754
        $filtered_cap = apply_filters('FHEE__EE_Capabilities__user_can__cap__' . $context, $cap, $user, $id);
755
        $filtered_cap = apply_filters('FHEE__EE_Capabilities__user_can__cap', $filtered_cap, $context, $cap, $user,
756
            $id);
757
        return ! empty($id) ? user_can($user, $filtered_cap, $id) : user_can($user, $filtered_cap);
758
    }
759
760
761
    /**
762
     * Wrapper for the native WP current_user_can_for_blog() method.
763
     * This is provided as a handy method for a couple things:
764
     * 1. Using the context string it allows for targeted filtering by addons for a specific check (without having to
765
     * write those filters wherever current_user_can is called).
766
     * 2. Explicit passing of $id from a given context ( useful in the cases of map_meta_cap filters )
767
     *
768
     * @since 4.5.0
769
     * @param int    $blog_id The blog id that is being checked for.
770
     * @param string $cap     The cap being checked.
771
     * @param string $context The context where the current_user_can is being called from.
772
     * @param int    $id      Optional. Id for item where current_user_can is being called from (used in map_meta_cap()
773
     *                        filters.
774
     * @return bool  Whether user can or not.
775
     */
776
    public function current_user_can_for_blog($blog_id, $cap, $context, $id = 0)
777
    {
778
        $user_can = ! empty($id)
779
            ? current_user_can_for_blog($blog_id, $cap, $id)
780
            : current_user_can($blog_id, $cap);
781
        //apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
782
        $user_can = apply_filters(
783
            'FHEE__EE_Capabilities__current_user_can_for_blog__user_can__' . $context,
784
            $user_can,
785
            $blog_id,
786
            $cap,
787
            $id
788
        );
789
        $user_can = apply_filters(
790
            'FHEE__EE_Capabilities__current_user_can_for_blog__user_can',
791
            $user_can,
792
            $context,
793
            $blog_id,
794
            $cap,
795
            $id
796
        );
797
        return $user_can;
798
    }
799
800
801
    /**
802
     * This helper method just returns an array of registered EE capabilities.
803
     * Note this array is filtered.  It is assumed that all available EE capabilities are assigned to the administrator
804
     * role.
805
     *
806
     * @since 4.5.0
807
     * @param string $role If empty then the entire role/capability map is returned.  Otherwise just the capabilities
808
     *                     for the given role are returned.
809
     * @return array
810
     */
811
    public function get_ee_capabilities($role = 'administrator')
812
    {
813
        $capabilities = $this->_init_caps_map();
814
        if (empty($role)) {
815
            return $capabilities;
816
        }
817
        return isset($capabilities[ $role ]) ? $capabilities[ $role ] : array();
818
    }
819
}
820
821
822
/**
823
 * Meta Capability Map class.
824
 * This children of this class are used to define capability mappings for capabilities that have further filtering
825
 * depending on context.
826
 *
827
 * @since      4.5.0
828
 * @package    Event Espresso
829
 * @subpackage core, capabilities
830
 * @author     Darren Ethier
831
 */
832
abstract class EE_Meta_Capability_Map
833
{
834
835
    public $meta_cap;
836
837
    /**
838
     * @var EEM_Base
839
     */
840
    protected $_model;
841
842
    protected $_model_name;
843
844
    public $published_cap = '';
845
846
    public $others_cap = '';
847
848
    public $private_cap = '';
849
850
851
    /**
852
     * constructor.
853
     * Receives the setup arguments for the map.
854
     *
855
     * @since                        4.5.0
856
     * @param string $meta_cap       What meta capability is this mapping.
857
     * @param array  $map_values     array {
858
     *                               //array of values that MUST match a count of 4.  It's okay to send an empty string
859
     *                               for capabilities that don't get mapped to.
860
     * @type         $map_values     [0] string A string representing the model name. Required.  String's
861
     *                               should always be used when Menu Maps are registered via the
862
     *                               plugin API as models are not allowed to be instantiated when
863
     *                               in maintenance mode 2 (migrations).
864
     * @type         $map_values     [1] string represents the capability used for published. Optional.
865
     * @type         $map_values     [2] string represents the capability used for "others". Optional.
866
     * @type         $map_values     [3] string represents the capability used for private. Optional.
867
     *                               }
868
     * @throws EE_Error
869
     */
870
    public function __construct($meta_cap, $map_values)
871
    {
872
        $this->meta_cap = $meta_cap;
873
        //verify there are four args in the $map_values array;
874
        if (count($map_values) !== 4) {
875
            throw new EE_Error(
876
                sprintf(
877
                    __(
878
                        'Incoming $map_values array should have a count of four values in it.  This is what was given: %s',
879
                        'event_espresso'
880
                    ),
881
                    '<br>' . print_r($map_values, true)
882
                )
883
            );
884
        }
885
        //set properties
886
        $this->_model = null;
887
        $this->_model_name = $map_values[0];
888
        $this->published_cap = (string)$map_values[1];
889
        $this->others_cap = (string)$map_values[2];
890
        $this->private_cap = (string)$map_values[3];
891
    }
892
893
    /**
894
     * Makes it so this object stops filtering caps
895
     */
896
    public function remove_filters()
897
    {
898
        remove_filter('map_meta_cap', array($this, 'map_meta_caps'), 10);
899
    }
900
901
902
    /**
903
     * This method ensures that the $model property is converted from the model name string to a proper EEM_Base class
904
     *
905
     * @since 4.5.0
906
     * @throws EE_Error
907
     * @return void
908
     */
909
    public function ensure_is_model()
910
    {
911
        //is it already instantiated?
912
        if ($this->_model instanceof EEM_Base) {
913
            return;
914
        }
915
        //ensure model name is string
916
        $this->_model_name = (string)$this->_model_name;
917
        //error proof if the name has EEM in it
918
        $this->_model_name = str_replace('EEM', '', $this->_model_name);
919
        $this->_model = EE_Registry::instance()->load_model($this->_model_name);
920 View Code Duplication
        if (! $this->_model instanceof EEM_Base) {
921
            throw new EE_Error(
922
                sprintf(
923
                    __(
924
                        'This string passed in to %s to represent a EEM_Base model class was not able to be used to instantiate the class.   Please ensure that the string is a match for the EEM_Base model name (not including the EEM_ part). This was given: %s',
925
                        'event_espresso'
926
                    ),
927
                    get_class($this),
928
                    $this->_model
929
                )
930
            );
931
        }
932
    }
933
934
935
    /**
936
     * @see   EE_Meta_Capability_Map::_map_meta_caps() for docs on params.
937
     * @since 4.6.x
938
     * @param $caps
939
     * @param $cap
940
     * @param $user_id
941
     * @param $args
942
     * @return array
943
     */
944
    public function map_meta_caps($caps, $cap, $user_id, $args)
945
    {
946
        return $this->_map_meta_caps($caps, $cap, $user_id, $args);
947
    }
948
949
950
    /**
951
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
952
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
953
     *
954
     * @since 4.5.0
955
     * @see   wp-includes/capabilities.php
956
     * @param array  $caps    actual users capabilities
957
     * @param string $cap     initial capability name that is being checked (the "map" key)
958
     * @param int    $user_id The user id
959
     * @param array  $args    Adds context to the cap. Typically the object ID.
960
     * @return array   actual users capabilities
961
     */
962
    abstract protected function _map_meta_caps($caps, $cap, $user_id, $args);
963
}
964
965
966
/**
967
 * Meta Capability Map class for Edit type capabilities.
968
 * Any capability that is an edit type of capability utilizes this map.
969
 *
970
 * @since      4.5.0
971
 * @package    Event Espresso
972
 * @subpackage core, capabilities
973
 * @author     Darren Ethier
974
 */
975
class EE_Meta_Capability_Map_Edit extends EE_Meta_Capability_Map
976
{
977
978
    /**
979
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
980
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
981
     *
982
     * @since 4.5.0
983
     * @see   wp-includes/capabilities.php
984
     * @param array  $caps    actual users capabilities
985
     * @param string $cap     initial capability name that is being checked (the "map" key)
986
     * @param int    $user_id The user id
987
     * @param array  $args    Adds context to the cap. Typically the object ID.
988
     * @return array   actual users capabilities
989
     */
990
    protected function _map_meta_caps($caps, $cap, $user_id, $args)
991
    {
992
        //only process if we're checking our mapped_cap
993
        if ($cap !== $this->meta_cap) {
994
            return $caps;
995
        }
996
        /** @var EE_Base_Class $obj */
997
        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
998
        //if no obj then let's just do cap
999
        if (! $obj instanceof EE_Base_Class) {
1000
            $caps[] = $cap;
1001
            return $caps;
1002
        }
1003
        if ($obj instanceof EE_CPT_Base) {
1004
            //if the item author is set and the user is the author...
1005
            if ($obj->wp_user() && $user_id == $obj->wp_user()) {
1006
                if (empty($this->published_cap)) {
1007
                    $caps[] = $cap;
1008
                } else {
1009
                    //if obj is published...
1010
                    if ($obj->status() === 'publish') {
1011
                        $caps[] = $this->published_cap;
1012
                    } else {
1013
                        $caps[] = $cap;
1014
                    }
1015
                }
1016
            } else {
1017
                //the user is trying to edit someone else's obj
1018
                if (! empty($this->others_cap)) {
1019
                    $caps[] = $this->others_cap;
1020
                }
1021
                if (! empty($this->published_cap) && $obj->status() === 'publish') {
1022
                    $caps[] = $this->published_cap;
1023
                } elseif (! empty($this->private_cap) && $obj->status() === 'private') {
1024
                    $caps[] = $this->private_cap;
1025
                }
1026
            }
1027
        } else {
1028
            //not a cpt object so handled differently
1029
            $has_cap = false;
1030
            try {
1031
                $has_cap = method_exists($obj, 'wp_user')
1032
                    && $obj->wp_user()
1033
                    && $obj->wp_user() === $user_id;
1034
            } catch (Exception $e) {
1035
                if (WP_DEBUG) {
1036
                    EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1037
                }
1038
            }
1039
            if ($has_cap) {
1040
                $caps[] = $cap;
1041
            } else {
1042
                if (! empty($this->others_cap)) {
1043
                    $caps[] = $this->others_cap;
1044
                }
1045
            }
1046
        }
1047
        return $caps;
1048
    }
1049
}
1050
1051
1052
/**
1053
 * Meta Capability Map class for delete type capabilities
1054
 * Merely extends the Edit map.  Intention is for type hinting so it's clear a capability is a "delete" type of
1055
 * capability (in case mapping needs to change in the future)
1056
 *
1057
 * @since      4.5.0
1058
 * @package    Event Espresso
1059
 * @subpackage core, capabilities
1060
 * @author     Darren Ethier
1061
 */
1062
class EE_Meta_Capability_Map_Delete extends EE_Meta_Capability_Map_Edit
1063
{
1064
1065
    /**
1066
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1067
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1068
     *
1069
     * @since 4.5.0
1070
     * @see   wp-includes/capabilities.php
1071
     * @param array  $caps    actual users capabilities
1072
     * @param string $cap     initial capability name that is being checked (the "map" key)
1073
     * @param int    $user_id The user id
1074
     * @param array  $args    Adds context to the cap. Typically the object ID.
1075
     * @return array   actual users capabilities
1076
     */
1077
    protected function _map_meta_caps($caps, $cap, $user_id, $args)
1078
    {
1079
        return parent::_map_meta_caps($caps, $cap, $user_id, $args);
1080
    }
1081
}
1082
1083
1084
/**
1085
 * Meta Capability Map class for reads.
1086
 * Maps any read meta capabilities to equivalents for context.
1087
 *
1088
 * @since      4.5.0
1089
 * @package    Event Espresso
1090
 * @subpackage core, capabilities
1091
 * @author     Darren Ethier
1092
 */
1093
class EE_Meta_Capability_Map_Read extends EE_Meta_Capability_Map
1094
{
1095
1096
    /**
1097
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1098
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1099
     *
1100
     * @since 4.5.0
1101
     * @see   wp-includes/capabilities.php
1102
     * @param array  $caps    actual users capabilities
1103
     * @param string $cap     initial capability name that is being checked (the "map" key)
1104
     * @param int    $user_id The user id
1105
     * @param array  $args    Adds context to the cap. Typically the object ID.
1106
     * @return array   actual users capabilities
1107
     */
1108
    protected function _map_meta_caps($caps, $cap, $user_id, $args)
1109
    {
1110
        //only process if we're checking our mapped cap;
1111
        if ($cap !== $this->meta_cap) {
1112
            return $caps;
1113
        }
1114
        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1115
        //if no obj then let's just do cap
1116
        if (! $obj instanceof EE_Base_Class) {
1117
            $caps[] = $cap;
1118
            return $caps;
1119
        }
1120
        if ($obj instanceof EE_CPT_Base) {
1121
            $status_obj = get_post_status_object($obj->status());
1122
            if ($status_obj->public) {
1123
                $caps[] = $cap;
1124
                return $caps;
1125
            }
1126
            //if the item author is set and the user is the author...
1127
            if ($obj->wp_user() && $obj->wp_user() === $user_id) {
1128
                $caps[] = $cap;
1129
            } elseif ($status_obj->private && ! empty($this->private_cap)) {
1130
                //the user is trying to view someone else's obj
1131
                $caps[] = $this->private_cap;
1132
            } elseif (! empty($this->others_cap)) {
1133
                $caps[] = $this->others_cap;
1134
            } else {
1135
                $caps[] = $cap;
1136
            }
1137
        } else {
1138
            //not a cpt object so handled differently
1139
            $has_cap = false;
1140
            try {
1141
                $has_cap = method_exists($obj, 'wp_user') && $obj->wp_user() && $obj->wp_user() === $user_id;
1142
            } catch (Exception $e) {
1143
                if (WP_DEBUG) {
1144
                    EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1145
                }
1146
            }
1147
            if ($has_cap) {
1148
                $caps[] = $cap;
1149
            } elseif (! empty($this->private_cap)) {
1150
                $caps[] = $this->private_cap;
1151
            } elseif (! empty($this->others_cap)) {
1152
                $caps[] = $this->others_cap;
1153
            } else {
1154
                $caps[] = $cap;
1155
            }
1156
        }
1157
        return $caps;
1158
    }
1159
}
1160
1161
1162
/**
1163
 * Meta Capability Map class for the messages component
1164
 * This is a special map due to messages having global and custom messages.  Only users with the edit_global_message
1165
 * capability should be able to do things with the global messages.
1166
 *
1167
 * @since      4.5.0
1168
 * @package    Event Espresso
1169
 * @subpackage core, capabilities
1170
 * @author     Darren Ethier
1171
 */
1172
class EE_Meta_Capability_Map_Messages_Cap extends EE_Meta_Capability_Map
1173
{
1174
1175
    /**
1176
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1177
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1178
     *
1179
     * @since 4.5.0
1180
     * @see   wp-includes/capabilities.php
1181
     * @param array  $caps    actual users capabilities
1182
     * @param string $cap     initial capability name that is being checked (the "map" key)
1183
     * @param int    $user_id The user id
1184
     * @param array  $args    Adds context to the cap. Typically the object ID.
1185
     * @return array   actual users capabilities
1186
     */
1187
    protected function _map_meta_caps($caps, $cap, $user_id, $args)
1188
    {
1189
        //only process if we're checking our mapped_cap
1190
        if ($cap !== $this->meta_cap) {
1191
            return $caps;
1192
        }
1193
        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1194
        //if no obj then let's just do cap
1195
        if (! $obj instanceof EE_Message_Template_Group) {
1196
            $caps[] = $cap;
1197
            return $caps;
1198
        }
1199
        $is_global = $obj->is_global();
1200
        if ($obj->wp_user() && $obj->wp_user() === $user_id) {
1201
            if ($is_global) {
1202
                $caps[] = $this->private_cap;
1203
            } else {
1204
                $caps[] = $cap;
1205
            }
1206
        } else {
1207
            if ($is_global) {
1208
                $caps[] = $this->private_cap;
1209
            } else {
1210
                $caps[] = $this->others_cap;
1211
            }
1212
        }
1213
        return $caps;
1214
    }
1215
}
1216
1217
1218
/**
1219
 * Meta Capability Map class for the registration form (questions and question groups) component
1220
 * This is a special map due to questions and question groups having special "system" data.  Only users with the
1221
 * edit_system_question or edit_system_question_group capability should be able to do things with the system data.
1222
 *
1223
 * @since      4.5.0
1224
 * @package    Event Espresso
1225
 * @subpackage core, capabilities
1226
 * @author     Darren Ethier
1227
 */
1228
class EE_Meta_Capability_Map_Registration_Form_Cap extends EE_Meta_Capability_Map
1229
{
1230
1231
    /**
1232
     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1233
     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1234
     *
1235
     * @since 4.5.0
1236
     * @see   wp-includes/capabilities.php
1237
     * @param array  $caps    actual users capabilities
1238
     * @param string $cap     initial capability name that is being checked (the "map" key)
1239
     * @param int    $user_id The user id
1240
     * @param array  $args    Adds context to the cap. Typically the object ID.
1241
     * @return array   actual users capabilities
1242
     */
1243
    protected function _map_meta_caps($caps, $cap, $user_id, $args)
1244
    {
1245
        //only process if we're checking our mapped_cap
1246
        if ($cap !== $this->meta_cap) {
1247
            return $caps;
1248
        }
1249
        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1250
        //if no obj then let's just do cap
1251
        if (! $obj instanceof EE_Base_Class) {
1252
            $caps[] = $cap;
1253
            return $caps;
1254
        }
1255
        $is_system = $obj instanceof EE_Question_Group ? $obj->system_group() : false;
1256
        $is_system = $obj instanceof EE_Question ? $obj->is_system_question() : $is_system;
1257
        if ($is_system) {
1258
            $caps[] = $this->private_cap;
1259
        } else {
1260
            $caps[] = $cap;
1261
        }
1262
        return $caps;
1263
    }
1264
1265
1266
}
1267