' . esc_html__( 'This page lists all current roles and their enabled capabilities.', 'geniem-roles' ) . '
';
486 |
487 | // Do not list cap if in $legacy_caps
488 | $legacy_caps = [
489 | 'level_10',
490 | 'level_9',
491 | 'level_8',
492 | 'level_7',
493 | 'level_6',
494 | 'level_5',
495 | 'level_4',
496 | 'level_3',
497 | 'level_2',
498 | 'level_1',
499 | 'level_0',
500 | ];
501 |
502 | if ( ! empty( $wp_roles->roles ) ) {
503 |
504 | echo '
'; // wrapper ends
534 | }
535 |
536 | /**
537 | * Geniem roles menu items slug list.
538 | *
539 | * @return void
540 | */
541 | public static function geniem_roles_slug_html() {
542 |
543 | $menu_list = self::get_menu_list();
544 |
545 | echo '
';
546 | echo '
' . esc_html__( 'Geniem roles', 'geniem-roles' ) . '
';
547 | echo '
' . esc_html__( 'This page lists all admin menu slugs.', 'geniem-roles' ) . '
';
548 | echo '
';
549 | echo '
';
550 | echo '
';
551 |
552 | foreach ( $menu_list as $menu ) {
553 | echo '';
554 | echo '' . esc_html( $menu->label ) . ' | ';
555 | echo '' . esc_html( $menu->path ) . ' | ';
556 | echo '
';
557 |
558 | foreach ( $menu->children as $child_menu ) {
559 | echo '';
563 | }
564 | }
565 | echo '
';
566 |
567 | echo '
';
568 | echo '
';
569 | echo '
';
570 | }
571 |
572 | /**
573 | * Get menu list.
574 | *
575 | * @return array An array of admin menu items.
576 | */
577 | public static function get_menu_list() {
578 | global $menu, $submenu;
579 |
580 | $menu_list = [];
581 | foreach ( $menu as $i => $menu_data ) {
582 | if ( $menu_data[0] ) {
583 | $parent_menu = (object) [
584 | 'label' => strip_tags( $menu_data[0] ),
585 | 'path' => $menu_data[2],
586 | 'children' => [],
587 | ];
588 |
589 | $menu_list[ $parent_menu->path ] = $parent_menu;
590 | }
591 | }
592 | foreach ( $submenu as $i => $menu_data ) {
593 | if ( array_key_exists( $i, $menu_list ) ) {
594 | $sub_menus = array_map( function( $menu ) {
595 | $item = (object) [
596 | 'label' => strip_tags( $menu[0] ),
597 | 'path' => $menu[2],
598 | ];
599 |
600 | return $item;
601 |
602 | }, $menu_data);
603 | $menu_list[ $i ]->children = $sub_menus;
604 | }
605 | }
606 |
607 | return $menu_list;
608 | }
609 |
610 | /**
611 | * Add function to map_meta_cap which disallows certain actions for role in specifed posts.
612 | *
613 | * @param string $name WP Role name.
614 | * @param array $blocked_posts Blocked posts.
615 | * @param string|array $capabilities Capabilities which is disallowed for the user.
616 | * @return void
617 | */
618 | public static function restrict_post_edit( $name, $blocked_posts, $capabilities ) {
619 |
620 | // Fail fast.
621 | if ( empty( $capabilities ) ) {
622 | return;
623 | }
624 |
625 | $current_user = wp_get_current_user();
626 |
627 | // Add function to map_meta_cap which disallows certain actions for role in specifed posts.
628 | // Check if we need to restrict current user.
629 | if ( in_array( $name, $current_user->roles, true ) ) {
630 |
631 | /**
632 | * Map_meta_cap arguments.
633 | *
634 | * $caps (array) Returns the user's actual capabilities.
635 | * $cap (string) Capabilities name.
636 | * $user_id (int) The user ID.
637 | * $args (array) Adds the context to the cap. Typically the object ID.
638 | */
639 | \add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id, $args ) use ( $blocked_posts, $name, $capabilities ) {
640 |
641 | if ( is_array( $capabilities ) ) {
642 |
643 | // $args[0] is the post id.
644 | if ( in_array( $cap, $capabilities ) && in_array( $args[0], $blocked_posts, true ) ) {
645 | // This is default Wordpress way to restrict access.
646 | $caps[] = 'do_not_allow';
647 | }
648 | }
649 | else {
650 |
651 | // $args[0] is the post id.
652 | if ( $cap === $capabilities && in_array( $args[0], $blocked_posts, true ) ) {
653 | // This is default Wordpress way to restrict access.
654 | $caps[] = 'do_not_allow';
655 | }
656 | }
657 |
658 | return $caps;
659 | }, 10, 4 );
660 | }
661 | }
662 |
663 | /**
664 | * Grant rights for the role by the given post ids and capabilities.
665 | *
666 | * @param string $name WP Role name.
667 | * @param array $blocked_posts Blocked posts.
668 | * @param array $granted_posts_caps Capabilities which is allowed for the user.
669 | * @param array $restricted_posts_caps (optional) Capabilities which is allowed for the restricted posts.
670 | * @param array $post_types (optional) Post types to restrict. If defined other post types won't be handled.
671 | * @return void|false On fail returns false.
672 | */
673 | public static function grant_post_edit( $name, $granted_posts, $granted_posts_caps, $restricted_posts_caps, $post_types ) {
674 |
675 | // Fail fast.
676 | if ( empty( $granted_posts_caps ) || ! is_array( $granted_posts_caps ) ) {
677 | return false;
678 | }
679 |
680 | $current_user = wp_get_current_user();
681 |
682 | // Add function to map_meta_cap which disallows certain actions for role in specifed posts.
683 | // Check if we need to restrict current user.
684 | if ( in_array( $name, $current_user->roles, true ) ) {
685 |
686 | /**
687 | * Map_meta_cap arguments.
688 | *
689 | * $caps (array) Returns the user's actual capabilities.
690 | * $cap (string) Capabilities name.
691 | * $user_id (int) The user ID.
692 | * $args (array) Adds the context to the cap. Typically the object ID.
693 | */
694 | \add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id, $args ) use ( $granted_posts, $name, $granted_posts_caps, $restricted_posts_caps, $post_types ) {
695 |
696 | // If we are not handling a post type capability.
697 | if ( ! in_array( $cap, self::get_all_post_type_caps() ) ) {
698 | return $caps;
699 | }
700 |
701 | // Note $args[0] is empty on post list at first.
702 | if ( empty( $args[0] ) ) {
703 | return $caps;
704 | }
705 |
706 | // Check if the $post_types parameter has been defined.
707 | if ( ! empty( $post_types ) ) {
708 |
709 | $current_post_type = \get_post( $args[0] )->post_type ?? '';
710 |
711 | // If we are not handling a post with desired post type skip the handling.
712 | if ( ! in_array( $current_post_type, $post_types, true ) ) {
713 |
714 | return $caps;
715 | }
716 | }
717 |
718 | // Restricted posts
719 | // If post is not in the granted_posts restrict it.
720 | if ( ! in_array( $args[0], $granted_posts, true ) ) {
721 |
722 | // If restricted posts has some caps the need to match the current cap.
723 | if ( ! empty( $restricted_posts_caps ) ) {
724 | if ( ! in_array( $cap, $restricted_posts_caps ) ) {
725 | $caps[] = 'do_not_allow';
726 | }
727 | }
728 | // If not given specific caps then we need to restrict all.
729 | else {
730 | $caps[] = 'do_not_allow';
731 | }
732 | }
733 | // Granted posts
734 | // If cap not matching -> do_not_allow.
735 | else {
736 |
737 | if ( ! in_array( $cap, $granted_posts_caps ) ) {
738 | $caps[] = 'do_not_allow';
739 | }
740 | }
741 |
742 | return $caps;
743 | }, 10, 4 );
744 | }
745 | }
746 |
747 | /**
748 | * Get all post type caps.
749 | *
750 | * @return array Array of post type capabilities.
751 | */
752 | private static function get_all_post_type_caps() {
753 |
754 | global $wp_post_types;
755 |
756 | $all_caps = [];
757 |
758 | if ( empty( $wp_post_types ) ) {
759 | return [];
760 | }
761 |
762 | foreach ( $wp_post_types as $post_type ) {
763 |
764 | $post_type_caps = array_values( (array) $post_type->cap ) ?? [];
765 | $all_caps = array_merge( $all_caps, $post_type_caps );
766 | }
767 |
768 | return array_unique( $all_caps );
769 | }
770 |
771 | /**
772 | * Prevents user to create and manage users by the given user roles and capabilities.
773 | * Removes role from the admin side dropdowns if
774 | * 'edit_user' or 'promote_user' has been restricted.
775 | *
776 | * Example usage:
777 | * 'administrator' => [
778 | * 'add_user',
779 | * 'edit_user',
780 | * 'delete_user',
781 | * 'remove_user',
782 | * ],
783 | *
784 | * @param string $name Name of the role.
785 | * @param array $removed_user_caps_by_role Associative array of role specific restricted caps.
786 | * @return void
787 | */
788 | public static function restrict_user_management_by_role( $name, $removed_user_caps_by_role ) {
789 |
790 | // Get current user.
791 | $current_user = wp_get_current_user();
792 | $current_user_roles = $current_user->roles;
793 |
794 | // Remove restricted role from the role lists in the admin side.
795 | \add_filter( 'editable_roles', function ( $roles ) use ( $name, $removed_user_caps_by_role, $current_user_roles ) {
796 |
797 | // If current users role is the smae as the edited one.
798 | if ( in_array( $name, $current_user_roles, true ) ) {
799 |
800 | // Loop through restricted user roles.
801 | foreach ( $removed_user_caps_by_role as $role => $restricted_caps ) {
802 |
803 | $edit_user_caps = [
804 | 'add_user',
805 | 'edit_user',
806 | 'promote_user',
807 | ];
808 |
809 | // Loop through restricted caps.
810 | foreach ( $restricted_caps as $cap ) {
811 | if ( in_array( $cap, $edit_user_caps ) && isset( $roles[ $role ] ) ) {
812 | // Unset the role from editable roles.
813 | unset( $roles[ $role ] );
814 | }
815 | }
816 | } // End foreach().
817 | } // End if().
818 |
819 | return $roles;
820 |
821 | }); // End filter editable_roles.
822 |
823 | // If current users role is the same as the edited one.
824 | if ( in_array( $name, $current_user_roles, true ) ) {
825 |
826 | // Restrict user to manage users with given $removed_user_caps_by_role.
827 | \add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id, $args ) use ( $name, $removed_user_caps_by_role ) {
828 |
829 | // Loop through the roles and their caps.
830 | foreach ( $removed_user_caps_by_role as $role => $restricted_caps ) {
831 |
832 | // Check that array of caps have been assigned to the role.
833 | if ( ! empty( $restricted_caps ) && is_array( $restricted_caps ) ) {
834 |
835 | // Loop through roles restricted capabilities.
836 | foreach ( $restricted_caps as $restricted_cap ) {
837 |
838 | // Map meta cap switch case for user capabilities.
839 | if ( $cap === $restricted_cap ) {
840 |
841 | // Currently edited user.
842 | $edited_user = new \WP_User( absint( $args[0] ) );
843 |
844 | if ( in_array( $role, $edited_user->roles ) ) {
845 | $caps[] = 'do_not_allow';
846 | }
847 | } // End if().
848 | } // End foreach().
849 | } // End if().
850 | } // End foreach().
851 |
852 | return $caps;
853 |
854 | }, 10, 4 ); // End map_meta_cap.
855 | } // End if().
856 | }
857 |
858 | /**
859 | * Helper function reset default WordPress roles.
860 | *
861 | * @return void
862 | */
863 | public static function reset_to_default_roles() {
864 | require_once( ABSPATH . 'wp-admin/includes/schema.php' );
865 | \populate_roles();
866 | }
867 |
868 | /**
869 | * Reset roles from the database.
870 | * Run this before your role changes on your theme.
871 | *
872 | * @return void
873 | */
874 | public static function reset_roles() {
875 |
876 | global $wp_roles;
877 |
878 | // Check if multisite and roles reset is requested from CLI
879 | // Reset roles on all sites
880 | if ( \is_multisite() && defined( 'WP_CLI' ) && WP_CLI ) {
881 | // Get the sites. Default amount is 100 and there is no flag to list all sites so we have to use
882 | // high number here. Maybe would be better to use custom SQL query later if there is going to be
883 | // huge amounts of sites.
884 | $sites_args = [
885 | 'number' => 1000,
886 | ];
887 | $sites = \get_sites( $sites_args );
888 | foreach ( $sites as $site ) {
889 | \set_time_limit( 30 );
890 | \switch_to_blog( $site->blog_id );
891 |
892 | foreach ( $wp_roles->roles as $role_name => $role ) {
893 | \remove_role( $role_name );
894 | }
895 |
896 | // Create and define WordPress default roles.
897 | Roles::reset_to_default_roles();
898 | }
899 | \restore_current_blog();
900 |
901 | }
902 | else {
903 | // Single site
904 | foreach ( $wp_roles->roles as $role_name => $role ) {
905 | \remove_role( $role_name );
906 | }
907 |
908 | // Create and define WordPress default roles.
909 | Roles::reset_to_default_roles();
910 | }
911 | }
912 |
913 | /**
914 | * Restrict role templates.
915 | * If called restricts other templates than added templates from the given role.
916 | * Note!: WordPress doesn't handle default template the same way as other templates.
917 | * Default template will always be available for the users. If you want to enable only default template add string 'default' as a parameter.
918 | *
919 | * @param string $name Role name.
920 | * @param array|string $allowed_templates Array of templates to be added for the role. 'default' If we want to enable only the default template for the role.
921 | * @return void|false False on failure. No return if success.
922 | */
923 | public static function restrict_role_templates( $name, $allowed_templates ) {
924 |
925 | // Fail fast.
926 | if ( empty( $allowed_templates ) ) {
927 | return false;
928 | }
929 |
930 | $current_user = wp_get_current_user();
931 |
932 | // Check if we need to restrict current user.
933 | if ( in_array( $name, $current_user->roles, true ) ) {
934 |
935 | /**
936 | * Filters list of page templates for a theme.
937 | *
938 | * @since 4.9.6
939 | *
940 | * @param string[] $post_templates Array of page templates. Keys are filenames,
941 | * values are translated names.
942 | */
943 | add_filter( 'theme_templates', function( $post_templates ) use ( $allowed_templates ) {
944 |
945 | // If we want to add multiple templates for the role.
946 | if ( is_array( $allowed_templates ) ) {
947 | foreach ( $post_templates as $template_file => &$template_name ) {
948 |
949 | // If the template isn't in the allowed_templates remove it from the current user.
950 | if ( ! in_array( $template_file, $allowed_templates ) ) {
951 |
952 | unset( $post_templates[ $template_file ] );
953 | }
954 | }
955 | }
956 | else {
957 |
958 | // WordPress doesn't handle default template the same way as other templates.
959 | // Default template will always be available for the users.
960 | // Check if we wan't to enable only default template.
961 | if ( empty( $post_templates === 'default' ) ) {
962 |
963 | // Set post templates empty here.
964 | $post_templates = [];
965 | }
966 | }
967 |
968 | return $post_templates;
969 | });
970 | }
971 | }
972 | }
973 |
974 | /**
975 | * Class Role which handles a intance of a single editable role
976 | */
977 | class Role {
978 |
979 | /**
980 | * Role name for role identification.
981 | *
982 | * @var string Role name.
983 | */
984 | public $name;
985 |
986 | /**
987 | * Role display name shown for the admin user.
988 | *
989 | * @var string Role display name.
990 | */
991 | public $display_name;
992 |
993 | /**
994 | * Role capabilities.
995 | *
996 | * @var array
997 | */
998 | public $capabilities;
999 |
1000 | /**
1001 | * Get default caps for roles
1002 | *
1003 | * @return array Default capabilities.
1004 | */
1005 | public static function get_default_caps() {
1006 |
1007 | // Default to empty list.
1008 | $defaults = [];
1009 |
1010 | // filter default caps
1011 | return \apply_filters( 'geniem/roles/default_roles', $defaults );
1012 | }
1013 |
1014 | /**
1015 | * Role constructor.
1016 | *
1017 | * @param string $name Role name.
1018 | * @param string $display_name Display name for the role.
1019 | */
1020 | public function __construct( $name, $display_name ) {
1021 |
1022 | // Get WordPress role.
1023 | $role = \get_role( $name );
1024 |
1025 | // Get role
1026 | if ( $role ) {
1027 | // Set role properties..
1028 | $this->capabilities = $role->capabilities;
1029 | $this->name = $role->name;
1030 | $this->display_name = $display_name;
1031 |
1032 | // Create a new role and set role properties.
1033 | } else {
1034 | $this->capabilities = self::get_default_caps();
1035 | $this->name = $name;
1036 | $this->display_name = $display_name;
1037 | }
1038 | }
1039 |
1040 | /**
1041 | * Remove a role.
1042 | *
1043 | * @return void
1044 | */
1045 | public function remove() {
1046 | Roles::remove_role( $this->name );
1047 | }
1048 |
1049 | /**
1050 | * Remove menu pages.
1051 | *
1052 | * @param array $menu_pages Mixed array of removable admin menu items.
1053 | * Array value can be a string or
1054 | * assoaciative array item 'parent_slug' => [ 'submenu_item1_slug', 'submenu_item2_slug' ].
1055 | * @return void
1056 | */
1057 | public function remove_menu_pages( $menu_pages ) {
1058 | Roles::remove_menu_pages( $this->name, $menu_pages );
1059 | }
1060 |
1061 | /**
1062 | * Remove admin bar nodes.
1063 | *
1064 | * @param array $nodes An array of removable nodes.
1065 | * @return void
1066 | */
1067 | public function remove_admin_bar_nodes( $nodes ) {
1068 | Roles::remove_admin_bar_nodes( $this->name, $nodes );
1069 | }
1070 |
1071 | /**
1072 | * Add capabilities for a role
1073 | * Makes db changes do not run everytime.
1074 | *
1075 | * @param array $caps An array of capabilities.
1076 | * @return void
1077 | */
1078 | public function add_caps( $caps ) {
1079 | Roles::add_caps( $this->name, $caps );
1080 | }
1081 |
1082 | /**
1083 | * Remove capabilities for a role
1084 | * Makes db changes do not run everytime.
1085 | *
1086 | * @param array $caps An array of capabilities to be removed.
1087 | */
1088 | public function remove_caps( $caps ) {
1089 | Roles::remove_caps( $this->name, $caps );
1090 | }
1091 |
1092 | /**
1093 | * Get all caps from a role.
1094 | *
1095 | * @return array Role capabilities.
1096 | */
1097 | public function get_caps() {
1098 | return \get_role( $this->name )->capabilities;
1099 | }
1100 |
1101 | /**
1102 | * Rename a role.
1103 | *
1104 | * @param string $new_display_name Display name for a role.
1105 | * @return void
1106 | */
1107 | public function rename( $new_display_name ) {
1108 | return Roles::rename( $this->name, $new_display_name );
1109 | }
1110 |
1111 | /**
1112 | * Restrict post editing capabilities by post ids.
1113 | *
1114 | * @param array $blocked_posts An array of blocked post ids.
1115 | * @param string|array $capability Capability to restrict for the role.
1116 | * @return void
1117 | */
1118 | public function restrict_post_edit( $blocked_posts, $capability ) {
1119 | return Roles::restrict_post_edit( $this->name, $blocked_posts, $capability );
1120 | }
1121 |
1122 | /**
1123 | * Grant post editing capabilities by post ids.
1124 | *
1125 | * @param array $granted_posts An array of blocked post ids.
1126 | * @param array $granted_posts_caps Capability to allow for the role.
1127 | * @param array $restricted_posts_caps (optional) Capabilities which is allowed for the restricted posts.
1128 | * @param array $post_types (optional) Post types to restrict. If defined other post types won't be handled.
1129 | * @return void
1130 | */
1131 | public function grant_post_edit( $granted_posts, $granted_posts_caps, $restricted_posts_caps = [], $post_types = [] ) {
1132 | return Roles::grant_post_edit( $this->name, $granted_posts, $granted_posts_caps, $restricted_posts_caps, $post_types );
1133 | }
1134 |
1135 | /**
1136 | * Restrict user management capabilities for the given role object.
1137 | *
1138 | * Example usage:
1139 | * 'administrator' => [
1140 | * 'add_user',
1141 | * 'edit_user',
1142 | * 'delete_user',
1143 | * 'remove_user',
1144 | * ],
1145 | *
1146 | * @param array $removed_user_caps_by_role Associative array of role specific restricted caps.
1147 | * @return void
1148 | */
1149 | public function restrict_user_management_by_role( $removed_user_caps_by_role ) {
1150 | return Roles::restrict_user_management_by_role( $this->name, $removed_user_caps_by_role );
1151 | }
1152 |
1153 | /**
1154 | * Restrict role templates.
1155 | *
1156 | * @param array|string $allowed_templates Array of templates to be added for the role. 'default' If we want to enable only the default template for the role.
1157 | * @return void|false False on failure. No return if success.
1158 | */
1159 | public function restrict_role_templates( $allowed_templates ) {
1160 | return Roles::restrict_role_templates( $this->name, $allowed_templates );
1161 | }
1162 | }
1163 |
1164 | /**
1165 | * Returns the Geniem Roles singleton.
1166 | *
1167 | * @return object
1168 | */
1169 | function roles() {
1170 | return Roles::instance();
1171 | }
1172 |
1173 | // Create Geniem role singleton.
1174 | roles();
1175 |
--------------------------------------------------------------------------------