Make WordPress Core

Ticket #16956: capabilities.php

File capabilities.php, 39.5 KB (added by codeelite, 10 years ago)
Line 
1<?php
2/**
3 * WordPress Roles and Capabilities.
4 *
5 * @package WordPress
6 * @subpackage User
7 */
8
9/**
10 * WordPress User Roles.
11 *
12 * The role option is simple, the structure is organized by role name that store
13 * the name in value of the 'name' key. The capabilities are stored as an array
14 * in the value of the 'capability' key.
15 *
16 *     array (
17 *              'rolename' => array (
18 *                      'name' => 'rolename',
19 *                      'capabilities' => array()
20 *              )
21 *     )
22 *
23 * @since 2.0.0
24 * @package WordPress
25 * @subpackage User
26 */
27class WP_Roles {
28        /**
29         * List of roles and capabilities.
30         *
31         * @since 2.0.0
32         * @access public
33         * @var array
34         */
35        public $roles;
36
37        /**
38         * List of the role objects.
39         *
40         * @since 2.0.0
41         * @access public
42         * @var array
43         */
44        public $role_objects = array();
45
46        /**
47         * List of role names.
48         *
49         * @since 2.0.0
50         * @access public
51         * @var array
52         */
53        public $role_names = array();
54
55        /**
56         * Option name for storing role list.
57         *
58         * @since 2.0.0
59         * @access public
60         * @var string
61         */
62        public $role_key;
63
64        /**
65         * Whether to use the database for retrieval and storage.
66         *
67         * @since 2.1.0
68         * @access public
69         * @var bool
70         */
71        public $use_db = true;
72
73        /**
74         * Constructor
75         *
76         * @since 2.0.0
77         */
78        public function __construct() {
79                $this->_init();
80        }
81
82        /**
83         * Make private/protected methods readable for backwards compatibility.
84         *
85         * @since 4.0.0
86         * @access public
87         *
88         * @param callable $name      Method to call.
89         * @param array    $arguments Arguments to pass when calling.
90         * @return mixed|bool Return value of the callback, false otherwise.
91         */
92        public function __call( $name, $arguments ) {
93                if ( '_init' === $name ) {
94                        return call_user_func_array( array( $this, $name ), $arguments );
95                }
96                return false;
97        }
98
99        /**
100         * Set up the object properties.
101         *
102         * The role key is set to the current prefix for the $wpdb object with
103         * 'user_roles' appended. If the $wp_user_roles global is set, then it will
104         * be used and the role option will not be updated or used.
105         *
106         * @since 2.1.0
107         * @access protected
108         *
109         * @global wpdb  $wpdb          WordPress database abstraction object.
110         * @global array $wp_user_roles Used to set the 'roles' property value.
111         */
112        protected function _init() {
113                global $wpdb, $wp_user_roles;
114                $this->role_key = $wpdb->get_blog_prefix() . 'user_roles';
115                if ( ! empty( $wp_user_roles ) ) {
116                        $this->roles = $wp_user_roles;
117                        $this->use_db = false;
118                } else {
119                        $this->roles = get_option( $this->role_key );
120                }
121
122                if ( empty( $this->roles ) )
123                        return;
124
125                $this->role_objects = array();
126                $this->role_names =  array();
127                foreach ( array_keys( $this->roles ) as $role ) {
128                        $this->role_objects[$role] = new WP_Role( $role, $this->roles[$role]['capabilities'] );
129                        $this->role_names[$role] = $this->roles[$role]['name'];
130                }
131        }
132
133        /**
134         * Reinitialize the object
135         *
136         * Recreates the role objects. This is typically called only by switch_to_blog()
137         * after switching wpdb to a new blog ID.
138         *
139         * @since 3.5.0
140         * @access public
141         */
142        public function reinit() {
143                // There is no need to reinit if using the wp_user_roles global.
144                if ( ! $this->use_db )
145                        return;
146
147                global $wpdb;
148
149                // Duplicated from _init() to avoid an extra function call.
150                $this->role_key = $wpdb->get_blog_prefix() . 'user_roles';
151                $this->roles = get_option( $this->role_key );
152                if ( empty( $this->roles ) )
153                        return;
154
155                $this->role_objects = array();
156                $this->role_names =  array();
157                foreach ( array_keys( $this->roles ) as $role ) {
158                        $this->role_objects[$role] = new WP_Role( $role, $this->roles[$role]['capabilities'] );
159                        $this->role_names[$role] = $this->roles[$role]['name'];
160                }
161        }
162
163        /**
164         * Add role name with capabilities to list.
165         *
166         * Updates the list of roles, if the role doesn't already exist.
167         *
168         * The capabilities are defined in the following format `array( 'read' => true );`
169         * To explicitly deny a role a capability you set the value for that capability to false.
170         *
171         * @since 2.0.0
172         * @access public
173         *
174         * @param string $role Role name.
175         * @param string $display_name Role display name.
176         * @param array $capabilities List of role capabilities in the above format.
177         * @return WP_Role|null WP_Role object if role is added, null if already exists.
178         */
179        public function add_role( $role, $display_name, $capabilities = array() ) {
180                if ( isset( $this->roles[$role] ) )
181                        return;
182
183                $this->roles[$role] = array(
184                        'name' => $display_name,
185                        'capabilities' => $capabilities
186                        );
187                if ( $this->use_db )
188                        update_option( $this->role_key, $this->roles );
189                $this->role_objects[$role] = new WP_Role( $role, $capabilities );
190                $this->role_names[$role] = $display_name;
191                return $this->role_objects[$role];
192        }
193
194        /**
195         * Remove role by name.
196         *
197         * @since 2.0.0
198         * @access public
199         *
200         * @param string $role Role name.
201         */
202        public function remove_role( $role ) {
203                if ( ! isset( $this->role_objects[$role] ) )
204                        return;
205
206                unset( $this->role_objects[$role] );
207                unset( $this->role_names[$role] );
208                unset( $this->roles[$role] );
209
210                if ( $this->use_db )
211                        update_option( $this->role_key, $this->roles );
212
213                if ( get_option( 'default_role' ) == $role )
214                        update_option( 'default_role', 'subscriber' );
215        }
216
217        /**
218         * Add capability to role.
219         *
220         * @since 2.0.0
221         * @access public
222         *
223         * @param string $role Role name.
224         * @param string $cap Capability name.
225         * @param bool $grant Optional, default is true. Whether role is capable of performing capability.
226         */
227        public function add_cap( $role, $cap, $grant = true ) {
228                if ( ! isset( $this->roles[$role] ) )
229                        return;
230
231                $this->roles[$role]['capabilities'][$cap] = $grant;
232                if ( $this->use_db )
233                        update_option( $this->role_key, $this->roles );
234        }
235
236        /**
237         * Remove capability from role.
238         *
239         * @since 2.0.0
240         * @access public
241         *
242         * @param string $role Role name.
243         * @param string $cap Capability name.
244         */
245        public function remove_cap( $role, $cap ) {
246                if ( ! isset( $this->roles[$role] ) )
247                        return;
248
249                unset( $this->roles[$role]['capabilities'][$cap] );
250                if ( $this->use_db )
251                        update_option( $this->role_key, $this->roles );
252        }
253
254        /**
255         * Retrieve role object by name.
256         *
257         * @since 2.0.0
258         * @access public
259         *
260         * @param string $role Role name.
261         * @return WP_Role|null WP_Role object if found, null if the role does not exist.
262         */
263        public function get_role( $role ) {
264                if ( isset( $this->role_objects[$role] ) )
265                        return $this->role_objects[$role];
266                else
267                        return null;
268        }
269
270        /**
271         * Retrieve list of role names.
272         *
273         * @since 2.0.0
274         * @access public
275         *
276         * @return array List of role names.
277         */
278        public function get_names() {
279                return $this->role_names;
280        }
281
282        /**
283         * Whether role name is currently in the list of available roles.
284         *
285         * @since 2.0.0
286         * @access public
287         *
288         * @param string $role Role name to look up.
289         * @return bool
290         */
291        public function is_role( $role ) {
292                return isset( $this->role_names[$role] );
293        }
294}
295
296/**
297 * WordPress Role class.
298 *
299 * @since 2.0.0
300 * @package WordPress
301 * @subpackage User
302 */
303class WP_Role {
304        /**
305         * Role name.
306         *
307         * @since 2.0.0
308         * @access public
309         * @var string
310         */
311        public $name;
312
313        /**
314         * List of capabilities the role contains.
315         *
316         * @since 2.0.0
317         * @access public
318         * @var array
319         */
320        public $capabilities;
321
322        /**
323         * Constructor - Set up object properties.
324         *
325         * The list of capabilities, must have the key as the name of the capability
326         * and the value a boolean of whether it is granted to the role.
327         *
328         * @since 2.0.0
329         * @access public
330         *
331         * @param string $role Role name.
332         * @param array $capabilities List of capabilities.
333         */
334        public function __construct( $role, $capabilities ) {
335                $this->name = $role;
336                $this->capabilities = $capabilities;
337        }
338
339        /**
340         * Assign role a capability.
341         *
342         * @see WP_Roles::add_cap() Method uses implementation for role.
343         * @since 2.0.0
344         * @access public
345         *
346         * @param string $cap Capability name.
347         * @param bool $grant Whether role has capability privilege.
348         */
349        public function add_cap( $cap, $grant = true ) {
350                global $wp_roles;
351
352                if ( ! isset( $wp_roles ) )
353                        $wp_roles = new WP_Roles();
354
355                $this->capabilities[$cap] = $grant;
356                $wp_roles->add_cap( $this->name, $cap, $grant );
357        }
358
359        /**
360         * Remove capability from role.
361         *
362         * This is a container for {@link WP_Roles::remove_cap()} to remove the
363         * capability from the role. That is to say, that {@link
364         * WP_Roles::remove_cap()} implements the functionality, but it also makes
365         * sense to use this class, because you don't need to enter the role name.
366         *
367         * @since 2.0.0
368         * @access public
369         *
370         * @param string $cap Capability name.
371         */
372        public function remove_cap( $cap ) {
373                global $wp_roles;
374
375                if ( ! isset( $wp_roles ) )
376                        $wp_roles = new WP_Roles();
377
378                unset( $this->capabilities[$cap] );
379                $wp_roles->remove_cap( $this->name, $cap );
380        }
381
382        /**
383         * Whether role has capability.
384         *
385         * The capabilities is passed through the 'role_has_cap' filter. The first
386         * parameter for the hook is the list of capabilities the class has
387         * assigned. The second parameter is the capability name to look for. The
388         * third and final parameter for the hook is the role name.
389         *
390         * @since 2.0.0
391         * @access public
392         *
393         * @param string $cap Capability name.
394         * @return bool True, if user has capability. False, if doesn't have capability.
395         */
396        public function has_cap( $cap ) {
397                /**
398                 * Filter which capabilities a role has.
399                 *
400                 * @since 2.0.0
401                 *
402                 * @param array  $capabilities Array of role capabilities.
403                 * @param string $cap          Capability name.
404                 * @param string $name         Role name.
405                 */
406                $capabilities = apply_filters( 'role_has_cap', $this->capabilities, $cap, $this->name );
407                if ( !empty( $capabilities[$cap] ) )
408                        return $capabilities[$cap];
409                else
410                        return false;
411        }
412
413}
414
415/**
416 * WordPress User class.
417 *
418 * @since 2.0.0
419 * @package WordPress
420 * @subpackage User
421 *
422 * @property string $nickname
423 * @property string $user_description
424 * @property string $user_firstname
425 * @property string $user_lastname
426 * @property string $user_login
427 * @property string $user_pass
428 * @property string $user_nicename
429 * @property string $user_email
430 * @property string $user_url
431 * @property string $user_registered
432 * @property string $user_activation_key
433 * @property string $user_status
434 * @property string $display_name
435 * @property string $spam
436 * @property string $deleted
437 */
438class WP_User {
439        /**
440         * User data container.
441         *
442         * @since 2.0.0
443         * @var object
444         */
445        public $data;
446
447        /**
448         * The user's ID.
449         *
450         * @since 2.1.0
451         * @access public
452         * @var int
453         */
454        public $ID = 0;
455
456        /**
457         * The individual capabilities the user has been given.
458         *
459         * @since 2.0.0
460         * @access public
461         * @var array
462         */
463        public $caps = array();
464
465        /**
466         * User metadata option name.
467         *
468         * @since 2.0.0
469         * @access public
470         * @var string
471         */
472        public $cap_key;
473
474        /**
475         * The roles the user is part of.
476         *
477         * @since 2.0.0
478         * @access public
479         * @var array
480         */
481        public $roles = array();
482
483        /**
484         * All capabilities the user has, including individual and role based.
485         *
486         * @since 2.0.0
487         * @access public
488         * @var array
489         */
490        public $allcaps = array();
491
492        /**
493         * The filter context applied to user data fields.
494         *
495         * @since 2.9.0
496         * @access private
497         * @var string
498         */
499        var $filter = null;
500
501        private static $back_compat_keys;
502
503        /**
504         * Constructor
505         *
506         * Retrieves the userdata and passes it to {@link WP_User::init()}.
507         *
508         * @since 2.0.0
509         * @access public
510         *
511         * @param int|string|stdClass|WP_User $id User's ID, a WP_User object, or a user object from the DB.
512         * @param string $name Optional. User's username
513         * @param int $blog_id Optional Blog ID, defaults to current blog.
514         */
515        public function __construct( $id = 0, $name = '', $blog_id = '' ) {
516                if ( ! isset( self::$back_compat_keys ) ) {
517                        $prefix = $GLOBALS['wpdb']->prefix;
518                        self::$back_compat_keys = array(
519                                'user_firstname' => 'first_name',
520                                'user_lastname' => 'last_name',
521                                'user_description' => 'description',
522                                'user_level' => $prefix . 'user_level',
523                                $prefix . 'usersettings' => $prefix . 'user-settings',
524                                $prefix . 'usersettingstime' => $prefix . 'user-settings-time',
525                        );
526                }
527
528                if ( $id instanceof WP_User ) {
529                        $this->init( $id->data, $blog_id );
530                        return;
531                } elseif ( is_object( $id ) ) {
532                        $this->init( $id, $blog_id );
533                        return;
534                }
535
536                if ( ! empty( $id ) && ! is_numeric( $id ) ) {
537                        $name = $id;
538                        $id = 0;
539                }
540
541                if ( $id ) {
542                        $data = self::get_data_by( 'id', $id );
543                } else {
544                        $data = self::get_data_by( 'login', $name );
545                }
546
547                if ( $data ) {
548                        $this->init( $data, $blog_id );
549                } else {
550                        $this->data = new stdClass;
551                }
552        }
553
554        /**
555         * Sets up object properties, including capabilities.
556         *
557         * @param object $data User DB row object
558         * @param int $blog_id Optional. The blog id to initialize for
559         */
560        public function init( $data, $blog_id = '' ) {
561                $this->data = $data;
562                $this->ID = (int) $data->ID;
563
564                $this->for_blog( $blog_id );
565        }
566
567        /**
568         * Return only the main user fields
569         *
570         * @since 3.3.0
571         *
572         * @param string $field The field to query against: 'id', 'slug', 'email' or 'login'
573         * @param string|int $value The field value
574         * @return object|false Raw user object
575         */
576        public static function get_data_by( $field, $value ) {
577                global $wpdb;
578
579                if ( 'id' == $field ) {
580                        // Make sure the value is numeric to avoid casting objects, for example,
581                        // to int 1.
582                        if ( ! is_numeric( $value ) )
583                                return false;
584                        $value = intval( $value );
585                        if ( $value < 1 )
586                                return false;
587                } else {
588                        $value = trim( $value );
589                }
590
591                if ( !$value )
592                        return false;
593
594                switch ( $field ) {
595                        case 'id':
596                                $user_id = $value;
597                                $db_field = 'ID';
598                                break;
599                        case 'slug':
600                                $user_id = wp_cache_get($value, 'userslugs');
601                                $db_field = 'user_nicename';
602                                break;
603                        case 'email':
604                                $user_id = wp_cache_get($value, 'useremail');
605                                $db_field = 'user_email';
606                                break;
607                        case 'login':
608                                $value = sanitize_user( $value );
609                                $user_id = wp_cache_get($value, 'userlogins');
610                                $db_field = 'user_login';
611                                break;
612                        default:
613                                return false;
614                }
615
616                if ( false !== $user_id ) {
617                        if ( $user = wp_cache_get( $user_id, 'users' ) )
618                                return $user;
619                }
620
621                if ( !$user = $wpdb->get_row( $wpdb->prepare(
622                        "SELECT * FROM $wpdb->users WHERE $db_field = %s", $value
623                ) ) )
624                        return false;
625
626                update_user_caches( $user );
627
628                return $user;
629        }
630
631        /**
632         * Magic method for checking the existence of a certain custom field
633         *
634         * @since 3.3.0
635         * @param string $key
636         * @return bool
637         */
638        public function __isset( $key ) {
639                if ( 'id' == $key ) {
640                        _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) );
641                        $key = 'ID';
642                }
643
644                if ( isset( $this->data->$key ) )
645                        return true;
646
647                if ( isset( self::$back_compat_keys[ $key ] ) )
648                        $key = self::$back_compat_keys[ $key ];
649
650                return metadata_exists( 'user', $this->ID, $key );
651        }
652
653        /**
654         * Magic method for accessing custom fields
655         *
656         * @since 3.3.0
657         * @param string $key
658         * @return mixed
659         */
660        public function __get( $key ) {
661                if ( 'id' == $key ) {
662                        _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) );
663                        return $this->ID;
664                }
665
666                if ( isset( $this->data->$key ) ) {
667                        $value = $this->data->$key;
668                } else {
669                        if ( isset( self::$back_compat_keys[ $key ] ) )
670                                $key = self::$back_compat_keys[ $key ];
671                        $value = get_user_meta( $this->ID, $key, true );
672                }
673
674                if ( $this->filter ) {
675                        $value = sanitize_user_field( $key, $value, $this->ID, $this->filter );
676                }
677
678                return $value;
679        }
680
681        /**
682         * Magic method for setting custom fields
683         *
684         * @since 3.3.0
685         */
686        public function __set( $key, $value ) {
687                if ( 'id' == $key ) {
688                        _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) );
689                        $this->ID = $value;
690                        return;
691                }
692
693                $this->data->$key = $value;
694        }
695
696        /**
697         * Determine whether the user exists in the database.
698         *
699         * @since 3.4.0
700         * @access public
701         *
702         * @return bool True if user exists in the database, false if not.
703         */
704        public function exists() {
705                return ! empty( $this->ID );
706        }
707
708        /**
709         * Retrieve the value of a property or meta key.
710         *
711         * Retrieves from the users and usermeta table.
712         *
713         * @since 3.3.0
714         *
715         * @param string $key Property
716         */
717        public function get( $key ) {
718                return $this->__get( $key );
719        }
720
721        /**
722         * Determine whether a property or meta key is set
723         *
724         * Consults the users and usermeta tables.
725         *
726         * @since 3.3.0
727         *
728         * @param string $key Property
729         */
730        public function has_prop( $key ) {
731                return $this->__isset( $key );
732        }
733
734        /**
735         * Return an array representation.
736         *
737         * @since 3.5.0
738         *
739         * @return array Array representation.
740         */
741        public function to_array() {
742                return get_object_vars( $this->data );
743        }
744
745        /**
746         * Set up capability object properties.
747         *
748         * Will set the value for the 'cap_key' property to current database table
749         * prefix, followed by 'capabilities'. Will then check to see if the
750         * property matching the 'cap_key' exists and is an array. If so, it will be
751         * used.
752         *
753         * @access protected
754         * @since 2.1.0
755         *
756         * @param string $cap_key Optional capability key
757         */
758        function _init_caps( $cap_key = '' ) {
759                global $wpdb;
760
761                if ( empty($cap_key) )
762                        $this->cap_key = $wpdb->get_blog_prefix() . 'capabilities';
763                else
764                        $this->cap_key = $cap_key;
765
766                $this->caps = get_user_meta( $this->ID, $this->cap_key, true );
767
768                if ( ! is_array( $this->caps ) )
769                        $this->caps = array();
770
771                $this->get_role_caps();
772        }
773
774        /**
775         * Retrieve all of the role capabilities and merge with individual capabilities.
776         *
777         * All of the capabilities of the roles the user belongs to are merged with
778         * the users individual roles. This also means that the user can be denied
779         * specific roles that their role might have, but the specific user isn't
780         * granted permission to.
781         *
782         * @since 2.0.0
783         * @uses $wp_roles
784         * @access public
785         *
786         * @return array List of all capabilities for the user.
787         */
788        public function get_role_caps() {
789                global $wp_roles;
790
791                if ( ! isset( $wp_roles ) )
792                        $wp_roles = new WP_Roles();
793
794                //Filter out caps that are not role names and assign to $this->roles
795                if ( is_array( $this->caps ) )
796                        $this->roles = array_filter( array_keys( $this->caps ), array( $wp_roles, 'is_role' ) );
797
798                //Build $allcaps from role caps, overlay user's $caps
799                $this->allcaps = array();
800                foreach ( (array) $this->roles as $role ) {
801                        $the_role = $wp_roles->get_role( $role );
802                        $this->allcaps = array_merge( (array) $this->allcaps, (array) $the_role->capabilities );
803                }
804                $this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps );
805
806                return $this->allcaps;
807        }
808
809        /**
810         * Add role to user.
811         *
812         * Updates the user's meta data option with capabilities and roles.
813         *
814         * @since 2.0.0
815         * @access public
816         *
817         * @param string $role Role name.
818         */
819        public function add_role( $role ) {
820                $this->caps[$role] = true;
821                update_user_meta( $this->ID, $this->cap_key, $this->caps );
822                $this->get_role_caps();
823                $this->update_user_level_from_caps();
824        }
825
826        /**
827         * Remove role from user.
828         *
829         * @since 2.0.0
830         * @access public
831         *
832         * @param string $role Role name.
833         */
834        public function remove_role( $role ) {
835                if ( !in_array($role, $this->roles) )
836                        return;
837                unset( $this->caps[$role] );
838                update_user_meta( $this->ID, $this->cap_key, $this->caps );
839                $this->get_role_caps();
840                $this->update_user_level_from_caps();
841        }
842
843        /**
844         * Set the role of the user.
845         *
846         * This will remove the previous roles of the user and assign the user the
847         * new one. You can set the role to an empty string and it will remove all
848         * of the roles from the user.
849         *
850         * @since 2.0.0
851         * @access public
852         *
853         * @param string $role Role name.
854         */
855        public function set_role( $role ) {
856                if ( 1 == count( $this->roles ) && $role == current( $this->roles ) )
857                        return;
858
859                foreach ( (array) $this->roles as $oldrole )
860                        unset( $this->caps[$oldrole] );
861
862                $old_roles = $this->roles;
863                if ( !empty( $role ) ) {
864                        $this->caps[$role] = true;
865                        $this->roles = array( $role => true );
866                } else {
867                        $this->roles = false;
868                }
869                update_user_meta( $this->ID, $this->cap_key, $this->caps );
870                $this->get_role_caps();
871                $this->update_user_level_from_caps();
872
873                /**
874                 * Fires after the user's role has changed.
875                 *
876                 * @since 2.9.0
877                 * @since 3.6.0 Added $old_roles to include an array of the user's previous roles.
878                 *
879                 * @param int    $user_id   The user ID.
880                 * @param string $role      The new role.
881                 * @param array  $old_roles An array of the user's previous roles.
882                 */
883                do_action( 'set_user_role', $this->ID, $role, $old_roles );
884        }
885
886        /**
887         * Choose the maximum level the user has.
888         *
889         * Will compare the level from the $item parameter against the $max
890         * parameter. If the item is incorrect, then just the $max parameter value
891         * will be returned.
892         *
893         * Used to get the max level based on the capabilities the user has. This
894         * is also based on roles, so if the user is assigned the Administrator role
895         * then the capability 'level_10' will exist and the user will get that
896         * value.
897         *
898         * @since 2.0.0
899         * @access public
900         *
901         * @param int $max Max level of user.
902         * @param string $item Level capability name.
903         * @return int Max Level.
904         */
905        public function level_reduction( $max, $item ) {
906                if ( preg_match( '/^level_(10|[0-9])$/i', $item, $matches ) ) {
907                        $level = intval( $matches[1] );
908                        return max( $max, $level );
909                } else {
910                        return $max;
911                }
912        }
913
914        /**
915         * Update the maximum user level for the user.
916         *
917         * Updates the 'user_level' user metadata (includes prefix that is the
918         * database table prefix) with the maximum user level. Gets the value from
919         * the all of the capabilities that the user has.
920         *
921         * @since 2.0.0
922         * @access public
923         */
924        public function update_user_level_from_caps() {
925                global $wpdb;
926                $this->user_level = array_reduce( array_keys( $this->allcaps ), array( $this, 'level_reduction' ), 0 );
927                update_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level', $this->user_level );
928        }
929
930        /**
931         * Add capability and grant or deny access to capability.
932         *
933         * @since 2.0.0
934         * @access public
935         *
936         * @param string $cap Capability name.
937         * @param bool $grant Whether to grant capability to user.
938         */
939        public function add_cap( $cap, $grant = true ) {
940                $this->caps[$cap] = $grant;
941                update_user_meta( $this->ID, $this->cap_key, $this->caps );
942                $this->get_role_caps();
943                $this->update_user_level_from_caps();
944        }
945
946        /**
947         * Remove capability from user.
948         *
949         * @since 2.0.0
950         * @access public
951         *
952         * @param string $cap Capability name.
953         */
954        public function remove_cap( $cap ) {
955                if ( ! isset( $this->caps[ $cap ] ) ) {
956                        return;
957                }
958                unset( $this->caps[ $cap ] );
959                update_user_meta( $this->ID, $this->cap_key, $this->caps );
960                $this->get_role_caps();
961                $this->update_user_level_from_caps();
962        }
963
964        /**
965         * Remove all of the capabilities of the user.
966         *
967         * @since 2.1.0
968         * @access public
969         */
970        public function remove_all_caps() {
971                global $wpdb;
972                $this->caps = array();
973                delete_user_meta( $this->ID, $this->cap_key );
974                delete_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level' );
975                $this->get_role_caps();
976        }
977
978        /**
979         * Whether user has capability or role name.
980         *
981         * This is useful for looking up whether the user has a specific role
982         * assigned to the user. The second optional parameter can also be used to
983         * check for capabilities against a specific object, such as a post or user.
984         *
985         * @since 2.0.0
986         * @access public
987         *
988         * @param string|int $cap Capability or role name to search.
989         * @return bool True, if user has capability; false, if user does not have capability.
990         */
991        public function has_cap( $cap ) {
992                if ( is_numeric( $cap ) ) {
993                        _deprecated_argument( __FUNCTION__, '2.0', __('Usage of user levels by plugins and themes is deprecated. Use roles and capabilities instead.') );
994                        $cap = $this->translate_level_to_cap( $cap );
995                }
996
997                $args = array_slice( func_get_args(), 1 );
998                $args = array_merge( array( $cap, $this->ID ), $args );
999                $caps = call_user_func_array( 'map_meta_cap', $args );
1000
1001                // Multisite super admin has all caps by definition, Unless specifically denied.
1002                if ( is_multisite() && is_super_admin( $this->ID ) ) {
1003                        if ( in_array('do_not_allow', $caps) )
1004                                return false;
1005                        return true;
1006                }
1007
1008                /**
1009                 * Dynamically filter a user's capabilities.
1010                 *
1011                 * @since 2.0.0
1012                 * @since 3.7.0 Added the user object.
1013                 *
1014                 * @param array   $allcaps An array of all the user's capabilities.
1015                 * @param array   $caps    Actual capabilities for meta capability.
1016                 * @param array   $args    Optional parameters passed to has_cap(), typically object ID.
1017                 * @param WP_User $user    The user object.
1018                 */
1019                // Must have ALL requested caps
1020                $capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args, $this );
1021                $capabilities['exist'] = true; // Everyone is allowed to exist
1022                foreach ( (array) $caps as $cap ) {
1023                        if ( empty( $capabilities[ $cap ] ) )
1024                                return false;
1025                }
1026
1027                return true;
1028        }
1029
1030        /**
1031         * Convert numeric level to level capability name.
1032         *
1033         * Prepends 'level_' to level number.
1034         *
1035         * @since 2.0.0
1036         * @access public
1037         *
1038         * @param int $level Level number, 1 to 10.
1039         * @return string
1040         */
1041        public function translate_level_to_cap( $level ) {
1042                return 'level_' . $level;
1043        }
1044
1045        /**
1046         * Set the blog to operate on. Defaults to the current blog.
1047         *
1048         * @since 3.0.0
1049         *
1050         * @param int $blog_id Optional Blog ID, defaults to current blog.
1051         */
1052        public function for_blog( $blog_id = '' ) {
1053                global $wpdb;
1054                if ( ! empty( $blog_id ) )
1055                        $cap_key = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';
1056                else
1057                        $cap_key = '';
1058                $this->_init_caps( $cap_key );
1059        }
1060}
1061
1062/**
1063 * Map meta capabilities to primitive capabilities.
1064 *
1065 * This does not actually compare whether the user ID has the actual capability,
1066 * just what the capability or capabilities are. Meta capability list value can
1067 * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post',
1068 * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'.
1069 *
1070 * @since 2.0.0
1071 *
1072 * @param string $cap Capability name.
1073 * @param int $user_id User ID.
1074 * @return array Actual capabilities for meta capability.
1075 */
1076function map_meta_cap( $cap, $user_id ) {
1077        $args = array_slice( func_get_args(), 2 );
1078        $caps = array();
1079
1080        switch ( $cap ) {
1081        case 'remove_user':
1082                $caps[] = 'remove_users';
1083                break;
1084        case 'promote_user':
1085                $caps[] = 'promote_users';
1086                break;
1087        case 'edit_user':
1088        case 'edit_users':
1089                // Allow user to edit itself
1090                if ( 'edit_user' == $cap && isset( $args[0] ) && $user_id == $args[0] )
1091                        break;
1092
1093                // If multisite these caps are allowed only for super admins.
1094                if ( is_multisite() && !is_super_admin( $user_id ) )
1095                        $caps[] = 'do_not_allow';
1096                else
1097                        $caps[] = 'edit_users'; // edit_user maps to edit_users.
1098                break;
1099        case 'delete_post':
1100        case 'delete_page':
1101                $post = get_post( $args[0] );
1102
1103                if ( 'revision' == $post->post_type ) {
1104                        $post = get_post( $post->post_parent );
1105                }
1106
1107                $post_type = get_post_type_object( $post->post_type );
1108
1109                if ( post_type_exists($post->post_type) && ! $post_type->map_meta_cap ) {
1110                        $caps[] = $post_type->cap->$cap;
1111                        // Prior to 3.1 we would re-call map_meta_cap here.
1112                        if ( 'delete_post' == $cap )
1113                                $cap = $post_type->cap->$cap;
1114                        break;
1115                }
1116
1117                // If the post author is set and the user is the author...
1118                if ( post_type_exists($post->post_type) ) {
1119                        if ( $post->post_author && $user_id == $post->post_author ) {
1120                                // If the post is published...
1121                                if ( 'publish' == $post->post_status ) {
1122                                        $caps[] = $post_type->cap->delete_published_posts;
1123                                } elseif ( 'trash' == $post->post_status ) {
1124                                        if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) {
1125                                                $caps[] = $post_type->cap->delete_published_posts;
1126                                        }
1127                                } else {
1128                                        // If the post is draft...
1129                                        $caps[] = $post_type->cap->delete_posts;
1130                                }
1131                        } else {
1132                                // The user is trying to edit someone else's post.
1133                                $caps[] = $post_type->cap->delete_others_posts;
1134                                // The post is published, extra cap required.
1135                                if ( 'publish' == $post->post_status ) {
1136                                        $caps[] = $post_type->cap->delete_published_posts;
1137                                } elseif ( 'private' == $post->post_status ) {
1138                                        $caps[] = $post_type->cap->delete_private_posts;
1139                                }
1140                        }
1141                }
1142                break;
1143                // edit_post breaks down to edit_posts, edit_published_posts, or
1144                // edit_others_posts
1145        case 'edit_post':
1146        case 'edit_page':
1147                $post = get_post( $args[0] );
1148                if ( empty( $post ) )
1149                        break;
1150
1151                if ( 'revision' == $post->post_type ) {
1152                        $post = get_post( $post->post_parent );
1153                }
1154
1155                $post_type = get_post_type_object( $post->post_type );
1156
1157                if ( post_type_exists($post->post_type) && ! $post_type->map_meta_cap ) {
1158                        $caps[] = $post_type->cap->$cap;
1159                        // Prior to 3.1 we would re-call map_meta_cap here.
1160                        if ( 'edit_post' == $cap )
1161                                $cap = $post_type->cap->$cap;
1162                        break;
1163                }
1164
1165                // If the post author is set and the user is the author...
1166                if ( post_type_exists($post->post_type) ) {
1167                        if ( $post->post_author && $user_id == $post->post_author ) {
1168                                // If the post is published...
1169                                if ( 'publish' == $post->post_status ) {
1170                                        $caps[] = $post_type->cap->edit_published_posts;
1171                                } elseif ( 'trash' == $post->post_status ) {
1172                                        if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) {
1173                                                $caps[] = $post_type->cap->edit_published_posts;
1174                                        }
1175                                } else {
1176                                        // If the post is draft...
1177                                        $caps[] = $post_type->cap->edit_posts;
1178                                }
1179                        } else {
1180                                // The user is trying to edit someone else's post.
1181                                $caps[] = $post_type->cap->edit_others_posts;
1182                                // The post is published, extra cap required.
1183                                if ( 'publish' == $post->post_status ) {
1184                                        $caps[] = $post_type->cap->edit_published_posts;
1185                                } elseif ( 'private' == $post->post_status ) {
1186                                        $caps[] = $post_type->cap->edit_private_posts;
1187                                }
1188                        }
1189                }
1190                break;
1191        case 'read_post':
1192        case 'read_page':
1193                $post = get_post( $args[0] );
1194
1195                if ( 'revision' == $post->post_type ) {
1196                        $post = get_post( $post->post_parent );
1197                }
1198
1199                $post_type = get_post_type_object( $post->post_type );
1200
1201                if ( post_type_exists($post->post_type) && ! $post_type->map_meta_cap ) {
1202                        $caps[] = $post_type->cap->$cap;
1203                        // Prior to 3.1 we would re-call map_meta_cap here.
1204                        if ( 'read_post' == $cap )
1205                                $cap = $post_type->cap->$cap;
1206                        break;
1207                }
1208
1209                $status_obj = get_post_status_object( $post->post_status );
1210                if ( post_type_exists($post->post_type) && $status_obj->public ) {
1211                        $caps[] = $post_type->cap->read;
1212                        break;
1213                }
1214
1215                if ( post_type_exists($post->post_type) ) {
1216                        if ( $post->post_author && $user_id == $post->post_author ) {
1217                                $caps[] = $post_type->cap->read;
1218                        } elseif ( $status_obj->private ) {
1219                                $caps[] = $post_type->cap->read_private_posts;
1220                        } else {
1221                                $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
1222                        }
1223                }
1224                break;
1225        case 'publish_post':
1226                $post = get_post( $args[0] );
1227                $post_type = get_post_type_object( $post->post_type );
1228
1229                if ( post_type_exists($post->post_type) ) {
1230                        $caps[] = $post_type->cap->publish_posts;
1231                }
1232                break;
1233        case 'edit_post_meta':
1234        case 'delete_post_meta':
1235        case 'add_post_meta':
1236                $post = get_post( $args[0] );
1237                $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
1238
1239                $meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
1240
1241                if ( $meta_key && has_filter( "auth_post_meta_{$meta_key}" ) ) {
1242                        /**
1243                         * Filter whether the user is allowed to add post meta to a post.
1244                         *
1245                         * The dynamic portion of the hook name, `$meta_key`, refers to the
1246                         * meta key passed to {@see map_meta_cap()}.
1247                         *
1248                         * @since 3.3.0
1249                         *
1250                         * @param bool   $allowed  Whether the user can add the post meta. Default false.
1251                         * @param string $meta_key The meta key.
1252                         * @param int    $post_id  Post ID.
1253                         * @param int    $user_id  User ID.
1254                         * @param string $cap      Capability name.
1255                         * @param array  $caps     User capabilities.
1256                         */
1257                        $allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps );
1258                        if ( ! $allowed )
1259                                $caps[] = $cap;
1260                } elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
1261                        $caps[] = $cap;
1262                }
1263                break;
1264        case 'edit_comment':
1265                $comment = get_comment( $args[0] );
1266                if ( empty( $comment ) )
1267                        break;
1268                $post = get_post( $comment->comment_post_ID );
1269                $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
1270                break;
1271        case 'unfiltered_upload':
1272                if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) )  )
1273                        $caps[] = $cap;
1274                else
1275                        $caps[] = 'do_not_allow';
1276                break;
1277        case 'unfiltered_html' :
1278                // Disallow unfiltered_html for all users, even admins and super admins.
1279                if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
1280                        $caps[] = 'do_not_allow';
1281                elseif ( is_multisite() && ! is_super_admin( $user_id ) )
1282                        $caps[] = 'do_not_allow';
1283                else
1284                        $caps[] = $cap;
1285                break;
1286        case 'edit_files':
1287        case 'edit_plugins':
1288        case 'edit_themes':
1289                // Disallow the file editors.
1290                if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
1291                        $caps[] = 'do_not_allow';
1292                elseif ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
1293                        $caps[] = 'do_not_allow';
1294                elseif ( is_multisite() && ! is_super_admin( $user_id ) )
1295                        $caps[] = 'do_not_allow';
1296                else
1297                        $caps[] = $cap;
1298                break;
1299        case 'update_plugins':
1300        case 'delete_plugins':
1301        case 'install_plugins':
1302        case 'upload_plugins':
1303        case 'update_themes':
1304        case 'delete_themes':
1305        case 'install_themes':
1306        case 'upload_themes':
1307        case 'update_core':
1308                // Disallow anything that creates, deletes, or updates core, plugin, or theme files.
1309                // Files in uploads are excepted.
1310                if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
1311                        $caps[] = 'do_not_allow';
1312                } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
1313                        $caps[] = 'do_not_allow';
1314                } elseif ( 'upload_themes' === $cap ) {
1315                        $caps[] = 'install_themes';
1316                } elseif ( 'upload_plugins' === $cap ) {
1317                        $caps[] = 'install_plugins';
1318                } else {
1319                        $caps[] = $cap;
1320                }
1321                break;
1322        case 'activate_plugins':
1323                $caps[] = $cap;
1324                if ( is_multisite() ) {
1325                        // update_, install_, and delete_ are handled above with is_super_admin().
1326                        $menu_perms = get_site_option( 'menu_items', array() );
1327                        if ( empty( $menu_perms['plugins'] ) )
1328                                $caps[] = 'manage_network_plugins';
1329                }
1330                break;
1331        case 'delete_user':
1332        case 'delete_users':
1333                // If multisite only super admins can delete users.
1334                if ( is_multisite() && ! is_super_admin( $user_id ) )
1335                        $caps[] = 'do_not_allow';
1336                else
1337                        $caps[] = 'delete_users'; // delete_user maps to delete_users.
1338                break;
1339        case 'create_users':
1340                if ( !is_multisite() )
1341                        $caps[] = $cap;
1342                elseif ( is_super_admin() || get_site_option( 'add_new_users' ) )
1343                        $caps[] = $cap;
1344                else
1345                        $caps[] = 'do_not_allow';
1346                break;
1347        case 'manage_links' :
1348                if ( get_option( 'link_manager_enabled' ) )
1349                        $caps[] = $cap;
1350                else
1351                        $caps[] = 'do_not_allow';
1352                break;
1353        case 'customize' :
1354                $caps[] = 'edit_theme_options';
1355                break;
1356        case 'delete_site':
1357                $caps[] = 'manage_options';
1358                break;
1359        default:
1360                // Handle meta capabilities for custom post types.
1361                $post_type_meta_caps = _post_type_meta_capabilities();
1362                if ( isset( $post_type_meta_caps[ $cap ] ) ) {
1363                        $args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
1364                        return call_user_func_array( 'map_meta_cap', $args );
1365                }
1366
1367                // If no meta caps match, return the original cap.
1368                $caps[] = $cap;
1369        }
1370
1371        /**
1372         * Filter a user's capabilities depending on specific context and/or privilege.
1373         *
1374         * @since 2.8.0
1375         *
1376         * @param array  $caps    Returns the user's actual capabilities.
1377         * @param string $cap     Capability name.
1378         * @param int    $user_id The user ID.
1379         * @param array  $args    Adds the context to the cap. Typically the object ID.
1380         */
1381        return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
1382}
1383
1384/**
1385 * Whether current user has capability or role.
1386 *
1387 * @since 2.0.0
1388 *
1389 * @param string $capability Capability or role name.
1390 * @return bool
1391 */
1392function current_user_can( $capability ) {
1393        $current_user = wp_get_current_user();
1394
1395        if ( empty( $current_user ) )
1396                return false;
1397
1398        $args = array_slice( func_get_args(), 1 );
1399        $args = array_merge( array( $capability ), $args );
1400
1401        return call_user_func_array( array( $current_user, 'has_cap' ), $args );
1402}
1403
1404/**
1405 * Whether current user has a capability or role for a given blog.
1406 *
1407 * @since 3.0.0
1408 *
1409 * @param int $blog_id Blog ID
1410 * @param string $capability Capability or role name.
1411 * @return bool
1412 */
1413function current_user_can_for_blog( $blog_id, $capability ) {
1414        $switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
1415
1416        $current_user = wp_get_current_user();
1417
1418        if ( empty( $current_user ) ) {
1419                if ( $switched ) {
1420                        restore_current_blog();
1421                }
1422                return false;
1423        }
1424
1425        $args = array_slice( func_get_args(), 2 );
1426        $args = array_merge( array( $capability ), $args );
1427
1428        $can = call_user_func_array( array( $current_user, 'has_cap' ), $args );
1429
1430        if ( $switched ) {
1431                restore_current_blog();
1432        }
1433
1434        return $can;
1435}
1436
1437/**
1438 * Whether author of supplied post has capability or role.
1439 *
1440 * @since 2.9.0
1441 *
1442 * @param int|object $post Post ID or post object.
1443 * @param string $capability Capability or role name.
1444 * @return bool
1445 */
1446function author_can( $post, $capability ) {
1447        if ( !$post = get_post($post) )
1448                return false;
1449
1450        $author = get_userdata( $post->post_author );
1451
1452        if ( ! $author )
1453                return false;
1454
1455        $args = array_slice( func_get_args(), 2 );
1456        $args = array_merge( array( $capability ), $args );
1457
1458        return call_user_func_array( array( $author, 'has_cap' ), $args );
1459}
1460
1461/**
1462 * Whether a particular user has capability or role.
1463 *
1464 * @since 3.1.0
1465 *
1466 * @param int|object $user User ID or object.
1467 * @param string $capability Capability or role name.
1468 * @return bool
1469 */
1470function user_can( $user, $capability ) {
1471        if ( ! is_object( $user ) )
1472                $user = get_userdata( $user );
1473
1474        if ( ! $user || ! $user->exists() )
1475                return false;
1476
1477        $args = array_slice( func_get_args(), 2 );
1478        $args = array_merge( array( $capability ), $args );
1479
1480        return call_user_func_array( array( $user, 'has_cap' ), $args );
1481}
1482
1483/**
1484 * Retrieve role object.
1485 *
1486 * @see WP_Roles::get_role() Uses method to retrieve role object.
1487 * @since 2.0.0
1488 *
1489 * @param string $role Role name.
1490 * @return WP_Role|null WP_Role object if found, null if the role does not exist.
1491 */
1492function get_role( $role ) {
1493        global $wp_roles;
1494
1495        if ( ! isset( $wp_roles ) )
1496                $wp_roles = new WP_Roles();
1497
1498        return $wp_roles->get_role( $role );
1499}
1500
1501/**
1502 * Add role, if it does not exist.
1503 *
1504 * @see WP_Roles::add_role() Uses method to add role.
1505 * @since 2.0.0
1506 *
1507 * @param string $role Role name.
1508 * @param string $display_name Display name for role.
1509 * @param array $capabilities List of capabilities, e.g. array( 'edit_posts' => true, 'delete_posts' => false );
1510 * @return WP_Role|null WP_Role object if role is added, null if already exists.
1511 */
1512function add_role( $role, $display_name, $capabilities = array() ) {
1513        global $wp_roles;
1514
1515        if ( ! isset( $wp_roles ) )
1516                $wp_roles = new WP_Roles();
1517
1518        return $wp_roles->add_role( $role, $display_name, $capabilities );
1519}
1520
1521/**
1522 * Remove role, if it exists.
1523 *
1524 * @see WP_Roles::remove_role() Uses method to remove role.
1525 * @since 2.0.0
1526 *
1527 * @param string $role Role name.
1528 */
1529function remove_role( $role ) {
1530        global $wp_roles;
1531
1532        if ( ! isset( $wp_roles ) )
1533                $wp_roles = new WP_Roles();
1534
1535        $wp_roles->remove_role( $role );
1536}
1537
1538/**
1539 * Retrieve a list of super admins.
1540 *
1541 * @since 3.0.0
1542 *
1543 * @uses $super_admins Super admins global variable, if set.
1544 *
1545 * @return array List of super admin logins
1546 */
1547function get_super_admins() {
1548        global $super_admins;
1549
1550        if ( isset($super_admins) )
1551                return $super_admins;
1552        else
1553                return get_site_option( 'site_admins', array('admin') );
1554}
1555
1556/**
1557 * Determine if user is a site admin.
1558 *
1559 * @since 3.0.0
1560 *
1561 * @param int $user_id (Optional) The ID of a user. Defaults to the current user.
1562 * @return bool True if the user is a site admin.
1563 */
1564function is_super_admin( $user_id = false ) {
1565        if ( ! $user_id || $user_id == get_current_user_id() )
1566                $user = wp_get_current_user();
1567        else
1568                $user = get_userdata( $user_id );
1569
1570        if ( ! $user || ! $user->exists() )
1571                return false;
1572
1573        if ( is_multisite() ) {
1574                $super_admins = get_super_admins();
1575                if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) )
1576                        return true;
1577        } else {
1578                if ( $user->has_cap('delete_users') )
1579                        return true;
1580        }
1581
1582        return false;
1583}