File: //proc/thread-self/cwd/wp-content/plugins/cornerstone/includes/classes/Services/Permissions.php
<?php
namespace Themeco\Cornerstone\Services;
use Themeco\Cornerstone\Plugin;
class Permissions implements Service {
public $_content_types;
protected $roleDefaults;
protected $cache = array();
protected $elements;
public function defaultPolicy() {
return [
'administrator' => [
'content.page' => true,
'content.post' => false,
'layout' => true,
'component' => true,
'global' => true,
'template' => true,
'element-library' => true,
'preferences' => true,
'global.export_documents' => true,
],
'_default' => array(
'content.{post-type}' => false,
'content.{post-type}.create' => true,
'content.{post-type}.delete' => true,
'content.{post-type}.element_locking' => true,
'content.{post-type}.edit_custom_css' => true,
'content.{post-type}.edit_custom_js' => true,
'layout' => false,
'layout.create' => true,
'layout.delete' => true,
'layout.element_locking' => true,
'layout.edit_custom_css' => true,
'layout.edit_custom_js' => true,
'component' => false,
'component.create' => true,
'component.delete' => true,
'component.element_locking' => true,
'component.edit_custom_css' => true,
'component.edit_custom_js' => true,
'template' => false,
'template.manage_library' => true,
'template.save' => true,
'template.insert' => true,
'template.import_terms' => true,
'template.themeco_templates' => true,
'template.max' => true,
'global' => false,
'global.colors' => true,
'global.fonts' => true,
'global.theme_options' => true,
'global.theme_options_import' => true,
'global.theme_options_export' => true,
'global.export_documents' => false,
'global.edit_custom_css' => true,
'global.edit_custom_js' => true,
'global.element_locking' => true,
'global.apply_preset' => true,
'global.save_preset' => true,
'global.edit_parameters' => true,
'preferences' => true,
'preferences.update' => true,
'element-library' => false,
'element-library.classic' => false,
'element-library.{id}' => true,
'element-library.{id}.inspect' => true,
'element-library.{id}.copy' => true,
'element-library.{id}.paste' => true,
'element-library.{id}.paste_style' => true,
'element-library.{id}.clear_style' => true,
'element-library.{id}.apply_preset' => true,
'element-library.{id}.delete' => true,
'element-library.{id}.manage' => true,
'element-library.{id}.save_preset' => true,
'element-library.{id}.show_in_library' => true,
'element.{id}' => true,
'element.{id}.inspect' => true,
'element.{id}.copy' => true,
'element.{id}.paste' => true,
'element.{id}.paste_style' => true,
'element.{id}.clear_style' => true,
'element.{id}.apply_preset' => true,
'element.{id}.save_preset' => true,
'element.{id}.show_in_library' => true,
)
];
}
public function __construct(Elements $elements) {
$this->elements = $elements;
}
public function getRoleDefaults() {
if ( ! isset( $this->roleDefaults ) ) {
$this->roleDefaults = apply_filters( 'cs_app_permission_defaults', $this->hideDeprecatedElements( $this->defaultPolicy() ) );
}
return $this->roleDefaults;
}
// Deprecated elements permission setups
public function hideDeprecatedElements( $defaults ) {
$definitions = $this->elements->get_all_elements();
$deprecatedEnabled = apply_filters(
"cs_elements_deprecated_enabled_default",
false
);
foreach ($definitions as $name => $definition) {
if ( $definition->get_group() === 'deprecated' ) {
$defaults['_default']["element-library.$name"] = $deprecatedEnabled;
}
}
return $defaults;
}
public function getStoredPermissions() {
$stored = get_option('cs_permissions', []);
$defaults = $this->getRoleDefaults();
$migrated = [];
foreach ($stored as $role => $permissions) {
$migratedRole = [];
$hasLayouts = false;
$canEdit = false;
foreach ($permissions as $permission => $state) {
// Migrate Elements
if (strpos($permission, 'element.' ) === 0) {
$parts = explode('.',$permission);
if (strpos($parts[1], 'classic:' ) !== 0) {
$migratedRole['element-library.' . $parts[1]] = $state;
}
continue;
}
// Migrate Content & Global Blocks
if ( $permission === 'content.cs_global_block') {
$migratedRole['component'] = $state;
continue;
}
if (strpos($permission, 'content.' ) === 0) {
$parts = explode('.',$permission);
$post_type = $parts[1];
if ($post_type !== 'cs_global_block') {
$parts[1] = '{post-type}';
$generic_permission = implode('.', $parts);
if ( isset( $defaults['_default'][$generic_permission])) {
$migratedRole[$permission] = $state;
if ($state) {
$canEdit = true;
}
}
}
continue;
}
// Migrate Headers / Footers to Layouts
if (strpos($permission, 'headers' ) === 0) {
if ($permission === 'headers' && $state ) {
$hasLayouts = true;
$canEdit = true;
}
continue;
}
if (strpos($permission, 'footers' ) === 0) {
if ($permission === 'footers' && $state ) {
$hasLayouts = true;
$canEdit = true;
}
continue;
}
if ( $permission === 'theme_options' ) {
$migratedRole['global'] = $state;
$migratedRole['global.theme_options'] = $state;
continue;
}
if ( $permission === 'theme_options.edit_custom_css' ) {
$migratedRole['global.edit_custom_css'] = $state;
continue;
}
if ( $permission === 'theme_options.edit_custom_js' ) {
$migratedRole['global.edit_custom_js'] = $state;
continue;
}
if ( $permission === 'colors' ) {
$migratedRole['global.colors'] = $state;
continue;
}
if ( $permission === 'fonts' ) {
$migratedRole['global.fonts'] = $state;
continue;
}
if ( isset( $defaults['_default'][$permission] ) || strpos($permission, 'element-library.' ) === 0 ) {
$migratedRole[$permission] = $state;
}
}
if ($hasLayouts && (! isset($defaults[$role]['layout']) || ! $defaults[$role]['layout'])) {
$migratedRole['layout'] = true;
}
// Setup role
$migrated[$role] = $migratedRole;
}
return $migrated;
}
public function getAppData() {
return array(
'roles' => cs_get_wp_roles_options(),
'permissions' => $this->getStoredPermissions(),
'defaults' => $this->getRoleDefaults(),
'controls' => $this->getControls(),
'elements' => $this->getElementOptions(),
);
}
public function updateStoredPermissions($update) {
return empty( $update ) ? delete_option('cs_permissions') : update_option('cs_permissions', $update);
}
// Update singular permission
public function updateStoredPermission($role, $key, $value) {
$permissions = $this->getStoredPermissions();
if (empty($permissions[$role])) {
$permissions[$role] = [];
}
$permissions[$role][$key] = $value;
return $this->updateStoredPermissions($permissions);
}
public function getUserPermissions( $user_id = null ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! $user_id ) {
return apply_filters('cs_permissions_user', array());
}
$user_key = "u$user_id";
$roles = $this->getUserRoles( $user_id );
if ( ! isset( $this->cache[$user_key] ) ) {
$this->cache[$user_key] = $this->getRolePermissions( $roles );
$this->cache[$user_key]['manage_options'] = user_can( $user_id, 'manage_options');
$this->cache[$user_key]['unfiltered_html'] = user_can( $user_id, 'unfiltered_html');
}
return apply_filters('cs_permissions_user', $this->cache[$user_key]);
}
public function userCan( $permission, $user_id = null ) {
if ( ! is_string( $permission ) ) {
return false;
}
$permissions = $this->getUserPermissions( $user_id );
$parts = explode('.', $permission);
$next = array();
$dynamic = array(
'content' => '{post-type}',
'element' => '{id}'
);
$implicit = ['element-library'];
$siteBuilder = ['layout'];
if ( isset( $parts[0]) && in_array( $parts[0], $siteBuilder ) && ! cornerstone('Env')->isSiteBuilder()) {
return false;
}
while ( count( $parts ) ) {
$segment = array_shift( $parts );
$check = implode('.', $next) . ".$segment";
$check = trim($check, '.');
if ( ! isset( $permissions[$check] ) && isset( $next[0] ) && isset( $dynamic[$next[0]] ) ) {
$test = array( $next[0], $dynamic[$next[0]] );
if ( count( $next ) > 1 ) {
$test[] = $segment;
}
$check = implode('.', $test );
}
$next[] = $segment;
$test = isset( $permissions[$check] ) ? $permissions[$check] : null;
if ( ! in_array( $next[0], $implicit, true) ) {
$test = (bool) $test;
}
if ( ! isset( $dynamic[$segment] ) && false === $test ) {
return false;
}
}
return true;
}
public function getUserPostTypes( $user = null ) {
$permissions = $this->getUserPermissions( $user );
$types = array();
if ( is_null( $user ) ) {
$user = get_current_user_id();
}
foreach ($permissions as $key => $value) {
if ( 0 !== strpos( $key, 'content.' ) || ! $value ) {
continue;
}
$parts = explode('.', $key);
if ( count( $parts ) > 2 || in_array( $parts[1], array( '{post-type}', 'cs_global_block'), true ) ) {
continue;
}
// Ensure type exists. (Post type previously saved, but plugin is no longer active)
$post_type = get_post_type_object($parts[1]);
if ( $post_type && user_can( $user, $post_type->cap->edit_posts) ) {
$types[] = $parts[1];
}
}
return $types;
}
public function userCanAccessPostType( $post = '') {
$post_type = '';
if ( is_string( $post ) ) {
$post_type = $post;
}
if ( ! $post ) {
$post = $this->contextualPost();
if ( ! $post ) {
return false;
}
}
if ( is_a( $post, 'WP_Post' ) ) {
$post_type_obj = get_post_type_object( $post->post_type );
$caps = (array) $post_type_obj->cap;
if ( ! current_user_can( $caps['edit_post'], $post->ID ) ) {
return false;
}
$post_type = $post->post_type;
}
$allowed = $this->getUserPostTypes();
return in_array( $post_type, $allowed, true );
}
/**
* administrator helper for roleHasPermission
*/
public function adminHasPermission($permission) {
return $this->roleHasPermission($permission, "administrator");
}
/**
* Given a role does a permission exist for it
*/
public function roleHasPermission($permission, $role) {
$roleDefaults = $this->getRoleDefaults();
$defaults = !empty($roleDefaults[$role]) ? $roleDefaults[$role] : [];
$stored = $this->getStoredPermissions();
$storedRole = !empty($stored[$role]) ? $stored[$role] : [];
$permissions = array_merge($defaults, $storedRole);
return !empty($permissions[$permission]);
}
public function getRolePermissions( $roles ) {
if ( is_string( $roles ) ) {
$roles = array( $roles );
}
$stored = $this->getStoredPermissions();
$role_permissions = array();
$merged = array();
$role_defaults = $this->getRoleDefaults();
foreach ($roles as $role) {
$default_role = isset( $role_defaults[$role] ) ? $role_defaults[$role] : array();
$default_role = array_merge($role_defaults['_default'], $default_role );
$stored_role = isset( $stored[$role] ) ? $stored[$role] : array();
$role_permissions[$role] = array_merge($default_role,$stored_role);
}
// If a user has multiple roles, merge the permissions together.
foreach ($role_permissions as $permissions) {
ksort($permissions);
foreach ($permissions as $key => $value) {
$parts = explode('.', $key);
// If multiple rows share a top level key allow either role to enable the feature
if ( 1 !== count($parts) &&
isset($merged[$parts[0]]) && // Only merge a nested permission if the parent of this role has turned it on
$merged[$parts[0]] !== isset( $permissions[$parts[0]] )
) {
continue;
}
$merged[$key] = ( isset($merged[$key])) ? $merged[$key] || $value : $value;
}
}
ksort( $merged);
return $merged;
}
public function getUserRoles( $user_id ) {
$roles = array();
global $wpdb;
$caps = get_user_meta( $user_id, $wpdb->get_blog_prefix() . 'capabilities', true );
$wp_roles = wp_roles();
if ( is_array( $caps ) ) {
$roles = array_filter( array_keys( $caps ), array( $wp_roles, 'is_role' ) );
}
if ( is_super_admin() && ! in_array( 'administrator', $roles, true ) ) {
$roles[] = 'administrator';
}
return $roles;
}
protected function getPostTypes() {
if ( ! isset( $this->_content_types ) ) {
$post_types = get_post_types( apply_filters( 'cs_get_content_types_args', array(
'public' => true,
'show_ui' => true,
) ), 'objects' );
unset( $post_types['attachment'] );
$this->_content_types = array();
foreach ( $post_types as $name => $post_type ) {
$this->_content_types[$name] = ( isset( $post_type->labels->name ) ) ? $post_type->labels->name : $name;
}
}
return $this->_content_types;
}
public function getControls() {
$post_types = $this->getPostTypes();
$documentTypes = [];
if ( isset( $post_types['page'] ) ) {
$documentTypes['content.page'] = $post_types['page'];
unset( $post_types['page'] ); // ensure page is first
}
foreach ($post_types as $name => $title) {
$documentTypes["content.$name"] = $title;
}
if ( cornerstone('Env')->isSiteBuilder() ) {
$documentTypes['layout'] = csi18n('common.document.layouts');
}
$documentTypes['component'] = csi18n('common.document.components');
$docControls = array();
foreach ($documentTypes as $key => $value) {
$docControls[] = array(
'id' => $key,
'context' => 'document',
'title' => $value,
'options' => $this->getContextKeys( $key )
);
}
$controls = [
[
'id' => 'element-library',
'title' => csi18n('admin.permissions.element-library'),
],
[
'id' => 'template',
'title' => csi18n('admin.permissions.templates'),
'options' => [
'save',
'insert',
'import_terms',
'manage_library',
'themeco_templates',
'max',
]
],
[
'id' => 'global',
'title' => csi18n('admin.permissions.global'),
'options' => [
'theme_options',
'theme_options_import',
'theme_options_export',
'export_documents',
'colors',
'fonts',
'edit_custom_css',
'edit_custom_js',
'apply_preset',
'edit_parameters',
]
],
[
'id' => 'preferences',
'title' => csi18n('app.preferences.title'),
'options' => [
'update',
]
],
];
$controls = array_merge( $docControls, $controls );
return apply_filters('cs_permissions_controls', $controls);
}
public function getElementOptions() {
$options = array();
$context_defaults = $this->getContextKeys('element-library.{id}');
$elements = cornerstone('Elements')->get_public_definitions();
foreach ( $elements as $element ) {
if ( $element->is_classic() ) {
continue;
}
$title = $element->get_title();
$title = $element->get_group() === "deprecated"
? sprintf("%s (Deprecated)", $title)
: $title;
$id = 'element-library.' . $element->get_type();
$options[] = array(
'id' => $id,
'context' => $id,
'title' => $title,
'options' => $context_defaults,
);
}
return $options;
}
public function getContextKeys( $context ) {
$dynamic = array(
'element-library' => 'element-library.{id}',
'content' => 'content.{post-type}'
);
$context_keys = array();
$role_defaults = $this->getRoleDefaults();
$keys = array_keys( $role_defaults['_default'] );
$parts = explode('.', $context);
foreach ($keys as $key) {
if ( 0 === strpos( $key, "$context." ) ) {
$context_keys[] = str_replace("$context.", "",$key );
}
if ( isset($dynamic[$parts[0]]) && 0 === strpos( $key, $dynamic[$parts[0]] . '.' ) ) {
$context_keys[] = str_replace( $dynamic[$parts[0]] . '.', "",$key );
}
}
return array_values( array_unique( $context_keys ) );
}
/**
* Get a WP_Post object from an ID or an automatic source
* If $post_id is left black, it will be automatically populated (works in dashboard or on front end)
* @param string $post_id
* @return \WP_Post
*/
public function contextualPost( $post_id = '') {
// Allow pass through of full post objects
if ( is_object($post_id) && isset( $post_id->ID ) )
return $post_id;
// Get post by ID
if ( is_int( $post_id ) )
return get_post( $post_id );
// Or, in the dashboard use a query string
if ( is_admin() && isset($_GET['post']) )
return get_post( $_GET['post'] );
// Or, use the queried object
if ( '' == $post_id ) {
$post = get_queried_object();
if ( is_a( $post, 'WP_POST' ))
return $post;
}
// Otherwise there's just no way...
return false;
}
public function getPostCapability( $post_id, $cap ) {
$post = $this->contextualPost( $post_id );
if ( ! is_a( $post, 'WP_POST' ) ) {
return $cap;
}
return $this->getPostTypeCapability( $post->post_type, $cap );
}
public function getPostTypeCapability( $post_type, $cap ) {
$post_type_object = get_post_type_object( $post_type );
$caps = (array) $post_type_object->cap;
return $caps[ $cap ];
}
/**
* Helper for checking if has any cornerstone edit permissions
*/
public function userCanEditAnything() {
$permissions = $this->getUserPermissions();
return $this->permissionCanEditAnything($permissions);
}
public function permissionCanEditAnything($permissions) {
return apply_filters('cs_permissions_can_edit_anything', (
!empty($permissions['content.page'])
|| !empty($permissions['content.post'])
|| !empty($permissions['component'])
|| !empty($permissions['layout'])
|| !empty($permissions['template'])
|| !empty($permissions['global'])
));
}
/**
* Migrates template => true if they can edit anything
* This was originally doing something goofy with the permission default
* causing an issue when you actually wanted to disable template => false
*/
public function migrateTemplateEditPermission() {
$roles = wp_roles();
$roles = array_keys($roles->roles);
foreach ($roles as $role) {
$rolePermissions = $this->getRolePermissions($role);
// If can edit and template is not on, turn on
if ($this->permissionCanEditAnything($rolePermissions) && empty($rolePermissions['template'])) {
$this->updateStoredPermission($role, 'template', true);
}
}
}
}