add_cap( $cap ); } } $editor = get_role( 'editor' ); if ( null !== $editor ) { $editor->add_cap( self::EDIT_MAP_DATA ); } $snapshot = get_option( self::SNAPSHOT_OPTION ); if ( is_array( $snapshot ) ) { foreach ( $snapshot as $role_name => $caps ) { $role = get_role( (string) $role_name ); if ( null === $role || ! is_array( $caps ) ) { continue; } foreach ( array_intersect( $caps, self::all() ) as $cap ) { $role->add_cap( $cap ); } } } delete_option( self::SNAPSHOT_OPTION ); } /** * Removes the capabilities from every role. Runs on deactivation. * * Swept across all roles rather than the two we granted to, because a site * may have handed `edit_mapsemble_map_data` to a role of its own and a * capability left behind after deactivation is a capability nothing * documents. * * Before the sweep, every role's plugin capabilities are snapshotted to an * option for the next grant() to restore — all roles, the stock ones * included, because a site may have added to those too and the sweep alone * would silently erase its choices. The roles themselves still end the * sweep holding nothing, so a deactivated plugin leaves no capability * behind; only uninstall (which deletes the option) forgets the grants. */ public static function revoke(): void { $snapshot = array(); foreach ( wp_roles()->role_objects as $role_name => $role ) { $held = array_values( array_filter( self::all(), static fn ( string $cap ): bool => $role->has_cap( $cap ) ) ); if ( array() !== $held ) { $snapshot[ $role_name ] = $held; } } if ( array() !== $snapshot ) { update_option( self::SNAPSHOT_OPTION, $snapshot, false ); } foreach ( wp_roles()->role_objects as $role ) { foreach ( self::all() as $cap ) { $role->remove_cap( $cap ); } } } /** * The capability the top-level Mapsemble menu is registered with. * * The menu holds screens with three different capabilities, and WordPress * accepts only one for the parent. Returning the first the current user * actually holds shows the menu to anyone with any business inside it, * while the fallback hides it from everyone else — a plain * `edit_mapsemble_maps` would hide the whole menu from the data editors * the `Maps` screen exists for. */ public static function menu_cap(): string { foreach ( array( self::EDIT_MAP_DATA, self::EDIT_MAPS, self::MANAGE ) as $cap ) { if ( current_user_can( $cap ) ) { return $cap; } } return self::EDIT_MAP_DATA; } }