· 4 years ago · Jul 26, 2021, 07:56 AM
1<?php
2/**
3 * Core User Role & Capabilities API
4 *
5 * @package WordPress
6 * @subpackage Users
7 */
8
9/**
10 * Maps a capability to the primitive capabilities required of the given user to
11 * satisfy the capability being checked.
12 *
13 * This function also accepts an ID of an object to map against if the capability is a meta capability. Meta
14 * capabilities such as `edit_post` and `edit_user` are capabilities used by this function to map to primitive
15 * capabilities that a user or role requires, such as `edit_posts` and `edit_others_posts`.
16 *
17 * Example usage:
18 *
19 * map_meta_cap( 'edit_posts', $user->ID );
20 * map_meta_cap( 'edit_post', $user->ID, $post->ID );
21 * map_meta_cap( 'edit_post_meta', $user->ID, $post->ID, $meta_key );
22 *
23 * This function does not check whether the user has the required capabilities,
24 * it just returns what the required capabilities are.
25 *
26 * @since 2.0.0
27 * @since 4.9.6 Added the `export_others_personal_data`, `erase_others_personal_data`,
28 * and `manage_privacy_options` capabilities.
29 * @since 5.1.0 Added the `update_php` capability.
30 * @since 5.2.0 Added the `resume_plugin` and `resume_theme` capabilities.
31 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
32 * by adding it to the function signature.
33 * @since 5.7.0 Added the `create_app_password`, `list_app_passwords`, `read_app_password`,
34 * `edit_app_password`, `delete_app_passwords`, `delete_app_password`,
35 * and `update_https` capabilities.
36 *
37 * @global array $post_type_meta_caps Used to get post type meta capabilities.
38 *
39 * @param string $cap Capability being checked.
40 * @param int $user_id User ID.
41 * @param mixed ...$args Optional further parameters, typically starting with an object ID.
42 * @return string[] Primitive capabilities required of the user.
43 */
44function map_meta_cap( $cap, $user_id, ...$args ) {
45 $caps = array();
46
47 switch ( $cap ) {
48 case 'remove_user':
49 // In multisite the user must be a super admin to remove themselves.
50 if ( isset( $args[0] ) && $user_id == $args[0] && ! is_super_admin( $user_id ) ) {
51 $caps[] = 'do_not_allow';
52 } else {
53 $caps[] = 'remove_users';
54 }
55 break;
56 case 'promote_user':
57 case 'add_users':
58 $caps[] = 'promote_users';
59 break;
60 case 'edit_user':
61 case 'edit_users':
62 // Allow user to edit themselves.
63 if ( 'edit_user' === $cap && isset( $args[0] ) && $user_id == $args[0] ) {
64 break;
65 }
66
67 // In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
68 if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
69 $caps[] = 'do_not_allow';
70 } else {
71 $caps[] = 'edit_users'; // edit_user maps to edit_users.
72 }
73 break;
74 case 'delete_post':
75 case 'delete_page':
76 $post = get_post( $args[0] );
77 if ( ! $post ) {
78 $caps[] = 'do_not_allow';
79 break;
80 }
81
82 if ( 'revision' === $post->post_type ) {
83 $caps[] = 'do_not_allow';
84 break;
85 }
86
87 if ( ( get_option( 'page_for_posts' ) == $post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) {
88 $caps[] = 'manage_options';
89 break;
90 }
91
92 $post_type = get_post_type_object( $post->post_type );
93 if ( ! $post_type ) {
94 /* translators: 1: Post type, 2: Capability name. */
95 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
96 $caps[] = 'edit_others_posts';
97 break;
98 }
99
100 if ( ! $post_type->map_meta_cap ) {
101 $caps[] = $post_type->cap->$cap;
102 // Prior to 3.1 we would re-call map_meta_cap here.
103 if ( 'delete_post' === $cap ) {
104 $cap = $post_type->cap->$cap;
105 }
106 break;
107 }
108
109 // If the post author is set and the user is the author...
110 if ( $post->post_author && $user_id == $post->post_author ) {
111 // If the post is published or scheduled...
112 if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
113 $caps[] = $post_type->cap->delete_published_posts;
114 } elseif ( 'trash' === $post->post_status ) {
115 $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
116 if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
117 $caps[] = $post_type->cap->delete_published_posts;
118 } else {
119 $caps[] = $post_type->cap->delete_posts;
120 }
121 } else {
122 // If the post is draft...
123 $caps[] = $post_type->cap->delete_posts;
124 }
125 } else {
126 // The user is trying to edit someone else's post.
127 $caps[] = $post_type->cap->delete_others_posts;
128 // The post is published or scheduled, extra cap required.
129 if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
130 $caps[] = $post_type->cap->delete_published_posts;
131 } elseif ( 'private' === $post->post_status ) {
132 $caps[] = $post_type->cap->delete_private_posts;
133 }
134 }
135
136 /*
137 * Setting the privacy policy page requires `manage_privacy_options`,
138 * so deleting it should require that too.
139 */
140 if ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post->ID ) {
141 $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
142 }
143
144 break;
145 // edit_post breaks down to edit_posts, edit_published_posts, or
146 // edit_others_posts.
147 case 'edit_post':
148 case 'edit_page':
149 $post = get_post( $args[0] );
150 if ( ! $post ) {
151 $caps[] = 'do_not_allow';
152 break;
153 }
154
155 if ( 'revision' === $post->post_type ) {
156 $post = get_post( $post->post_parent );
157 if ( ! $post ) {
158 $caps[] = 'do_not_allow';
159 break;
160 }
161 }
162
163 $post_type = get_post_type_object( $post->post_type );
164 if ( ! $post_type ) {
165 /* translators: 1: Post type, 2: Capability name. */
166 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
167 $caps[] = 'edit_others_posts';
168 break;
169 }
170
171 if ( ! $post_type->map_meta_cap ) {
172 $caps[] = $post_type->cap->$cap;
173 // Prior to 3.1 we would re-call map_meta_cap here.
174 if ( 'edit_post' === $cap ) {
175 $cap = $post_type->cap->$cap;
176 }
177 break;
178 }
179
180 // If the post author is set and the user is the author...
181 if ( $post->post_author && $user_id == $post->post_author ) {
182 // If the post is published or scheduled...
183 if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
184 $caps[] = $post_type->cap->edit_published_posts;
185 } elseif ( 'trash' === $post->post_status ) {
186 $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
187 if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
188 $caps[] = $post_type->cap->edit_published_posts;
189 } else {
190 $caps[] = $post_type->cap->edit_posts;
191 }
192 } else {
193 // If the post is draft...
194 $caps[] = $post_type->cap->edit_posts;
195 }
196 } else {
197 // The user is trying to edit someone else's post.
198 $caps[] = $post_type->cap->edit_others_posts;
199 // The post is published or scheduled, extra cap required.
200 if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
201 $caps[] = $post_type->cap->edit_published_posts;
202 } elseif ( 'private' === $post->post_status ) {
203 $caps[] = $post_type->cap->edit_private_posts;
204 }
205 }
206
207 /*
208 * Setting the privacy policy page requires `manage_privacy_options`,
209 * so editing it should require that too.
210 */
211 if ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post->ID ) {
212 $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
213 }
214
215 break;
216 case 'read_post':
217 case 'read_page':
218 $post = get_post( $args[0] );
219 if ( ! $post ) {
220 $caps[] = 'do_not_allow';
221 break;
222 }
223
224 if ( 'revision' === $post->post_type ) {
225 $post = get_post( $post->post_parent );
226 if ( ! $post ) {
227 $caps[] = 'do_not_allow';
228 break;
229 }
230 }
231
232 $post_type = get_post_type_object( $post->post_type );
233 if ( ! $post_type ) {
234 /* translators: 1: Post type, 2: Capability name. */
235 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
236 $caps[] = 'edit_others_posts';
237 break;
238 }
239
240 if ( ! $post_type->map_meta_cap ) {
241 $caps[] = $post_type->cap->$cap;
242 // Prior to 3.1 we would re-call map_meta_cap here.
243 if ( 'read_post' === $cap ) {
244 $cap = $post_type->cap->$cap;
245 }
246 break;
247 }
248
249 $status_obj = get_post_status_object( get_post_status( $post ) );
250 if ( ! $status_obj ) {
251 /* translators: 1: Post status, 2: Capability name. */
252 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), get_post_status( $post ), $cap ), '5.4.0' );
253 $caps[] = 'edit_others_posts';
254 break;
255 }
256
257 if ( $status_obj->public ) {
258 $caps[] = $post_type->cap->read;
259 break;
260 }
261
262 if ( $post->post_author && $user_id == $post->post_author ) {
263 $caps[] = $post_type->cap->read;
264 } elseif ( $status_obj->private ) {
265 $caps[] = $post_type->cap->read_private_posts;
266 } else {
267 $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
268 }
269 break;
270 case 'publish_post':
271 $post = get_post( $args[0] );
272 if ( ! $post ) {
273 $caps[] = 'do_not_allow';
274 break;
275 }
276
277 $post_type = get_post_type_object( $post->post_type );
278 if ( ! $post_type ) {
279 /* translators: 1: Post type, 2: Capability name. */
280 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
281 $caps[] = 'edit_others_posts';
282 break;
283 }
284
285 $caps[] = $post_type->cap->publish_posts;
286 break;
287 case 'edit_post_meta':
288 case 'delete_post_meta':
289 case 'add_post_meta':
290 case 'edit_comment_meta':
291 case 'delete_comment_meta':
292 case 'add_comment_meta':
293 case 'edit_term_meta':
294 case 'delete_term_meta':
295 case 'add_term_meta':
296 case 'edit_user_meta':
297 case 'delete_user_meta':
298 case 'add_user_meta':
299 $object_type = explode( '_', $cap )[1];
300 $object_id = (int) $args[0];
301
302 $object_subtype = get_object_subtype( $object_type, $object_id );
303
304 if ( empty( $object_subtype ) ) {
305 $caps[] = 'do_not_allow';
306 break;
307 }
308
309 $caps = map_meta_cap( "edit_{$object_type}", $user_id, $object_id );
310
311 $meta_key = isset( $args[1] ) ? $args[1] : false;
312
313 if ( $meta_key ) {
314 $allowed = ! is_protected_meta( $meta_key, $object_type );
315
316 if ( ! empty( $object_subtype ) && has_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
317
318 /**
319 * Filters whether the user is allowed to edit a specific meta key of a specific object type and subtype.
320 *
321 * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
322 * and `$object_subtype`, refer to the metadata object type (comment, post, term or user),
323 * the meta key value, and the object subtype respectively.
324 *
325 * @since 4.9.8
326 *
327 * @param bool $allowed Whether the user can add the object meta. Default false.
328 * @param string $meta_key The meta key.
329 * @param int $object_id Object ID.
330 * @param int $user_id User ID.
331 * @param string $cap Capability name.
332 * @param string[] $caps Array of the user's capabilities.
333 */
334 $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
335 } else {
336
337 /**
338 * Filters whether the user is allowed to edit a specific meta key of a specific object type.
339 *
340 * Return true to have the mapped meta caps from `edit_{$object_type}` apply.
341 *
342 * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
343 * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
344 *
345 * @since 3.3.0 As `auth_post_meta_{$meta_key}`.
346 * @since 4.6.0
347 *
348 * @param bool $allowed Whether the user can add the object meta. Default false.
349 * @param string $meta_key The meta key.
350 * @param int $object_id Object ID.
351 * @param int $user_id User ID.
352 * @param string $cap Capability name.
353 * @param string[] $caps Array of the user's capabilities.
354 */
355 $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
356 }
357
358 if ( ! empty( $object_subtype ) ) {
359
360 /**
361 * Filters whether the user is allowed to edit meta for specific object types/subtypes.
362 *
363 * Return true to have the mapped meta caps from `edit_{$object_type}` apply.
364 *
365 * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
366 * The dynamic portion of the hook name, `$object_subtype` refers to the object subtype being filtered.
367 * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
368 *
369 * @since 4.6.0 As `auth_post_{$post_type}_meta_{$meta_key}`.
370 * @since 4.7.0 Renamed from `auth_post_{$post_type}_meta_{$meta_key}` to
371 * `auth_{$object_type}_{$object_subtype}_meta_{$meta_key}`.
372 * @deprecated 4.9.8 Use {@see 'auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}'} instead.
373 *
374 * @param bool $allowed Whether the user can add the object meta. Default false.
375 * @param string $meta_key The meta key.
376 * @param int $object_id Object ID.
377 * @param int $user_id User ID.
378 * @param string $cap Capability name.
379 * @param string[] $caps Array of the user's capabilities.
380 */
381 $allowed = apply_filters_deprecated(
382 "auth_{$object_type}_{$object_subtype}_meta_{$meta_key}",
383 array( $allowed, $meta_key, $object_id, $user_id, $cap, $caps ),
384 '4.9.8',
385 "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}"
386 );
387 }
388
389 if ( ! $allowed ) {
390 $caps[] = $cap;
391 }
392 }
393 break;
394 case 'edit_comment':
395 $comment = get_comment( $args[0] );
396 if ( ! $comment ) {
397 $caps[] = 'do_not_allow';
398 break;
399 }
400
401 $post = get_post( $comment->comment_post_ID );
402
403 /*
404 * If the post doesn't exist, we have an orphaned comment.
405 * Fall back to the edit_posts capability, instead.
406 */
407 if ( $post ) {
408 $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
409 } else {
410 $caps = map_meta_cap( 'edit_posts', $user_id );
411 }
412 break;
413 case 'unfiltered_upload':
414 if ( defined( 'ALLOW_UNFILTERED_UPLOADS' ) && ALLOW_UNFILTERED_UPLOADS && ( ! is_multisite() || is_super_admin( $user_id ) ) ) {
415 $caps[] = $cap;
416 } else {
417 $caps[] = 'do_not_allow';
418 }
419 break;
420 case 'edit_css':
421 case 'unfiltered_html':
422 // Disallow unfiltered_html for all users, even admins and super admins.
423 if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) {
424 $caps[] = 'do_not_allow';
425 } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
426 $caps[] = 'do_not_allow';
427 } else {
428 $caps[] = 'unfiltered_html';
429 }
430 break;
431 case 'edit_files':
432 case 'edit_plugins':
433 case 'edit_themes':
434 // Disallow the file editors.
435 if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ) {
436 $caps[] = 'do_not_allow';
437 } elseif ( ! wp_is_file_mod_allowed( 'capability_edit_themes' ) ) {
438 $caps[] = 'do_not_allow';
439 } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
440 $caps[] = 'do_not_allow';
441 } else {
442 $caps[] = $cap;
443 }
444 break;
445 case 'update_plugins':
446 case 'delete_plugins':
447 case 'install_plugins':
448 case 'upload_plugins':
449 case 'update_themes':
450 case 'delete_themes':
451 case 'install_themes':
452 case 'upload_themes':
453 case 'update_core':
454 // Disallow anything that creates, deletes, or updates core, plugin, or theme files.
455 // Files in uploads are excepted.
456 if ( ! wp_is_file_mod_allowed( 'capability_update_core' ) ) {
457 $caps[] = 'do_not_allow';
458 } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
459 $caps[] = 'do_not_allow';
460 } elseif ( 'upload_themes' === $cap ) {
461 $caps[] = 'install_themes';
462 } elseif ( 'upload_plugins' === $cap ) {
463 $caps[] = 'install_plugins';
464 } else {
465 $caps[] = $cap;
466 }
467 break;
468 case 'install_languages':
469 case 'update_languages':
470 if ( ! wp_is_file_mod_allowed( 'can_install_language_pack' ) ) {
471 $caps[] = 'do_not_allow';
472 } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
473 $caps[] = 'do_not_allow';
474 } else {
475 $caps[] = 'install_languages';
476 }
477 break;
478 case 'activate_plugins':
479 case 'deactivate_plugins':
480 case 'activate_plugin':
481 case 'deactivate_plugin':
482 $caps[] = 'activate_plugins';
483 if ( is_multisite() ) {
484 // update_, install_, and delete_ are handled above with is_super_admin().
485 $menu_perms = get_site_option( 'menu_items', array() );
486 if ( empty( $menu_perms['plugins'] ) ) {
487 $caps[] = 'manage_network_plugins';
488 }
489 }
490 break;
491 case 'resume_plugin':
492 $caps[] = 'resume_plugins';
493 break;
494 case 'resume_theme':
495 $caps[] = 'resume_themes';
496 break;
497 case 'delete_user':
498 case 'delete_users':
499 // If multisite only super admins can delete users.
500 if ( is_multisite() && ! is_super_admin( $user_id ) ) {
501 $caps[] = 'do_not_allow';
502 } else {
503 $caps[] = 'delete_users'; // delete_user maps to delete_users.
504 }
505 break;
506 case 'create_users':
507 if ( ! is_multisite() ) {
508 $caps[] = $cap;
509 } elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) ) {
510 $caps[] = $cap;
511 } else {
512 $caps[] = 'do_not_allow';
513 }
514 break;
515 case 'manage_links':
516 if ( get_option( 'link_manager_enabled' ) ) {
517 $caps[] = $cap;
518 } else {
519 $caps[] = 'do_not_allow';
520 }
521 break;
522 case 'customize':
523 $caps[] = 'edit_theme_options';
524 break;
525 case 'delete_site':
526 if ( is_multisite() ) {
527 $caps[] = 'manage_options';
528 } else {
529 $caps[] = 'do_not_allow';
530 }
531 break;
532 case 'edit_term':
533 case 'delete_term':
534 case 'assign_term':
535 $term_id = (int) $args[0];
536 $term = get_term( $term_id );
537 if ( ! $term || is_wp_error( $term ) ) {
538 $caps[] = 'do_not_allow';
539 break;
540 }
541
542 $tax = get_taxonomy( $term->taxonomy );
543 if ( ! $tax ) {
544 $caps[] = 'do_not_allow';
545 break;
546 }
547
548 if ( 'delete_term' === $cap
549 && ( get_option( 'default_' . $term->taxonomy ) == $term->term_id
550 || get_option( 'default_term_' . $term->taxonomy ) == $term->term_id )
551 ) {
552 $caps[] = 'do_not_allow';
553 break;
554 }
555
556 $taxo_cap = $cap . 's';
557
558 $caps = map_meta_cap( $tax->cap->$taxo_cap, $user_id, $term_id );
559
560 break;
561 case 'manage_post_tags':
562 case 'edit_categories':
563 case 'edit_post_tags':
564 case 'delete_categories':
565 case 'delete_post_tags':
566 $caps[] = 'manage_categories';
567 break;
568 case 'assign_categories':
569 case 'assign_post_tags':
570 $caps[] = 'edit_posts';
571 break;
572 case 'create_sites':
573 case 'delete_sites':
574 case 'manage_network':
575 case 'manage_sites':
576 case 'manage_network_users':
577 case 'manage_network_plugins':
578 case 'manage_network_themes':
579 case 'manage_network_options':
580 case 'upgrade_network':
581 $caps[] = $cap;
582 break;
583 case 'setup_network':
584 if ( is_multisite() ) {
585 $caps[] = 'manage_network_options';
586 } else {
587 $caps[] = 'manage_options';
588 }
589 break;
590 case 'update_php':
591 if ( is_multisite() && ! is_super_admin( $user_id ) ) {
592 $caps[] = 'do_not_allow';
593 } else {
594 $caps[] = 'update_core';
595 }
596 break;
597 case 'update_https':
598 if ( is_multisite() && ! is_super_admin( $user_id ) ) {
599 $caps[] = 'do_not_allow';
600 } else {
601 $caps[] = 'manage_options';
602 $caps[] = 'update_core';
603 }
604 break;
605 case 'export_others_personal_data':
606 case 'erase_others_personal_data':
607 case 'manage_privacy_options':
608 $caps[] = is_multisite() ? 'manage_network' : 'manage_options';
609 break;
610 case 'create_app_password':
611 case 'list_app_passwords':
612 case 'read_app_password':
613 case 'edit_app_password':
614 case 'delete_app_passwords':
615 case 'delete_app_password':
616 $caps = map_meta_cap( 'edit_user', $user_id, $args[0] );
617 break;
618 default:
619 // Handle meta capabilities for custom post types.
620 global $post_type_meta_caps;
621 if ( isset( $post_type_meta_caps[ $cap ] ) ) {
622 return map_meta_cap( $post_type_meta_caps[ $cap ], $user_id, ...$args );
623 }
624
625 // Block capabilities map to their post equivalent.
626 $block_caps = array(
627 'edit_blocks',
628 'edit_others_blocks',
629 'publish_blocks',
630 'read_private_blocks',
631 'delete_blocks',
632 'delete_private_blocks',
633 'delete_published_blocks',
634 'delete_others_blocks',
635 'edit_private_blocks',
636 'edit_published_blocks',
637 );
638 if ( in_array( $cap, $block_caps, true ) ) {
639 $cap = str_replace( '_blocks', '_posts', $cap );
640 }
641
642 // If no meta caps match, return the original cap.
643 $caps[] = $cap;
644 }
645
646 /**
647 * Filters the primitive capabilities required of the given user to satisfy the
648 * capability being checked.
649 *
650 * @since 2.8.0
651 *
652 * @param string[] $caps Primitive capabilities required of the user.
653 * @param string $cap Capability being checked.
654 * @param int $user_id The user ID.
655 * @param array $args Adds context to the capability check, typically
656 * starting with an object ID.
657 */
658 return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
659}
660
661/**
662 * Returns whether the current user has the specified capability.
663 *
664 * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
665 * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
666 * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
667 *
668 * Example usage:
669 *
670 * current_user_can( 'edit_posts' );
671 * current_user_can( 'edit_post', $post->ID );
672 * current_user_can( 'edit_post_meta', $post->ID, $meta_key );
673 *
674 * While checking against particular roles in place of a capability is supported
675 * in part, this practice is discouraged as it may produce unreliable results.
676 *
677 * Note: Will always return true if the current user is a super admin, unless specifically denied.
678 *
679 * @since 2.0.0
680 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
681 * by adding it to the function signature.
682 * @since 5.8.0 Converted to wrapper for the user_can() function.
683 *
684 * @see WP_User::has_cap()
685 * @see map_meta_cap()
686 *
687 * @param string $capability Capability name.
688 * @param mixed ...$args Optional further parameters, typically starting with an object ID.
689 * @return bool Whether the current user has the given capability. If `$capability` is a meta cap and `$object_id` is
690 * passed, whether the current user has the given meta capability for the given object.
691 */
692function current_user_can( $capability, ...$args ) {
693 return user_can( wp_get_current_user(), $capability, ...$args );
694}
695
696/**
697 * Returns whether the current user has the specified capability for a given site.
698 *
699 * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
700 * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
701 * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
702 *
703 * Example usage:
704 *
705 * current_user_can_for_blog( $blog_id, 'edit_posts' );
706 * current_user_can_for_blog( $blog_id, 'edit_post', $post->ID );
707 * current_user_can_for_blog( $blog_id, 'edit_post_meta', $post->ID, $meta_key );
708 *
709 * @since 3.0.0
710 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
711 * by adding it to the function signature.
712 * @since 5.8.0 Wraps current_user_can() after switching to blog.
713 *
714 * @param int $blog_id Site ID.
715 * @param string $capability Capability name.
716 * @param mixed ...$args Optional further parameters, typically starting with an object ID.
717 * @return bool Whether the user has the given capability.
718 */
719function current_user_can_for_blog( $blog_id, $capability, ...$args ) {
720 $switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
721
722 $can = current_user_can( $capability, ...$args );
723
724 if ( $switched ) {
725 restore_current_blog();
726 }
727
728 return $can;
729}
730
731/**
732 * Returns whether the author of the supplied post has the specified capability.
733 *
734 * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
735 * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
736 * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
737 *
738 * Example usage:
739 *
740 * author_can( $post, 'edit_posts' );
741 * author_can( $post, 'edit_post', $post->ID );
742 * author_can( $post, 'edit_post_meta', $post->ID, $meta_key );
743 *
744 * @since 2.9.0
745 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
746 * by adding it to the function signature.
747 *
748 * @param int|WP_Post $post Post ID or post object.
749 * @param string $capability Capability name.
750 * @param mixed ...$args Optional further parameters, typically starting with an object ID.
751 * @return bool Whether the post author has the given capability.
752 */
753function author_can( $post, $capability, ...$args ) {
754 $post = get_post( $post );
755 if ( ! $post ) {
756 return false;
757 }
758
759 $author = get_userdata( $post->post_author );
760
761 if ( ! $author ) {
762 return false;
763 }
764
765 return $author->has_cap( $capability, ...$args );
766}
767
768/**
769 * Returns whether a particular user has the specified capability.
770 *
771 * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
772 * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
773 * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
774 *
775 * Example usage:
776 *
777 * user_can( $user->ID, 'edit_posts' );
778 * user_can( $user->ID, 'edit_post', $post->ID );
779 * user_can( $user->ID, 'edit_post_meta', $post->ID, $meta_key );
780 *
781 * @since 3.1.0
782 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
783 * by adding it to the function signature.
784 *
785 * @param int|WP_User $user User ID or object.
786 * @param string $capability Capability name.
787 * @param mixed ...$args Optional further parameters, typically starting with an object ID.
788 * @return bool Whether the user has the given capability.
789 */
790function user_can( $user, $capability, ...$args ) {
791 if ( ! is_object( $user ) ) {
792 $user = get_userdata( $user );
793 }
794
795 if ( empty( $user ) ) {
796 // User is logged out, create anonymous user object.
797 $user = new WP_User( 0 );
798 $user->init( new stdClass );
799 }
800
801 return $user->has_cap( $capability, ...$args );
802}
803
804/**
805 * Retrieves the global WP_Roles instance and instantiates it if necessary.
806 *
807 * @since 4.3.0
808 *
809 * @global WP_Roles $wp_roles WordPress role management object.
810 *
811 * @return WP_Roles WP_Roles global instance if not already instantiated.
812 */
813function wp_roles() {
814 global $wp_roles;
815
816 if ( ! isset( $wp_roles ) ) {
817 $wp_roles = new WP_Roles();
818 }
819 return $wp_roles;
820}
821
822/**
823 * Retrieve role object.
824 *
825 * @since 2.0.0
826 *
827 * @param string $role Role name.
828 * @return WP_Role|null WP_Role object if found, null if the role does not exist.
829 */
830function get_role( $role ) {
831 return wp_roles()->get_role( $role );
832}
833
834/**
835 * Add role, if it does not exist.
836 *
837 * @since 2.0.0
838 *
839 * @param string $role Role name.
840 * @param string $display_name Display name for role.
841 * @param bool[] $capabilities List of capabilities keyed by the capability name,
842 * e.g. array( 'edit_posts' => true, 'delete_posts' => false ).
843 * @return WP_Role|null WP_Role object if role is added, null if already exists.
844 */
845function add_role( $role, $display_name, $capabilities = array() ) {
846 if ( empty( $role ) ) {
847 return;
848 }
849 return wp_roles()->add_role( $role, $display_name, $capabilities );
850}
851
852/**
853 * Remove role, if it exists.
854 *
855 * @since 2.0.0
856 *
857 * @param string $role Role name.
858 */
859function remove_role( $role ) {
860 wp_roles()->remove_role( $role );
861}
862
863/**
864 * Retrieve a list of super admins.
865 *
866 * @since 3.0.0
867 *
868 * @global array $super_admins
869 *
870 * @return string[] List of super admin logins.
871 */
872function get_super_admins() {
873 global $super_admins;
874
875 if ( isset( $super_admins ) ) {
876 return $super_admins;
877 } else {
878 return get_site_option( 'site_admins', array( 'admin' ) );
879 }
880}
881
882/**
883 * Determine if user is a site admin.
884 *
885 * @since 3.0.0
886 *
887 * @param int|false $user_id Optional. The ID of a user. Defaults to false, to check the current user.
888 * @return bool Whether the user is a site admin.
889 */
890function is_super_admin( $user_id = false ) {
891 if ( ! $user_id || get_current_user_id() == $user_id ) {
892 $user = wp_get_current_user();
893 } else {
894 $user = get_userdata( $user_id );
895 }
896
897 if ( ! $user || ! $user->exists() ) {
898 return false;
899 }
900
901 if ( is_multisite() ) {
902 $super_admins = get_super_admins();
903 if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins, true ) ) {
904 return true;
905 }
906 } else {
907 if ( $user->has_cap( 'delete_users' ) ) {
908 return true;
909 }
910 }
911
912 return false;
913}
914
915/**
916 * Grants Super Admin privileges.
917 *
918 * @since 3.0.0
919 *
920 * @global array $super_admins
921 *
922 * @param int $user_id ID of the user to be granted Super Admin privileges.
923 * @return bool True on success, false on failure. This can fail when the user is
924 * already a super admin or when the `$super_admins` global is defined.
925 */
926function grant_super_admin( $user_id ) {
927 // If global super_admins override is defined, there is nothing to do here.
928 if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
929 return false;
930 }
931
932 /**
933 * Fires before the user is granted Super Admin privileges.
934 *
935 * @since 3.0.0
936 *
937 * @param int $user_id ID of the user that is about to be granted Super Admin privileges.
938 */
939 do_action( 'grant_super_admin', $user_id );
940
941 // Directly fetch site_admins instead of using get_super_admins().
942 $super_admins = get_site_option( 'site_admins', array( 'admin' ) );
943
944 $user = get_userdata( $user_id );
945 if ( $user && ! in_array( $user->user_login, $super_admins, true ) ) {
946 $super_admins[] = $user->user_login;
947 update_site_option( 'site_admins', $super_admins );
948
949 /**
950 * Fires after the user is granted Super Admin privileges.
951 *
952 * @since 3.0.0
953 *
954 * @param int $user_id ID of the user that was granted Super Admin privileges.
955 */
956 do_action( 'granted_super_admin', $user_id );
957 return true;
958 }
959 return false;
960}
961
962/**
963 * Revokes Super Admin privileges.
964 *
965 * @since 3.0.0
966 *
967 * @global array $super_admins
968 *
969 * @param int $user_id ID of the user Super Admin privileges to be revoked from.
970 * @return bool True on success, false on failure. This can fail when the user's email
971 * is the network admin email or when the `$super_admins` global is defined.
972 */
973function revoke_super_admin( $user_id ) {
974 // If global super_admins override is defined, there is nothing to do here.
975 if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
976 return false;
977 }
978
979 /**
980 * Fires before the user's Super Admin privileges are revoked.
981 *
982 * @since 3.0.0
983 *
984 * @param int $user_id ID of the user Super Admin privileges are being revoked from.
985 */
986 do_action( 'revoke_super_admin', $user_id );
987
988 // Directly fetch site_admins instead of using get_super_admins().
989 $super_admins = get_site_option( 'site_admins', array( 'admin' ) );
990
991 $user = get_userdata( $user_id );
992 if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) {
993 $key = array_search( $user->user_login, $super_admins, true );
994 if ( false !== $key ) {
995 unset( $super_admins[ $key ] );
996 update_site_option( 'site_admins', $super_admins );
997
998 /**
999 * Fires after the user's Super Admin privileges are revoked.
1000 *
1001 * @since 3.0.0
1002 *
1003 * @param int $user_id ID of the user Super Admin privileges were revoked from.
1004 */
1005 do_action( 'revoked_super_admin', $user_id );
1006 return true;
1007 }
1008 }
1009 return false;
1010}
1011
1012/**
1013 * Filters the user capabilities to grant the 'install_languages' capability as necessary.
1014 *
1015 * A user must have at least one out of the 'update_core', 'install_plugins', and
1016 * 'install_themes' capabilities to qualify for 'install_languages'.
1017 *
1018 * @since 4.9.0
1019 *
1020 * @param bool[] $allcaps An array of all the user's capabilities.
1021 * @return bool[] Filtered array of the user's capabilities.
1022 */
1023function wp_maybe_grant_install_languages_cap( $allcaps ) {
1024 if ( ! empty( $allcaps['update_core'] ) || ! empty( $allcaps['install_plugins'] ) || ! empty( $allcaps['install_themes'] ) ) {
1025 $allcaps['install_languages'] = true;
1026 }
1027
1028 return $allcaps;
1029}
1030
1031/**
1032 * Filters the user capabilities to grant the 'resume_plugins' and 'resume_themes' capabilities as necessary.
1033 *
1034 * @since 5.2.0
1035 *
1036 * @param bool[] $allcaps An array of all the user's capabilities.
1037 * @return bool[] Filtered array of the user's capabilities.
1038 */
1039function wp_maybe_grant_resume_extensions_caps( $allcaps ) {
1040 // Even in a multisite, regular administrators should be able to resume plugins.
1041 if ( ! empty( $allcaps['activate_plugins'] ) ) {
1042 $allcaps['resume_plugins'] = true;
1043 }
1044
1045 // Even in a multisite, regular administrators should be able to resume themes.
1046 if ( ! empty( $allcaps['switch_themes'] ) ) {
1047 $allcaps['resume_themes'] = true;
1048 }
1049
1050 return $allcaps;
1051}
1052
1053/**
1054 * Filters the user capabilities to grant the 'view_site_health_checks' capabilities as necessary.
1055 *
1056 * @since 5.2.2
1057 *
1058 * @param bool[] $allcaps An array of all the user's capabilities.
1059 * @param string[] $caps Required primitive capabilities for the requested capability.
1060 * @param array $args {
1061 * Arguments that accompany the requested capability check.
1062 *
1063 * @type string $0 Requested capability.
1064 * @type int $1 Concerned user ID.
1065 * @type mixed ...$2 Optional second and further parameters, typically object ID.
1066 * }
1067 * @param WP_User $user The user object.
1068 * @return bool[] Filtered array of the user's capabilities.
1069 */
1070function wp_maybe_grant_site_health_caps( $allcaps, $caps, $args, $user ) {
1071 if ( ! empty( $allcaps['install_plugins'] ) && ( ! is_multisite() || is_super_admin( $user->ID ) ) ) {
1072 $allcaps['view_site_health_checks'] = true;
1073 }
1074
1075 return $allcaps;
1076}
1077
1078return;
1079
1080// Dummy gettext calls to get strings in the catalog.
1081/* translators: User role for administrators. */
1082_x( 'Administrator', 'User role' );
1083/* translators: User role for editors. */
1084_x( 'Editor', 'User role' );
1085/* translators: User role for authors. */
1086_x( 'Author', 'User role' );
1087/* translators: User role for contributors. */
1088_x( 'Contributor', 'User role' );
1089/* translators: User role for subscribers. */
1090_x( 'Subscriber', 'User role' );
1091