| 1 |
<?php |
| 2 |
|
| 3 |
namespace Templately\Admin; |
| 4 |
|
| 5 |
use Templately\Utils\Base; |
| 6 |
use Templately\Utils\Database; |
| 7 |
use Templately\Utils\Options; |
| 8 |
|
| 9 |
class Roles extends Base { |
| 10 |
/** |
| 11 |
* Capability guarding the Theme Builder: creating, editing, publishing and |
| 12 |
* assigning display conditions to `templately_library` templates. |
| 13 |
* |
| 14 |
* A theme-builder template owns what every visitor sees in the header, footer, |
| 15 |
* archive and single slots, so it is site-structure control — not authoring of |
| 16 |
* one's own post content. It has always been declared administrator-only in |
| 17 |
* defaults_capabilities() below; until now it was declared and never checked, |
| 18 |
* and the feature gated on `delete_posts`/`publish_post`/`edit_posts` instead, |
| 19 |
* all of which the Author role holds by default. |
| 20 |
*/ |
| 21 |
const CAP_BUILDER = 'edit_templately_builder'; |
| 22 |
|
| 23 |
/** |
| 24 |
* Capability guarding Templately settings. |
| 25 |
*/ |
| 26 |
const CAP_SETTINGS = 'edit_templately_settings'; |
| 27 |
|
| 28 |
/** |
| 29 |
* Database class |
| 30 |
* @var Database |
| 31 |
*/ |
| 32 |
protected $database; |
| 33 |
|
| 34 |
/** |
| 35 |
* Options class |
| 36 |
* @var Options |
| 37 |
*/ |
| 38 |
protected $options; |
| 39 |
|
| 40 |
public function __construct() { |
| 41 |
$this->database = new Database(); |
| 42 |
$this->options = new Options(); |
| 43 |
} |
| 44 |
|
| 45 |
/** |
| 46 |
* Runtime hooks. Registered on every request (from Maintenance::init()), not |
| 47 |
* only on activation. |
| 48 |
*/ |
| 49 |
public static function init() { |
| 50 |
add_filter( 'user_has_cap', [ __CLASS__, 'grant_caps_to_administrators' ], 10, 4 ); |
| 51 |
} |
| 52 |
|
| 53 |
/** |
| 54 |
* Treat `manage_options` as implying Templately's own capabilities. |
| 55 |
* |
| 56 |
* setup() only runs on activation, so a site that was already active before |
| 57 |
* these capabilities were first wired to an enforcement point would have no |
| 58 |
* administrator holding them — and every gate below would lock the site's own |
| 59 |
* administrators out of the Theme Builder. Deriving them from `manage_options` |
| 60 |
* removes that migration hazard entirely, and grants nothing new: a user with |
| 61 |
* `manage_options` can already assign themselves any capability. Author, |
| 62 |
* Contributor and Editor hold `manage_options` in no default configuration. |
| 63 |
* |
| 64 |
* @param array $allcaps All capabilities of the user. |
| 65 |
* @param array $caps Required primitive capabilities for the requested capability. |
| 66 |
* @param array $args [0] requested capability, [1] user ID, [2] object ID. |
| 67 |
* @param \WP_User $user The user object. |
| 68 |
* |
| 69 |
* @return array |
| 70 |
*/ |
| 71 |
public static function grant_caps_to_administrators( $allcaps, $caps, $args, $user ) { |
| 72 |
if ( empty( $allcaps['manage_options'] ) ) { |
| 73 |
return $allcaps; |
| 74 |
} |
| 75 |
|
| 76 |
$allcaps[ self::CAP_BUILDER ] = true; |
| 77 |
$allcaps[ self::CAP_SETTINGS ] = true; |
| 78 |
|
| 79 |
return $allcaps; |
| 80 |
} |
| 81 |
|
| 82 |
/** |
| 83 |
* Whether the current user may manage Theme Builder templates. |
| 84 |
* |
| 85 |
* The single answer every Theme Builder / display-conditions gate asks, so the |
| 86 |
* bar cannot drift between the ajax handler, the REST controllers and the CPT. |
| 87 |
* |
| 88 |
* @param int|null $user_id Defaults to the current user. |
| 89 |
* |
| 90 |
* @return bool |
| 91 |
*/ |
| 92 |
public static function can_manage_builder( $user_id = null ): bool { |
| 93 |
$can = $user_id === null |
| 94 |
? current_user_can( self::CAP_BUILDER ) |
| 95 |
: user_can( $user_id, self::CAP_BUILDER ); |
| 96 |
|
| 97 |
/** |
| 98 |
* Filter the Theme Builder authorization answer. |
| 99 |
* |
| 100 |
* Sites that deliberately want editors (or another role) managing |
| 101 |
* templates should prefer granting `edit_templately_builder` through the |
| 102 |
* `templately_default_caps` filter; this is the escape hatch for gating |
| 103 |
* that cannot be expressed as a role capability. |
| 104 |
* |
| 105 |
* @param bool $can Whether the user may manage theme-builder templates. |
| 106 |
* @param int|null $user_id User being checked, null for the current user. |
| 107 |
*/ |
| 108 |
return (bool) apply_filters( 'templately_can_manage_builder', $can, $user_id ); |
| 109 |
} |
| 110 |
|
| 111 |
/** |
| 112 |
* Capability map for the `templately_library` post type. |
| 113 |
* |
| 114 |
* Every write/list primitive resolves to CAP_BUILDER, so creating, editing, |
| 115 |
* publishing and deleting a template all require it. `read` is deliberately |
| 116 |
* left at the `post` default — rendering a template on the front end must not |
| 117 |
* require an admin capability. |
| 118 |
* |
| 119 |
* @return array |
| 120 |
*/ |
| 121 |
public static function builder_post_type_capabilities(): array { |
| 122 |
$cap = self::CAP_BUILDER; |
| 123 |
|
| 124 |
return [ |
| 125 |
'create_posts' => $cap, |
| 126 |
'edit_posts' => $cap, |
| 127 |
'edit_others_posts' => $cap, |
| 128 |
'edit_published_posts' => $cap, |
| 129 |
'edit_private_posts' => $cap, |
| 130 |
'publish_posts' => $cap, |
| 131 |
'delete_posts' => $cap, |
| 132 |
'delete_others_posts' => $cap, |
| 133 |
'delete_published_posts' => $cap, |
| 134 |
'delete_private_posts' => $cap, |
| 135 |
'read_private_posts' => $cap, |
| 136 |
]; |
| 137 |
} |
| 138 |
|
| 139 |
/** |
| 140 |
* Default Roles Capabilities |
| 141 |
* |
| 142 |
* @return array |
| 143 |
*/ |
| 144 |
public function defaults_capabilities(): array { |
| 145 |
$default_capabilities = [ |
| 146 |
'administrator' => [ |
| 147 |
self::CAP_BUILDER, |
| 148 |
self::CAP_SETTINGS |
| 149 |
], |
| 150 |
'editor' => [ |
| 151 |
], |
| 152 |
'author' => [ |
| 153 |
], |
| 154 |
'contributor' => [ |
| 155 |
] |
| 156 |
]; |
| 157 |
|
| 158 |
return apply_filters( 'templately_default_caps', $default_capabilities ); |
| 159 |
} |
| 160 |
|
| 161 |
public function setup( $remove = false ) { |
| 162 |
if ( $this->options->get_option( '_templately_caps_initialized', false ) && ! $remove ) { |
| 163 |
return; |
| 164 |
} |
| 165 |
|
| 166 |
global $wp_roles; |
| 167 |
|
| 168 |
$capabilities = $this->defaults_capabilities(); |
| 169 |
|
| 170 |
if( $remove ) { |
| 171 |
unset( $capabilities['administrator'] ); |
| 172 |
} |
| 173 |
|
| 174 |
foreach ( $capabilities as $role => $caps ) { |
| 175 |
foreach ( $caps as $cap ) { |
| 176 |
if ( $remove ) { |
| 177 |
$wp_roles->remove_cap( $role, $cap ); |
| 178 |
continue; |
| 179 |
} |
| 180 |
|
| 181 |
$wp_roles->add_cap( $role, $cap ); |
| 182 |
} |
| 183 |
} |
| 184 |
|
| 185 |
$this->options->update_option( '_templately_caps_initialized', ! $remove ); |
| 186 |
} |
| 187 |
} |
| 188 |
|