diff --git a/admin/class-addon.php b/admin/class-addon.php new file mode 100644 index 0000000..5947ef1 --- /dev/null +++ b/admin/class-addon.php @@ -0,0 +1,173 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members; + +/** + * Add-on object class. + * + * @since 2.0.0 + * @access public + */ +final class Addon { + + /** + * Name/ID for the addon. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $name = ''; + + /** + * Title of the add-on. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $title = ''; + + /** + * Short description of the add-on. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $excerpt = ''; + + /** + * URL where the add-on can be found. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $url = 'https://themehybrid.com/plugins/members'; + + /** + * Add-on ZIP file URL. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $download_url = ''; + + /** + * Alternate purchase URL. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $purchase_url = ''; + + /** + * URL for a 128x128 (size used by WordPress.org) icon image. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $icon_url = ''; + + /** + * Add-on plugin's author URL. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $author_url = ''; + + /** + * Add-on plugin's author display name. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $author_name = ''; + + /** + * Rating for the add-on. This is the total rating based on a 5-star rating system. + * It will be divided by the rating count, so both must be supplied. + * + * @since 2.0.0 + * @access protected + * @var int + */ + public $rating = ''; + + /** + * Number of ratings. + * + * @since 2.0.0 + * @access protected + * @var int + */ + public $rating_count = 0; + + /** + * Number of active installs. Note that this will be displayed with a `+` at + * the end, such as `100,000+`. Exact counts are necessary. Just a round number. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $install_count = 0; + + /** + * Magic method to use in case someone tries to output the object as a string. + * We'll just return the name. + * + * @since 2.0.0 + * @access public + * @return string + */ + public function __toString() { + return $this->name; + } + + /** + * Register a new object. + * + * @since 2.0.0 + * @access public + * @param string $name + * @param array $args { + * @type string $label Internationalized text label. + * @type string $icon Dashicon icon in the form of `dashicons-icon-name`. + * @type array $caps Array of capabilities in the addon. + * @type bool $merge_added Whether to merge this caps into the added caps array. + * @type bool $diff_added Whether to remove previously-added caps from this addon. + * } + * @return void + */ + public function __construct( $name, $args = array() ) { + + foreach ( array_keys( get_object_vars( $this ) ) as $key ) { + + if ( isset( $args[ $key ] ) ) + $this->$key = $args[ $key ]; + } + + $this->name = sanitize_key( $name ); + + if ( ! $this->icon_url ) + $this->icon_url = members_plugin()->uri . 'img/icon-addon.png'; + } +} diff --git a/admin/class-cap-control.php b/admin/class-cap-control.php index 028f0da..27d1ad6 100644 --- a/admin/class-cap-control.php +++ b/admin/class-cap-control.php @@ -4,25 +4,27 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Cap control class. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Cap_Control { +final class Cap_Control { /** * Stores the cap tabs object. * * @see Members_Cap_Tabs - * @since 1.0.0 + * @since 2.0.0 * @access public * @var object */ @@ -31,7 +33,7 @@ final class Members_Cap_Control { /** * Name of the capability the control is for. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -40,7 +42,7 @@ final class Members_Cap_Control { /** * ID of the section the control is for. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -49,7 +51,7 @@ final class Members_Cap_Control { /** * Array of data to pass as a json object to the Underscore template. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -58,7 +60,7 @@ final class Members_Cap_Control { /** * Creates a new control object. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param object $manager * @param string $cap @@ -80,7 +82,7 @@ public function __construct( $manager, $cap, $args = array() ) { /** * Returns the json array. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return array */ @@ -92,7 +94,7 @@ public function json() { /** * Adds custom data to the json array. This data is passed to the Underscore template. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -112,6 +114,7 @@ public function to_json() { // Set up the input labels. $this->json['label'] = array( + 'cap' => members_show_human_caps() && members_cap_exists( $this->cap ) ? members_get_cap( $this->cap )->label : $this->cap, 'grant' => sprintf( esc_html__( 'Grant %s capability', 'members' ), "{$this->cap}" ), 'deny' => sprintf( esc_html__( 'Deny %s capability', 'members' ), "{$this->cap}" ) ); diff --git a/admin/class-cap-group-factory.php b/admin/class-cap-group-factory.php deleted file mode 100644 index d19d4be..0000000 --- a/admin/class-cap-group-factory.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members - * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - */ - -/** - * Capability group factory class. - * - * @since 1.0.0 - * @access public - */ -final class Members_Cap_Group_Factory { - - /** - * Array of group objects. - * - * @since 1.0.0 - * @access public - * @var array - */ - public $groups = array(); - - /** - * Constructor method. - * - * @since 1.0.0 - * @access private - * @return void - */ - private function __construct() {} - - /** - * Register a new group object - * - * @see Members_Cap_Group::__construct() - * @since 1.0.0 - * @access public - * @param string $name - * @param array $args - * @return void - */ - public function register_group( $name, $args = array() ) { - - if ( ! $this->group_exists( $name ) ) { - - $group = new Members_Cap_Group( $name, $args ); - - $this->groups[ $group->name ] = $group; - } - } - - /** - * Unregisters a group object. - * - * @since 1.0.0 - * @access public - * @param string $name - * @return void - */ - public function unregister_group( $name ) { - - if ( $this->group_exists( $name ) ) - unset( $this->groups[ $name ] ); - } - - /** - * Checks if a group exists. - * - * @since 1.0.0 - * @access public - * @param string $name - * @return bool - */ - public function group_exists( $name ) { - - return isset( $this->groups[ $name ] ); - } - - /** - * Gets a group object. - * - * @since 1.0.0 - * @access public - * @param string $name - * @return object|bool - */ - public function get_group( $name ) { - - return $this->group_exists( $name ) ? $this->groups[ $name ] : false; - } - - /** - * Returns the instance. - * - * @since 1.0.0 - * @access public - * @return object - */ - public static function get_instance() { - - static $instance = null; - - if ( is_null( $instance ) ) - $instance = new Members_Cap_Group_Factory; - - return $instance; - } -} diff --git a/admin/class-cap-section.php b/admin/class-cap-section.php index 06c9596..d53354b 100644 --- a/admin/class-cap-section.php +++ b/admin/class-cap-section.php @@ -4,25 +4,27 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Cap section class. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Cap_Section { +final class Cap_Section { /** * Stores the cap tabs object. * * @see Members_Cap_Tabs - * @since 1.0.0 + * @since 2.0.0 * @access public * @var object */ @@ -31,7 +33,7 @@ final class Members_Cap_Section { /** * ID of the section. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -40,7 +42,7 @@ final class Members_Cap_Section { /** * Dashicons icon for the section. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -49,7 +51,7 @@ final class Members_Cap_Section { /** * Label for the section. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -58,7 +60,7 @@ final class Members_Cap_Section { /** * Array of data to pass as a json object to the Underscore template. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -67,7 +69,7 @@ final class Members_Cap_Section { /** * Creates a new section object. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param object $manager * @param string $section @@ -89,7 +91,7 @@ public function __construct( $manager, $section, $args = array() ) { /** * Returns the json array. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return array */ @@ -101,7 +103,7 @@ public function json() { /** * Adds custom data to the json array. This data is passed to the Underscore template. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ diff --git a/admin/class-cap-tabs.php b/admin/class-cap-tabs.php index 6c728c9..46ab2d3 100644 --- a/admin/class-cap-tabs.php +++ b/admin/class-cap-tabs.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Handles building the edit caps tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Cap_Tabs { +final class Cap_Tabs { /** * The role object that we're creating tabs for. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var object */ @@ -30,7 +32,7 @@ final class Members_Cap_Tabs { /** * Array of caps shown by the cap tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -40,7 +42,7 @@ final class Members_Cap_Tabs { * The caps the role has. Note that if this is a new role (new role screen), the default * new role caps will be passed in. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -49,7 +51,7 @@ final class Members_Cap_Tabs { /** * Array of tab sections. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -58,7 +60,7 @@ final class Members_Cap_Tabs { /** * Array of single cap controls. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -67,7 +69,7 @@ final class Members_Cap_Tabs { /** * Array of section json data. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -76,7 +78,7 @@ final class Members_Cap_Tabs { /** * Array of control json data. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -85,7 +87,7 @@ final class Members_Cap_Tabs { /** * Sets up the cap tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param string $role * @param array $has_caps @@ -99,11 +101,11 @@ public function __construct( $role = '', $has_caps = array() ) { // Check if we have a role. if ( $role ) { - $this->role = get_role( $role ); + $this->role = members_get_role( $role ); // If no explicit caps were passed in, use the role's caps. if ( ! $has_caps ) - $this->has_caps = $this->role->capabilities; + $this->has_caps = $this->role->caps; } // Add sections and controls. @@ -118,7 +120,7 @@ public function __construct( $role = '', $has_caps = array() ) { * Registers the sections (and each section's controls) that will be used for * the tab content. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -127,8 +129,12 @@ public function register() { // Hook before registering. do_action( 'members_pre_edit_caps_manager_register' ); + $groups = members_get_cap_groups(); + + uasort( $groups, 'members_priority_sort' ); + // Get and loop through the available capability groups. - foreach ( members_get_cap_groups() as $group ) { + foreach ( $groups as $group ) { $caps = $group->caps; @@ -137,11 +143,10 @@ public function register() { $caps = array_diff( $group->caps, $this->added_caps ); // Add group's caps to the added caps array. - if ( $group->merge_added ) - $this->added_caps = array_unique( array_merge( $this->added_caps, $caps ) ); + $this->added_caps = array_unique( array_merge( $this->added_caps, $caps ) ); // Create a new section. - $this->sections[] = $section = new Members_Cap_Section( $this, $group->name, array( 'icon' => $group->icon, 'label' => $group->label ) ); + $this->sections[] = $section = new Cap_Section( $this, $group->name, array( 'icon' => $group->icon, 'label' => $group->label ) ); // Get the section json data. $this->sections_json[] = $section->json(); @@ -149,13 +154,28 @@ public function register() { // Create new controls for each cap. foreach ( $caps as $cap ) { - $this->controls[] = $control = new Members_Cap_Control( $this, $cap, array( 'section' => $group->name ) ); + $this->controls[] = $control = new Cap_Control( $this, $cap, array( 'section' => $group->name ) ); // Get the control json data. $this->controls_json[] = $control->json(); } } + // Create a new "All" section. + $this->sections[] = $section = new Cap_Section( $this, 'all', array( 'icon' => 'dashicons-plus', 'label' => esc_html__( 'All', 'members' ) ) ); + + // Get the section json data. + $this->sections_json[] = $section->json(); + + // Create new controls for each cap. + foreach ( $this->added_caps as $cap ) { + + $this->controls[] = $control = new Cap_Control( $this, $cap, array( 'section' => 'all' ) ); + + // Get the control json data. + $this->controls_json[] = $control->json(); + } + // Hook after registering. do_action( 'members_edit_caps_manager_register' ); } @@ -163,7 +183,7 @@ public function register() { /** * Displays the cap tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -171,7 +191,7 @@ public function display() { ?>
-

' ); ?>

+

' ); ?>

@@ -188,7 +208,7 @@ public function display() { ?> /** * Outputs the tab nav. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -212,7 +232,7 @@ public function tab_nav() { ?> /** * Passes our sections and controls data as json to the `edit-role.js` file. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -225,7 +245,7 @@ public function localize_scripts() { /** * Outputs the Underscore JS templates. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ diff --git a/admin/class-manage-roles.php b/admin/class-manage-roles.php index dd2e42d..0d6726d 100644 --- a/admin/class-manage-roles.php +++ b/admin/class-manage-roles.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Role management class. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Admin_Manage_Roles { +final class Manage_Roles { /** * Holds the instances of this class. * - * @since 1.0.0 + * @since 2.0.0 * @access private * @var object */ @@ -30,7 +32,7 @@ final class Members_Admin_Manage_Roles { /** * Name of the page we've created. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -39,7 +41,7 @@ final class Members_Admin_Manage_Roles { /** * The page object to show. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var object */ @@ -48,7 +50,7 @@ final class Members_Admin_Manage_Roles { /** * Sets up our initial actions. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -62,7 +64,7 @@ public function __construct() { /** * Adds the roles page to the admin. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -94,11 +96,11 @@ public function add_admin_page() { // If viewing the edit role page. if ( isset( $_REQUEST['action'] ) && 'edit' === $_REQUEST['action'] && current_user_can( 'edit_roles' ) ) - $this->page_obj = new Members_Admin_Role_Edit(); + $this->page_obj = new Role_Edit(); // If viewing the role list page. else - $this->page_obj = new Members_Admin_Roles(); + $this->page_obj = new Roles(); // Load actions. add_action( "load-{$this->page}", array( $this, 'load' ) ); @@ -111,7 +113,7 @@ public function add_admin_page() { /** * Checks posted data on load and performs actions if needed. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -124,7 +126,7 @@ public function load() { /** * Loads necessary scripts/styles. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param string $hook_suffix * @return void @@ -138,7 +140,7 @@ public function enqueue( $hook_suffix ) { /** * Outputs the page. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -151,7 +153,7 @@ public function page() { /** * Returns the instance. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return object */ @@ -164,4 +166,4 @@ public static function get_instance() { } } -Members_Admin_Manage_Roles::get_instance(); +Manage_Roles::get_instance(); diff --git a/admin/class-manage-users.php b/admin/class-manage-users.php new file mode 100644 index 0000000..644d697 --- /dev/null +++ b/admin/class-manage-users.php @@ -0,0 +1,466 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members\Admin; + +/** + * Manager users screen class. + * + * @since 2.0.0 + * @access public + */ +final class Manage_Users { + + /** + * Holds the instances of this class. + * + * @since 2.0.0 + * @access private + * @var object + */ + private static $instance = null; + + /** + * Custom admin notices. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $notices = array(); + + /** + * Constructore method. + * + * @since 2.0.0 + * @access private + * @return void + */ + private function __construct() {} + + /** + * Sets up needed actions/filters. + * + * @since 2.0.0 + * @access private + * @return void + */ + private function setup_actions() { + + // If multiple roles per user is not enabled, bail. + if ( ! members_multiple_user_roles_enabled() ) + return; + + // Add our primary actions to the load hook. + add_action( 'load-users.php', array( $this, 'load' ) ); + add_action( 'load-users.php', array( $this, 'role_bulk_add' ) ); + add_action( 'load-users.php', array( $this, 'role_bulk_remove' ) ); + } + + /** + * Adds actions/filters on load. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function load() { + + // Add custom bulk fields. + add_action( 'restrict_manage_users', array( $this, 'bulk_fields' ), 5 ); + + // Custom manage users columns. + add_filter( 'manage_users_columns', array( $this, 'manage_users_columns' ) ); + + // Handle scripts and styles. + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); + add_action( 'admin_footer', array( $this, 'print_scripts' ), 25 ); + add_action( 'admin_head', array( $this, 'print_styles' ) ); + + // If there was an update, add notices if they're from our plugin. + if ( isset( $_GET['update'] ) ) { + + $action = sanitize_key( $_GET['update'] ); + + // If a role was added. + if ( 'members-role-added' === $action ) { + + $this->notices['role_added'] = array( 'message' => esc_html__( 'Role added to selected users.', 'members' ), 'type' => 'success' ); + + // If a role was removed. + } elseif ( 'members-role-removed' === $action ) { + + $this->notices['role_removed'] = array( 'message' => esc_html__( 'Role removed from selected users.', 'members' ), 'type' => 'success' ); + + } elseif ( 'members-error-remove-admin' === $action ) { + + $this->notices['error_remove_admin'] = array( 'message' => esc_html__( 'The current user’s role must have user editing capabilities.', 'members' ), 'type' => 'error' ); + $this->notices['role_removed'] = array( 'message' => esc_html__( 'Role removed from other selected users.', 'members' ), 'type' => 'success' ); + } + + // If we have notices, hook them in. + if ( $this->notices ) + add_action( 'admin_notices', array( $this, 'notices' ) ); + } + } + + /** + * Adds a single role to users in bulk. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function role_bulk_add() { + + // Bail if we ain't got users. + if ( empty( $_REQUEST['users'] ) ) + return; + + // Figure out if we have a role selected. + if ( ! empty( $_REQUEST['members-add-role-top'] ) && ! empty( $_REQUEST['members-add-role-submit-top'] ) ) + $role = members_sanitize_role( $_REQUEST['members-add-role-top'] ); + + elseif ( ! empty( $_REQUEST['members-add-role-bottom'] ) && ! empty( $_REQUEST['members-add-role-submit-bottom'] ) ) + $role = members_sanitize_role( $_REQUEST['members-add-role-bottom'] ); + + // Get only editable roles. + $editable_roles = members_get_editable_roles(); + + // If we don't have a role or the role is not editable, bail. + if ( empty( $role ) || ! in_array( $role, $editable_roles ) ) + return; + + // Validate our nonce. + check_admin_referer( 'members-bulk-users', 'members-bulk-users-nonce' ); + + // If the current user cannot promote users, bail. + if ( ! current_user_can( 'promote_users' ) ) + return; + + // Loop through the users and add the role if possible. + foreach ( (array) $_REQUEST['users'] as $user_id ) { + + $user_id = absint( $user_id ); + + // If the user doesn't already belong to the blog, bail. + if ( is_multisite() && ! is_user_member_of_blog( $user_id ) ) { + + wp_die( + sprintf( + '

%s

%s

', + esc_html__( 'Whoah, partner!', 'members' ), + esc_html__( 'One of the selected users is not a member of this site.', 'members' ) + ), + 403 + ); + } + + // Check that the current user can promote this specific user. + if ( ! current_user_can( 'promote_user', $user_id ) ) + continue; + + // Get the user object. + $user = new \WP_User( $user_id ); + + // If the user doesn't have the role, add it. + if ( ! in_array( $role, $user->roles ) ) + $user->add_role( $role ); + } + + // Redirect to the users screen. + wp_redirect( add_query_arg( 'update', 'members-role-added', 'users.php' ) ); + } + + /** + * Removes a single role from users in bulk. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function role_bulk_remove() { + + // Bail if we ain't got users. + if ( empty( $_REQUEST['users'] ) ) + return; + + // Figure out if we have a role selected. + if ( ! empty( $_REQUEST['members-remove-role-top'] ) && ! empty( $_REQUEST['members-remove-role-submit-top'] ) ) + $role = members_sanitize_role( $_REQUEST['members-remove-role-top'] ); + + elseif ( ! empty( $_REQUEST['members-remove-role-bottom'] ) && ! empty( $_REQUEST['members-remove-role-submit-bottom'] ) ) + $role = members_sanitize_role( $_REQUEST['members-remove-role-bottom'] ); + + // Get only editable roles. + $editable_roles = members_get_editable_roles(); + + // If we don't have a role or the role is not editable, bail. + if ( empty( $role ) || ! in_array( $role, $editable_roles ) ) + return; + + // Validate our nonce. + check_admin_referer( 'members-bulk-users', 'members-bulk-users-nonce' ); + + // If the current user cannot promote users, bail. + if ( ! current_user_can( 'promote_users' ) ) + return; + + // Get the current user. + $current_user = wp_get_current_user(); + + $m_role = members_get_role( $role ); + + $update = 'members-role-removed'; + + // Loop through the users and remove the role if possible. + foreach ( (array) $_REQUEST['users'] as $user_id ) { + + $user_id = absint( $user_id ); + + // If the user doesn't already belong to the blog, bail. + if ( is_multisite() && ! is_user_member_of_blog( $user_id ) ) { + + wp_die( + sprintf( + '

%s

%s

', + esc_html__( 'Whoah, partner!', 'members' ), + esc_html__( 'One of the selected users is not a member of this site.', 'members' ) + ), + 403 + ); + } + + // Check that the current user can promote this specific user. + if ( ! current_user_can( 'promote_user', $user_id ) ) + continue; + + $is_current_user = $user_id == $current_user->ID; + $role_can_promote = in_array( 'promote_users', $m_role->granted_caps ); + $can_manage_network = is_multisite() && current_user_can( 'manage_network_users' ); + + // If the removed role has the `promote_users` cap and user is removing it from themselves. + if ( $is_current_user && $role_can_promote && ! $can_manage_network ) { + + $can_remove = false; + + // Loop through the current user's roles. + foreach ( $current_user->roles as $_r ) { + + // If the current user has another role that can promote users, it's + // safe to remove the role. Else, the current user needs to keep + // the role. + if ( $role !== $_r && in_array( 'promote_users', members_get_role( $_r )->granted_caps ) ) { + + $can_remove = true; + break; + } + } + + if ( ! $can_remove ) { + $update = 'members-error-remove-admin'; + continue; + } + } + + // Get the user object. + $user = new \WP_User( $user_id ); + + // If the user has the role, remove it. + if ( in_array( $role, $user->roles ) ) + $user->remove_role( $role ); + } + + // Redirect to the users screen. + wp_redirect( add_query_arg( 'update', $update, 'users.php' ) ); + } + + /** + * Print admin notices. + * + * @since 2.0.0 + * @access public + * @param string $which + * @return void + */ + public function notices() { + + if ( $this->notices ) : ?> + + notices as $notice ) : ?> + +
+ ' . $notice['message'] . '' ); ?> +
+ + + + + + + + + + + + + + roles ) ) { + + foreach ( $user->roles as $role ) { + + if ( members_role_exists( $role ) ) + $user_roles[] = members_translate_role( $role ); + } + + $output = join( ', ', $user_roles ); + } + } + + return $output; + } + + /** + * Enqueue scripts. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function enqueue() { + + wp_enqueue_script( 'jquery' ); + } + + /** + * Enqueue the plugin admin CSS. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function print_scripts() { ?> + + + + + + + + setup_actions(); + } + + return self::$instance; + } +} + +Manage_Users::get_instance(); diff --git a/admin/class-meta-box-content-permissions.php b/admin/class-meta-box-content-permissions.php index 354d00e..0cfde3a 100644 --- a/admin/class-meta-box-content-permissions.php +++ b/admin/class-meta-box-content-permissions.php @@ -4,33 +4,46 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Class to handle the content permissios meta box and saving the meta. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Meta_Box_Content_Permissions { +final class Meta_Box_Content_Permissions { /** * Holds the instances of this class. * - * @since 1.0.0 + * @since 2.0.0 * @access private * @var object */ private static $instance; + /** + * Whether this is a new post. Once the post is saved and we're + * no longer on the `post-new.php` screen, this is going to be + * `false`. + * + * @since 2.0.0 + * @access public + * @var bool + */ + public $is_new_post = false; + /** * Sets up the appropriate actions. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @return void */ @@ -48,12 +61,19 @@ protected function __construct() { * Fires on the page load hook to add actions specifically for the post and * new post screens. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ public function load() { + // Make sure meta box is allowed for this post type. + if ( ! $this->maybe_enable() ) + return; + + // Is this a new post? + $this->is_new_post = 'load-post-new.php' === current_action(); + // Enqueue scripts/styles. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); @@ -67,19 +87,20 @@ public function load() { /** * Enqueues scripts styles. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ public function enqueue() { + wp_enqueue_script( 'members-edit-post' ); wp_enqueue_style( 'members-admin' ); } /** * Adds the meta box. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param string $post_type * @return void @@ -90,19 +111,32 @@ public function add_meta_boxes( $post_type ) { if ( ! current_user_can( 'restrict_content' ) ) return; + // Add the meta box. + add_meta_box( 'members-cp', esc_html__( 'Content Permissions', 'members' ), array( $this, 'meta_box' ), $post_type, 'advanced', 'high' ); + } + + /** + * Checks if Content Permissions should appear for the given post type. + * + * @since 2.0.0 + * @access public + * @return bool + */ + public function maybe_enable() { + // Get the post type object. - $type = get_post_type_object( $post_type ); + $type = get_post_type_object( get_current_screen()->post_type ); - // If this is a public post type, add the meta box. - // Note that we're disabling for attachments b/c users get confused between "content" and "file". - if ( 'attachment' !== $type->name && $type->public ) - add_meta_box( 'members-cp', esc_html__( 'Content Permissions', 'members' ), array( $this, 'meta_box' ), $post_type, 'advanced', 'high' ); + // Only enable for public post types and non-attachments by default. + $enable = 'attachment' !== $type->name && $type->public; + + return apply_filters( "members_enable_{$type->name}_content_permissions", $enable ); } /** * Outputs the meta box HTML. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param object $post * @global object $wp_roles @@ -118,6 +152,9 @@ public function meta_box( $post ) { // Get the roles saved for the post. $roles = get_post_meta( $post->ID, '_members_access_role', false ); + if ( ! $roles && $this->is_new_post ) + $roles = apply_filters( 'members_default_post_roles', array(), $post->ID ); + // Convert old post meta to the new system if no roles were found. if ( empty( $roles ) ) $roles = members_convert_old_post_meta( $post->ID ); @@ -128,35 +165,69 @@ public function meta_box( $post ) { // Hook for firing at the top of the meta box. do_action( 'members_cp_meta_box_before', $post ); ?> -

- -

+
-
+ -
    +
    - $name ) : ?> -
  • - -
  • - +
    -
-
+ + + + +
+ +
    + + $name ) : ?> +
  • + +
  • + + +
+
-

- restrict_content' ); ?> -

+ + restrict_content' ); ?> + -

- - - -

+ +
+ + ID, '_members_access_error', true ), + 'members_access_error', + array( + 'drag_drop_upload' => true, + 'editor_height' => 200 + ) + ); ?> + +
+ +
+ +
- * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Class to handle the new cap meta box on the edit/new role screen. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Meta_Box_Custom_Cap { +final class Meta_Box_Custom_Cap { /** * Holds the instances of this class. * - * @since 1.0.0 + * @since 2.0.0 * @access private * @var object */ @@ -30,7 +32,7 @@ final class Members_Meta_Box_Custom_Cap { /** * Adds our methods to the proper hooks. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -43,7 +45,7 @@ protected function __construct() { /** * Runs on the page load hook to hook in the meta boxes. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -55,7 +57,7 @@ public function load() { /** * Adds the meta box. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param string $screen_id * @param string $role @@ -74,7 +76,7 @@ public function add_meta_boxes( $screen_id, $role = '' ) { /** * Outputs the meta box HTML. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -85,14 +87,14 @@ public function meta_box() { ?>

- +

- * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Class to handle the role meta box edit/new role screen. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Meta_Box_Publish_Role { +final class Meta_Box_Publish_Role { /** * Holds the instances of this class. * - * @since 1.0.0 + * @since 2.0.0 * @access private * @var object */ @@ -30,7 +32,7 @@ final class Members_Meta_Box_Publish_Role { /** * Adds our methods to the proper hooks. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -43,7 +45,7 @@ protected function __construct() { /** * Runs on the page load hook to hook in the meta boxes. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -55,7 +57,7 @@ public function load() { /** * Adds the meta box. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param string $screen_id * @return void @@ -68,7 +70,7 @@ public function add_meta_boxes( $screen_id ) { /** * Outputs the meta box HTML. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param object $role * @return void @@ -95,8 +97,19 @@ public function meta_box( $role ) {
- - + + + + + + + +
@@ -140,7 +153,7 @@ public function meta_box( $role ) { /** * Returns the instance. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return object */ @@ -153,4 +166,4 @@ public static function get_instance() { } } -Members_Meta_Box_Publish_Role::get_instance(); +Meta_Box_Publish_Role::get_instance(); diff --git a/admin/class-role-edit.php b/admin/class-role-edit.php index 10c630f..f555b66 100644 --- a/admin/class-role-edit.php +++ b/admin/class-role-edit.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Class that displays the edit role screen and handles the form submissions for that page. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Admin_Role_Edit { +final class Role_Edit { /** * Current role object to be edited/viewed. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @var object */ @@ -30,7 +32,7 @@ final class Members_Admin_Role_Edit { /** * Current Members role object to be edited/viewed. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @var object */ @@ -39,7 +41,7 @@ final class Members_Admin_Role_Edit { /** * Whether the current role can be edited. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @var bool */ @@ -48,7 +50,7 @@ final class Members_Admin_Role_Edit { /** * Available capabilities. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @var array */ @@ -57,7 +59,7 @@ final class Members_Admin_Role_Edit { /** * Whether the page was updated. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @var bool */ @@ -66,7 +68,7 @@ final class Members_Admin_Role_Edit { /** * Sets up some necessary actions/filters. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -79,7 +81,7 @@ public function __construct() { /** * Runs on the `load-{$page}` hook. This is the handler for form submissions. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -118,12 +120,12 @@ public function load() { check_admin_referer( 'edit_role', 'members_edit_role_nonce' ); // Get the granted and denied caps. - $grant_caps = ! empty( $_POST['grant-caps'] ) ? array_unique( $_POST['grant-caps'] ) : array(); - $deny_caps = ! empty( $_POST['deny-caps'] ) ? array_unique( $_POST['deny-caps'] ) : array(); + $grant_caps = ! empty( $_POST['grant-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['grant-caps'] ) ) : array(); + $deny_caps = ! empty( $_POST['deny-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['deny-caps'] ) ) : array(); // Get the new (custom) granted and denied caps. - $grant_new_caps = ! empty( $_POST['grant-new-caps'] ) ? array_unique( $_POST['grant-new-caps'] ) : array(); - $deny_new_caps = ! empty( $_POST['deny-new-caps'] ) ? array_unique( $_POST['deny-new-caps'] ) : array(); + $grant_new_caps = ! empty( $_POST['grant-new-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['grant-new-caps'] ) ) : array(); + $deny_new_caps = ! empty( $_POST['deny-new-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['deny-new-caps'] ) ) : array(); // Get the all and custom cap group objects. $all_group = members_get_cap_group( 'all' ); @@ -166,7 +168,7 @@ public function load() { $_cap = members_sanitize_cap( $grant_new_cap ); // If not an existing cap, add it. - if ( ! in_array( $_cap, $this->capabilities ) ) { + if ( 'do_not_allow' !== $_cap && ! in_array( $_cap, $this->capabilities ) ) { $this->role->add_cap( $_cap ); $push_caps[] = $_cap; @@ -179,7 +181,7 @@ public function load() { $_cap = members_sanitize_cap( $deny_new_cap ); // If not a granted cap and not an existing cap, add it. - if ( ! in_array( $_cap, $this->capabilities ) && ! in_array( $_cap, $grant_new_caps ) ) { + if ( 'do_not_allow' !== $_cap && ! in_array( $_cap, $this->capabilities ) && ! in_array( $_cap, $grant_new_caps ) ) { $this->role->add_cap( $_cap, false ); $push_caps[] = $_cap; @@ -200,8 +202,16 @@ public function load() { } } - // Add the updated role to the role factory. - members_role_factory()->add_role( $this->role->name ); + // Add the updated role to the role registry. + members_unregister_role( $this->role->name ); + + members_register_role( + $this->role->name, + array( + 'label' => $this->members_role->get( 'label' ), + 'caps' => $this->role->capabilities + ) + ); // Reset the Members role object. $this->members_role = members_get_role( $this->role->name ); @@ -213,15 +223,19 @@ public function load() { // If successful update. if ( $this->role_updated ) - add_settings_error( 'members_edit_role', 'role_updated', sprintf( esc_html__( '%s role updated.', 'members' ), members_get_role_name( $this->role->name ) ), 'updated' ); + add_settings_error( 'members_edit_role', 'role_updated', sprintf( esc_html__( '%s role updated.', 'members' ), members_get_role( $this->role->name )->get( 'label' ) ), 'updated' ); // If the role is not editable. if ( ! $this->is_editable ) - add_settings_error( 'members_edit_role', 'role_uneditable', sprintf( esc_html__( 'The %s role is not editable. This means that it is most likely added via another plugin for a special use or that you do not have permission to edit it.', 'members' ), members_get_role_name( $this->role->name ) ) ); + add_settings_error( 'members_edit_role', 'role_uneditable', sprintf( esc_html__( 'The %s role is not editable. This means that it is most likely added via another plugin for a special use or that you do not have permission to edit it.', 'members' ), members_get_role( $this->role->name )->get( 'label' ) ) ); + + // If editing the core administrator role. + if ( 'administrator' === $this->role->name ) + add_settings_error( 'members_edit_role', 'role_is_admin', sprintf( esc_html__( 'The %s role is typically the most important role on the site. Please take extreme caution that you do not inadvertently remove necessary capabilities.', 'members' ), members_get_role( $this->role->name )->get( 'label' ) ) ); // If a new role was added (redirect from new role screen). if ( isset( $_GET['message'] ) && 'role_added' === $_GET['message'] ) - add_settings_error( 'members_edit_role', 'role_added', sprintf( esc_html__( 'The %s role has been created.', 'members' ), members_get_role_name( $this->role->name ) ), 'updated' ); + add_settings_error( 'members_edit_role', 'role_added', sprintf( esc_html__( 'The %s role has been created.', 'members' ), members_get_role( $this->role->name )->get( 'label' ) ), 'updated' ); // Load page hook. do_action( 'members_load_role_edit' ); @@ -237,7 +251,7 @@ public function load() { /** * Adds help tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -259,7 +273,7 @@ public function add_help_tabs() { /** * Enqueue scripts/styles. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -272,7 +286,7 @@ public function enqueue() { /** * Displays the page content. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -284,7 +298,7 @@ public function page() { ?> - %s', esc_url( members_get_new_role_url() ), esc_html__( 'Add New', 'members' ) ); ?> + %s', esc_url( members_get_new_role_url() ), esc_html_x( 'Add New', 'role', 'members' ) ); ?> @@ -304,7 +318,7 @@ public function page() { ?>
- +
@@ -315,7 +329,7 @@ public function page() { ?>
- role->name ); ?> + role->name ); ?> display(); ?>
diff --git a/admin/class-role-group-factory.php b/admin/class-role-group-factory.php deleted file mode 100644 index 3870811..0000000 --- a/admin/class-role-group-factory.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members - * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - */ - -/** - * Roleability group factory class. - * - * @since 1.0.0 - * @access public - */ -final class Members_Role_Group_Factory { - - /** - * Array of group objects. - * - * @since 1.0.0 - * @access public - * @var array - */ - public $groups = array(); - - /** - * Constructor method. - * - * @since 1.0.0 - * @access private - * @return void - */ - private function __construct() {} - - /** - * Register a new group object - * - * @see Members_Role_Group::__construct() - * @since 1.0.0 - * @access public - * @param string $name - * @param array $args - * @return void - */ - public function register_group( $name, $args = array() ) { - - if ( ! $this->group_exists( $name ) ) { - - $group = new Members_Role_Group( $name, $args ); - - $this->groups[ $group->name ] = $group; - } - } - - /** - * Unregisters a group object. - * - * @since 1.0.0 - * @access public - * @param string $name - * @return void - */ - public function unregister_group( $name ) { - - if ( $this->group_exists( $name ) && false === $this->get( $name )->_internal ) - unset( $this->groups[ $name ] ); - } - - /** - * Checks if a group exists. - * - * @since 1.0.0 - * @access public - * @param string $name - * @return bool - */ - public function group_exists( $name ) { - - return isset( $this->groups[ $name ] ); - } - - /** - * Gets a group object. - * - * @since 1.0.0 - * @access public - * @param string $name - * @return object|bool - */ - public function get_group( $name ) { - - return $this->group_exists( $name ) ? $this->groups[ $name ] : false; - } - - /** - * Returns the instance. - * - * @since 1.0.0 - * @access public - * @return object - */ - public static function get_instance() { - - static $instance = null; - - if ( is_null( $instance ) ) - $instance = new Members_Role_Group_Factory; - - return $instance; - } -} diff --git a/admin/class-role-group.php b/admin/class-role-group.php deleted file mode 100644 index 521c2e0..0000000 --- a/admin/class-role-group.php +++ /dev/null @@ -1,129 +0,0 @@ - - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members - * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - */ - -/** - * Role group object class. - * - * @since 1.0.0 - * @access public - */ -final class Members_Role_Group { - - /** - * Stores the properties for the object. - * - * @since 1.0.0 - * @access protected - * @var array - */ - protected $args = array(); - - /** - * Magic method for getting object properties. - * - * @since 1.0.0 - * @access public - * @param string $property - * @return mixed - */ - public function __get( $property ) { - - return isset( $this->$property ) ? $this->args[ $property ] : null; - } - - /** - * Magic method for setting object properties. - * - * @since 1.0.0 - * @access public - * @param string $property - * @param mixed $value - * @return void - */ - public function __set( $property, $value ) { - - if ( isset( $this->$property ) ) - $this->args[ $property ] = $value; - } - - /** - * Magic method for checking if a property is set. - * - * @since 1.0.0 - * @access public - * @param string $property - * @return bool - */ - public function __isset( $property ) { - - return isset( $this->args[ $property ] ); - } - - /** - * Don't allow properties to be unset. - * - * @since 3.0.0 - * @access public - * @param string $property - * @return void - */ - public function __unset( $property ) {} - - /** - * Magic method to use in case someone tries to output the object as a string. - * We'll just return the name. - * - * @since 1.0.0 - * @access public - * @return string - */ - public function __toString() { - return $this->name; - } - - /** - * Register a new object. - * - * @since 1.0.0 - * @access public - * @param string $name - * @param array $args { - * @type string $label Internationalized text label. - * @type string $icon Count label in admin. - * @type array $roles Array of roles in the group. - * } - * @return void - */ - public function __construct( $name, $args = array() ) { - - $name = sanitize_key( $name ); - - $defaults = array( - 'label' => '', - 'label_count' => '', - 'roles' => array(), - 'show_in_view_list' => true - ); - - $this->args = wp_parse_args( $args, $defaults ); - - // Get the roles that exist. - $existing_roles = array_keys( members_get_role_names() ); - - // Remove roles that don't exist. - if ( $this->args['roles'] ) - $this->args['roles'] = array_intersect( $existing_roles, $this->args['roles'] ); - - // Set the name. - $this->args['name'] = $name; - } -} diff --git a/admin/class-role-list-table.php b/admin/class-role-list-table.php index 3801a54..225f991 100644 --- a/admin/class-role-list-table.php +++ b/admin/class-role-list-table.php @@ -4,34 +4,45 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Role list table for the roles management page in the admin. Extends the core `WP_List_Table` * class in the admin. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -class Members_Role_List_Table extends WP_List_Table { +class Role_List_Table extends \WP_List_Table { /** * The current view. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ public $role_view = 'all'; + /** + * Array of role views. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $role_views = array(); + /** * Allowed role views. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -40,7 +51,7 @@ class Members_Role_List_Table extends WP_List_Table { /** * The default role. This will be assigned the value of `get_option( 'default_role' )`. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -49,7 +60,7 @@ class Members_Role_List_Table extends WP_List_Table { /** * The current user object. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var object */ @@ -58,7 +69,7 @@ class Members_Role_List_Table extends WP_List_Table { /** * Sets up the list table. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -72,7 +83,7 @@ public function __construct() { parent::__construct( $args ); // Get the current user object. - $this->current_user = new WP_User( get_current_user_id() ); + $this->current_user = new \WP_User( get_current_user_id() ); // Get the defined default role. $this->default_role = get_option( 'default_role', $this->default_role ); @@ -81,32 +92,24 @@ public function __construct() { $this->allowed_role_views = array_keys( $this->get_views() ); // Get the current view. - if ( isset( $_GET['role_view'] ) && in_array( $_GET['role_view'], $this->allowed_role_views ) ) - $this->role_view = $_GET['role_view']; + if ( isset( $_GET['view'] ) && in_array( $_GET['view'], $this->allowed_role_views ) ) + $this->role_view = $_GET['view']; } /** * Sets up the items (roles) to list. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ public function prepare_items() { - // Get the roles for the default/All view. - if ( 'all' === $this->role_view ) { + $roles = array(); - $roles = members_get_role_slugs(); - - // If a custom view, get the roles. - } else { - // Get the current group being viewed. - $group = members_get_role_group( $this->role_view ); - - // Set the roles array. - $roles = $group ? $group->roles : array(); - } + // Get the roles for the view. + if ( ! empty( $this->role_views[ $this->role_view ]['roles'] ) ) + $roles = $this->role_views[ $this->role_view ]['roles']; // Allow devs to filter the items. $roles = apply_filters( 'members_manage_roles_items', $roles, $this->role_view ); @@ -168,7 +171,7 @@ public function prepare_items() { * Returns an array of columns to show. * * @see members_manage_roles_columns() - * @since 1.0.0 + * @since 2.0.0 * @access public * @return array */ @@ -195,7 +198,7 @@ protected function column_default( $role, $column_name ) { /** * The checkbox column callback. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @return string @@ -214,7 +217,7 @@ protected function column_cb( $role ) { /** * The role name column callback. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @return string @@ -226,11 +229,11 @@ protected function column_title( $role ) { // If the role is the default role. if ( $role == get_option( 'default_role' ) ) - $states[] = esc_html__( 'Default Role', 'members' ); + $states['default'] = esc_html__( 'Default Role', 'members' ); // If the current user has this role. if ( members_current_user_has_role( $role ) ) - $states[] = esc_html__( 'Your Role', 'members' ); + $states['mine'] = esc_html__( 'Your Role', 'members' ); // Allow devs to filter the role states. $states = apply_filters( 'members_role_states', $states, $role ); @@ -238,14 +241,18 @@ protected function column_title( $role ) { // If we have states, string them together. if ( ! empty( $states ) ) { - foreach ( $states as $state ) - $role_states .= sprintf( '%s', $state ); + foreach ( $states as $state => $label ) + $states[ $state ] = sprintf( '%s', $label ); - $role_states = ' – ' . $role_states; + $role_states = ' – ' . join( ', ', $states ); } // Add the title and role states. - $title = sprintf( '%s%s', esc_url( members_get_edit_role_url( $role ) ), esc_html( members_get_role_name( $role ) ), $role_states ); + if ( current_user_can( 'edit_roles' ) ) + $title = sprintf( '%s%s', esc_url( members_get_edit_role_url( $role ) ), esc_html( members_get_role( $role )->get( 'label' ) ), $role_states ); + + else + $title = sprintf( '%s%s', esc_html( members_get_role( $role )->get( 'label' ) ), $role_states ); return apply_filters( 'members_manage_roles_column_role_name', $title, $role ); } @@ -253,7 +260,7 @@ protected function column_title( $role ) { /** * The role column callback. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @return string @@ -265,19 +272,33 @@ protected function column_role( $role ) { /** * The users column callback. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @return string */ protected function column_users( $role ) { - return apply_filters( 'members_manage_roles_column_users', members_get_role_user_count( $role ), $role ); + + $user_count = members_get_role_user_count( $role ); + + $output = number_format_i18n( $user_count ); + + if ( 0 < absint( $user_count ) && current_user_can( 'list_users' ) ) { + + $output = sprintf( + '%s', + esc_url( add_query_arg( 'role', $role, admin_url( 'users.php' ) ) ), + $output + ); + } + + return apply_filters( 'members_manage_roles_column_users', $output, $role ); } /** * The caps column callback. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @return string @@ -289,7 +310,7 @@ protected function column_granted_caps( $role ) { /** * The caps column callback. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @return string @@ -301,7 +322,7 @@ protected function column_denied_caps( $role ) { /** * Returns the name of the primary column. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @return string */ @@ -312,7 +333,7 @@ protected function get_default_primary_column_name() { /** * Handles the row actions. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @param string $role * @param string $column_name @@ -338,7 +359,7 @@ protected function handle_row_actions( $role, $column_name, $primary ) { $actions['delete'] = sprintf( '%s', esc_url( members_get_delete_role_url( $role ) ), esc_html__( 'Delete', 'members' ) ); // If the role cannot be edited. - } else { + } elseif ( current_user_can( 'edit_roles' ) ) { // Add the view role link. $actions['view'] = sprintf( '%s', esc_url( members_get_edit_role_url( $role ) ), esc_html__( 'View', 'members' ) ); @@ -366,7 +387,7 @@ protected function handle_row_actions( $role, $column_name, $primary ) { /** * Returns an array of sortable columns. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @return array */ @@ -381,31 +402,46 @@ protected function get_sortable_columns() { /** * Returns an array of views for the list table. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @return array */ protected function get_views() { - $active = members_get_role_group( 'active' ); - $inactive = members_get_role_group( 'inactive' ); - - if ( $active ) - $active->roles = members_get_active_role_slugs(); - - if ( $inactive ) - $inactive->roles = members_get_inactive_role_slugs(); + // Get the current user. + $current_user = wp_get_current_user(); $views = array(); $current = ' class="current"'; - $all_count = count( members_get_role_slugs() ); - - // Add the default/all view. - $views['all'] = sprintf( - '%s', - 'all' === $this->role_view ? $current : '', - esc_url( members_get_edit_roles_url() ), - sprintf( _n( 'All %s', 'All %s', $all_count, 'members' ), sprintf( '(%s)', number_format_i18n( $all_count ) ) ) + + $this->role_views['all'] = array( + 'label_count' => _n_noop( 'All %s', 'All %s', 'members' ), + 'roles' => array_keys( members_get_roles() ) + ); + + $this->role_views['mine'] = array( + 'label_count' => _n_noop( 'Mine %s', 'Mine %s', 'members' ), + 'roles' => $current_user->roles + ); + + $this->role_views['active'] = array( + 'label_count' => _n_noop( 'Has Users %s', 'Has Users %s', 'members' ), + 'roles' => members_get_active_roles() + ); + + $this->role_views['inactive'] = array( + 'label_count' => _n_noop( 'No Users %s', 'No Users %s', 'members' ), + 'roles' =>members_get_inactive_roles() + ); + + $this->role_views['editable'] = array( + 'label_count' => _n_noop( 'Editable %s', 'Editable %s', 'members' ), + 'roles' => members_get_editable_roles() + ); + + $this->role_views['uneditable'] = array( + 'label_count' => _n_noop( 'Uneditable %s', 'Uneditable %s', 'members' ), + 'roles' => members_get_uneditable_roles() ); // Loop through the role groups and put them into the view list. @@ -415,19 +451,28 @@ protected function get_views() { if ( ! $group->show_in_view_list ) continue; - $count = count( $group->roles ); + $this->role_views[ "group-{$group->name}" ] = array( + 'label_count' => $group->label_count, + 'roles' => $group->roles + ); + } + + // Loop through the default views and put them into the view list. + foreach ( $this->role_views as $view => $args ) { + + $count = count( $args['roles'] ); // Skip any views with 0 roles. if ( 0 >= $count ) continue; - $noop = $group->label_count; + $noop = $args['label_count']; // Add the view link. - $views[ $group->name ] = sprintf( + $views[ $view ] = sprintf( '%s', - $group->name === $this->role_view ? $current : '', - 'all' === $group->name ? esc_url( members_get_edit_roles_url() ) : esc_url( members_get_role_view_url( $group->name ) ), + $view === $this->role_view ? $current : '', + 'all' === $view ? esc_url( members_get_edit_roles_url() ) : esc_url( members_get_role_view_url( $view ) ), sprintf( translate_nooped_plural( $noop, $count, $noop['domain'] ), sprintf( '(%s)', number_format_i18n( $count ) ) ) ); } @@ -438,7 +483,7 @@ protected function get_views() { /** * Displays the list table. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -452,7 +497,7 @@ public function display() { /** * Returns an array of bulk actions available. * - * @since 1.0.0 + * @since 2.0.0 * @access protected * @return array */ diff --git a/admin/class-role-new.php b/admin/class-role-new.php index 115e3b1..efa5e99 100644 --- a/admin/class-role-new.php +++ b/admin/class-role-new.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Class that displays the new role screen and handles the form submissions for that page. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Admin_Role_New { +final class Role_New { /** * Holds the instances of this class. * - * @since 1.0.0 + * @since 2.0.0 * @access private * @var object */ @@ -30,7 +32,7 @@ final class Members_Admin_Role_New { /** * Name of the page we've created. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -39,7 +41,7 @@ final class Members_Admin_Role_New { /** * Role that's being created. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -48,7 +50,7 @@ final class Members_Admin_Role_New { /** * Name of the role that's being created. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -57,7 +59,7 @@ final class Members_Admin_Role_New { /** * Array of the role's capabilities. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -66,7 +68,7 @@ final class Members_Admin_Role_New { /** * Conditional to see if we're cloning a role. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var bool */ @@ -75,7 +77,7 @@ final class Members_Admin_Role_New { /** * Role that is being cloned. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ @@ -84,7 +86,7 @@ final class Members_Admin_Role_New { /** * Sets up our initial actions. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -98,7 +100,7 @@ public function __construct() { /** * Adds the roles page to the admin. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -117,7 +119,7 @@ public function add_admin_page() { /** * Checks posted data on load and performs actions if needed. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -159,8 +161,8 @@ public function load() { // Check if any capabilities were selected. if ( isset( $_POST['grant-caps'] ) || isset( $_POST['deny-caps'] ) ) { - $grant_caps = ! empty( $_POST['grant-caps'] ) ? array_unique( $_POST['grant-caps'] ) : array(); - $deny_caps = ! empty( $_POST['deny-caps'] ) ? array_unique( $_POST['deny-caps'] ) : array(); + $grant_caps = ! empty( $_POST['grant-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['grant-caps'] ) ) : array(); + $deny_caps = ! empty( $_POST['deny-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['deny-caps'] ) ) : array(); foreach ( $_m_caps as $cap ) { @@ -172,8 +174,8 @@ public function load() { } } - $grant_new_caps = ! empty( $_POST['grant-new-caps'] ) ? array_unique( $_POST['grant-new-caps'] ) : array(); - $deny_new_caps = ! empty( $_POST['deny-new-caps'] ) ? array_unique( $_POST['deny-new-caps'] ) : array(); + $grant_new_caps = ! empty( $_POST['grant-new-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['grant-new-caps'] ) ) : array(); + $deny_new_caps = ! empty( $_POST['deny-new-caps'] ) ? members_remove_hidden_caps( array_unique( $_POST['deny-new-caps'] ) ) : array(); foreach ( $grant_new_caps as $grant_new_cap ) { @@ -193,7 +195,7 @@ public function load() { // Sanitize the new role name/label. We just want to strip any tags here. if ( ! empty( $_POST['role_name'] ) ) - $this->role_name = wp_strip_all_tags( $_POST['role_name'] ); + $this->role_name = wp_strip_all_tags( wp_unslash( $_POST['role_name'] ) ); // Sanitize the new role, removing any unwanted characters. if ( ! empty( $_POST['role'] ) ) @@ -262,7 +264,7 @@ public function load() { /** * Adds help tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -284,7 +286,7 @@ public function add_help_tabs() { /** * Enqueue scripts/styles. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -297,7 +299,7 @@ public function enqueue() { /** * Outputs the page. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -323,7 +325,7 @@ public function page() { ?>
- +
@@ -336,7 +338,7 @@ public function page() { ?>
- capabilities ); ?> + capabilities ); ?> display(); ?>
@@ -362,7 +364,7 @@ public function page() { ?> /** * Filters the new role default caps in the case that we're cloning a role. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param array $capabilities * @param array @@ -383,7 +385,7 @@ public function clone_default_caps( $capabilities ) { /** * Returns the instance. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return object */ @@ -396,4 +398,4 @@ public static function get_instance() { } } -Members_Admin_Role_New::get_instance(); +Role_New::get_instance(); diff --git a/admin/class-roles.php b/admin/class-roles.php index 96db209..6dd8b29 100644 --- a/admin/class-roles.php +++ b/admin/class-roles.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Class that displays the roles admin screen and handles requests for that page. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Admin_Roles { +final class Roles { /** * Sets up some necessary actions/filters. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -40,7 +42,7 @@ public function __construct() { /** * Modifies the current screen object. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -53,7 +55,7 @@ public function current_screen( $screen ) { /** * Sets up the roles column headers. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param array $columns * @return array @@ -75,7 +77,7 @@ public function manage_roles_columns( $columns ) { /** * Runs on the `load-{$page}` hook. This is the handler for form submissions and requests. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -126,7 +128,7 @@ public function load() { if ( members_role_exists( $role ) ) { // Add role deleted message. - add_settings_error( 'members_roles', 'role_deleted', sprintf( esc_html__( '%s role deleted.', 'members' ), members_get_role_name( $role ) ), 'updated' ); + add_settings_error( 'members_roles', 'role_deleted', sprintf( esc_html__( '%s role deleted.', 'members' ), members_get_role( $role )->get( 'label' ) ), 'updated' ); // Delete the role. members_delete_role( $role ); @@ -141,7 +143,7 @@ public function load() { /** * Enqueue scripts/styles. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -154,13 +156,13 @@ public function enqueue() { /** * Displays the page content. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ public function page() { - require_once( members_plugin()->admin_dir . 'class-role-list-table.php' ); ?> + require_once( members_plugin()->dir . 'admin/class-role-list-table.php' ); ?>
@@ -168,7 +170,7 @@ public function page() { - + @@ -178,7 +180,7 @@ public function page() {
- + prepare_items(); ?> display(); ?> @@ -192,7 +194,7 @@ public function page() { /** * Adds help tabs. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -244,7 +246,7 @@ public function add_help_tabs() { /** * Overview help tab callback function. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -258,7 +260,7 @@ public function help_tab_overview() { ?> /** * Screen content help tab callback function. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -277,7 +279,7 @@ public function help_tab_screen_content() { ?> /** * Row actions help tab callback function. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -298,7 +300,7 @@ public function help_tab_row_actions() { ?> /** * Bulk actions help tab callback function. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ diff --git a/admin/class-settings.php b/admin/class-settings.php index 61257b2..82838d0 100644 --- a/admin/class-settings.php +++ b/admin/class-settings.php @@ -4,28 +4,30 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Sets up and handles the plugin settings screen. * * @since 1.0.0 * @access public */ -final class Members_Settings_Page { +final class Settings_Page { /** - * Holds the instances of this class. + * Admin page name/ID. * - * @since 1.0.0 - * @access private - * @var object + * @since 2.0.0 + * @access public + * @var string */ - private static $instance; + public $name = 'members-settings'; /** * Settings page name. @@ -37,316 +39,228 @@ final class Members_Settings_Page { public $settings_page = ''; /** - * Holds an array the plugin settings. + * Holds an array the settings page views. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ - public $settings = array(); + public $views = array(); /** - * Sets up the needed actions for adding and saving the meta boxes. - * - * @since 1.0.0 - * @access public - * @return void - */ - private function __construct() { - add_action( 'admin_menu', array( $this, 'admin_menu' ) ); - } - - /** - * Sets up custom admin menus. + * Returns the instance. * * @since 1.0.0 * @access public - * @return void + * @return object */ - public function admin_menu() { - - // Create the settings page. - $this->settings_page = add_options_page( - esc_html__( 'Members Settings', 'members' ), - esc_html_x( 'Members', 'admin screen', 'members' ), - apply_filters( 'members_settings_capability', 'manage_options' ), - 'members-settings', - array( $this, 'settings_page' ) - ); - - if ( $this->settings_page ) { - - // Register setings. - add_action( 'admin_init', array( $this, 'register_settings' ) ); + public static function get_instance() { - // Add help tabs. - add_action( "load-{$this->settings_page}", array( $this, 'add_help_tabs' ) ); + static $instance = null; - // Enqueue scripts/styles. - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); + if ( is_null( $instance ) ) { + $instance = new self; + $instance->includes(); + $instance->setup_actions(); } + + return $instance; } /** - * Enqueue scripts/styles. + * Constructor method. * * @since 1.0.0 * @access public - * @param string $hook_suffix * @return void */ - public function enqueue( $hook_suffix ) { - - if ( $this->settings_page !== $hook_suffix ) - return; - - wp_enqueue_script( 'members-settings' ); - } + private function __construct() {} /** - * Registers the plugin settings. + * Loads settings files. * - * @since 1.0.0 - * @access public + * @since 2.0.0 + * @access private * @return void */ - function register_settings() { - - // Get the current plugin settings w/o the defaults. - $this->settings = get_option( 'members_settings' ); - - // Register the setting. - register_setting( 'members_settings', 'members_settings', array( $this, 'validate_settings' ) ); - - /* === Settings Sections === */ + private function includes() { - // Add settings sections. - add_settings_section( 'roles_caps', esc_html__( 'Roles and Capabilities', 'members' ), array( $this, 'section_roles_caps' ), $this->settings_page ); - add_settings_section( 'content_permissions', esc_html__( 'Content Permissions', 'members' ), '__return_false', $this->settings_page ); - add_settings_section( 'sidebar_widgets', esc_html__( 'Sidebar Widgets', 'members' ), '__return_false', $this->settings_page ); - add_settings_section( 'private_site', esc_html__( 'Private Site', 'members' ), '__return_false', $this->settings_page ); + // Include the settings functions. + require_once( members_plugin()->dir . 'admin/functions-settings.php' ); - /* === Settings Fields === */ - - // Role manager fields. - add_settings_field( 'enable_role_manager', esc_html__( 'Role Manager', 'members' ), array( $this, 'field_enable_role_manager' ), $this->settings_page, 'roles_caps' ); - add_settings_field( 'explicit_denied_caps', esc_html__( 'Capabilities', 'members' ), array( $this, 'field_explicit_denied_caps' ), $this->settings_page, 'roles_caps' ); - add_settings_field( 'enable_multi_roles', esc_html__( 'Multiple User Roles', 'members' ), array( $this, 'field_enable_multi_roles' ), $this->settings_page, 'roles_caps' ); - - // Content permissions fields. - add_settings_field( 'enable_content_permissions', esc_html__( 'Enable Permissions', 'members' ), array( $this, 'field_enable_content_permissions' ), $this->settings_page, 'content_permissions' ); - add_settings_field( 'content_permissions_error', esc_html__( 'Error Message', 'members' ), array( $this, 'field_content_permissions_error' ), $this->settings_page, 'content_permissions' ); - - // Widgets fields. - add_settings_field( 'widget_login', esc_html__( 'Login Widget', 'members' ), array( $this, 'field_widget_login' ), $this->settings_page, 'sidebar_widgets' ); - add_settings_field( 'widget_users', esc_html__( 'Users Widget', 'members' ), array( $this, 'field_widget_users' ), $this->settings_page, 'sidebar_widgets' ); - - // Private site fields. - add_settings_field( 'enable_private_site', esc_html__( 'Enable Private Site', 'members' ), array( $this, 'field_enable_private_site' ), $this->settings_page, 'private_site' ); - add_settings_field( 'enable_private_feed', esc_html__( 'Disable Feed', 'members' ), array( $this, 'field_enable_private_feed' ), $this->settings_page, 'private_site' ); - add_settings_field( 'private_feed_error', esc_html__( 'Feed Error Message', 'members' ), array( $this, 'field_private_feed_error' ), $this->settings_page, 'private_site' ); + // Load settings view classes. + require_once( members_plugin()->dir . 'admin/views/class-view.php' ); + require_once( members_plugin()->dir . 'admin/views/class-view-general.php' ); + require_once( members_plugin()->dir . 'admin/views/class-view-addons.php' ); + require_once( members_plugin()->dir . 'admin/views/class-view-donate.php' ); } /** - * Validates the plugin settings. + * Sets up initial actions. * - * @since 1.0.0 - * @access public - * @param array $input - * @return array - */ - function validate_settings( $settings ) { - - // Validate true/false checkboxes. - $settings['role_manager'] = ! empty( $settings['role_manager'] ) ? true : false; - $settings['explicit_denied_caps'] = ! empty( $settings['explicit_denied_caps'] ) ? true : false; - $settings['multi_roles'] = ! empty( $settings['multi_roles'] ) ? true : false; - $settings['content_permissions'] = ! empty( $settings['content_permissions'] ) ? true : false; - $settings['login_form_widget'] = ! empty( $settings['login_form_widget'] ) ? true : false; - $settings['users_widget'] = ! empty( $settings['users_widget'] ) ? true : false; - $settings['private_blog'] = ! empty( $settings['private_blog'] ) ? true : false; - $settings['private_feed'] = ! empty( $settings['private_feed'] ) ? true : false; - - // Kill evil scripts. - $settings['content_permissions_error'] = stripslashes( wp_filter_post_kses( addslashes( $settings['content_permissions_error'] ) ) ); - $settings['private_feed_error'] = stripslashes( wp_filter_post_kses( addslashes( $settings['private_feed_error'] ) ) ); - - // Return the validated/sanitized settings. - return $settings; - } - - /** - * Role/Caps section callback. - * - * @since 1.0.0 - * @access public + * @since 2.0.0 + * @access private * @return void */ - public function section_roles_caps() { ?> + private function setup_actions() { -

- -

- + public function register_view( $view ) { - - view_exists( $view->name ) ) + $this->views[ $view->name ] = $view; + } /** - * Explicit denied caps field callback. + * Unregister a view. * - * @since 1.0.0 + * @since 2.0.0 * @access public + * @param string $name * @return void */ - public function field_explicit_denied_caps() { ?> + public function unregister_view( $name ) { - - view_exists( $name ) ) + unset( $this->view[ $name ] ); + } /** - * Multiple roles field callback. + * Get a view object * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return void + * @param string $name + * @return object */ - public function field_enable_multi_roles() { ?> + public function get_view( $name ) { - - view_exists( $name ) ? $this->views[ $name ] : false; + } /** - * Enable content permissions field callback. + * Check if a view exists. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return void + * @param string $name + * @return bool */ - public function field_enable_content_permissions() { ?> + public function view_exists( $name ) { - - views[ $name ] ); + } /** - * Content permissions error message field callback. + * Sets up custom admin menus. * * @since 1.0.0 * @access public * @return void */ - public function field_content_permissions_error() { - - wp_editor( - members_get_setting( 'content_permissions_error' ), - 'members_settings_content_permissions_error', - array( - 'textarea_name' => 'members_settings[content_permissions_error]', - 'drag_drop_upload' => true, - 'editor_height' => 250 - ) + public function admin_menu() { + + // Create the settings page. + $this->settings_page = add_options_page( + esc_html_x( 'Members', 'admin screen', 'members' ), + esc_html_x( 'Members', 'admin screen', 'members' ), + apply_filters( 'members_settings_capability', 'manage_options' ), + $this->name, + array( $this, 'settings_page' ) ); + + if ( $this->settings_page ) { + + do_action( 'members_register_settings_views', $this ); + + uasort( $this->views, 'members_priority_sort' ); + + // Register setings. + add_action( 'admin_init', array( $this, 'register_settings' ) ); + + // Page load callback. + add_action( "load-{$this->settings_page}", array( $this, 'load' ) ); + + // Enqueue scripts/styles. + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); + } } /** - * Login widget field callback. + * Runs on page load. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ - public function field_widget_login() { ?> + public function load() { - - + // Add help tabs for the current view. + $view = $this->get_view( members_get_current_settings_view() ); - - load(); + $view->add_help_tabs(); + } + } /** - * Enable private site field callback. + * Print styles to the header. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ - public function field_enable_private_site() { ?> + public function print_styles() { ?> - + + public function enqueue( $hook_suffix ) { - - settings_page !== $hook_suffix ) + return; + + $view = $this->get_view( members_get_current_settings_view() ); + + if ( $view ) + $view->enqueue(); + } /** - * Private feed error message field callback. + * Registers the plugin settings. * * @since 1.0.0 * @access public * @return void */ - public function field_private_feed_error() { - - wp_editor( - members_get_setting( 'private_feed_error' ), - 'members_settings_private_feed_error', - array( - 'textarea_name' => 'members_settings[private_feed_error]', - 'drag_drop_upload' => true, - 'editor_height' => 250 - ) - ); + function register_settings() { + + foreach ( $this->views as $view ) + $view->register_settings(); } /** @@ -359,152 +273,57 @@ public function field_private_feed_error() { public function settings_page() { ?>
-

+

+ +
+ filter_links(); ?> +
- - - settings_page ); ?> - - + get_view( members_get_current_settings_view() )->template(); ?>
add_help_tab( - array( - 'id' => 'roles-caps', - 'title' => esc_html__( 'Role and Capabilities', 'members' ), - 'callback' => array( $this, 'help_tab_roles_caps' ) - ) - ); - - // Content Permissions help tab. - $screen->add_help_tab( - array( - 'id' => 'content-permissions', - 'title' => esc_html__( 'Content Permissions', 'members' ), - 'callback' => array( $this, 'help_tab_content_permissions' ) - ) - ); - - // Widgets help tab. - $screen->add_help_tab( - array( - 'id' => 'sidebar-widgets', - 'title' => esc_html__( 'Sidebar Widgets', 'members' ), - 'callback' => array( $this, 'help_tab_sidebar_widgets' ) - ) - ); - - // Private Site help tab. - $screen->add_help_tab( - array( - 'id' => 'private-site', - 'title' => esc_html__( 'Private Site', 'members' ), - 'callback' => array( $this, 'help_tab_private_site' ) - ) - ); - - // Get docs and help links. - $docs_link = sprintf( '
  • %s
  • ', esc_html__( 'Documentation', 'members' ) ); - $help_link = sprintf( '
  • %s
  • ', esc_html__( 'Support Forums', 'members' ) ); - $tut_link = sprintf( '
  • %s
  • ', esc_html__( 'Users, Roles, and Capabilities', 'members' ) ); - - // Set the help sidebar. - $screen->set_help_sidebar( members_get_help_sidebar_text() ); - } - - /** - * Displays the roles/caps help tab. + * Outputs the list of views. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ - public function help_tab_roles_caps() { ?> + private function filter_links() { ?> -

    - -

    + - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members\Admin; + /** * Edit user screen class. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Admin_User_Edit { +final class User_Edit { /** * Holds the instances of this class. * - * @since 1.0.0 + * @since 2.0.0 * @access private * @var object */ @@ -30,7 +32,7 @@ final class Members_Admin_User_Edit { /** * Sets up needed actions/filters for the admin to initialize. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ @@ -47,25 +49,28 @@ public function __construct() { /** * Adds actions/filters on load. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ public function load_user_edit() { - add_action( 'admin_head', array( $this, 'print_styles' ) ); + // Handle scripts and styles. + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); + add_action( 'admin_footer', array( $this, 'print_scripts' ), 25 ); + add_action( 'admin_head', array( $this, 'print_styles' ) ); add_action( 'show_user_profile', array( $this, 'profile_fields' ) ); add_action( 'edit_user_profile', array( $this, 'profile_fields' ) ); // Must use `profile_update` to change role. Otherwise, WP will wipe it out. - add_action( 'profile_update', array( $this, 'role_update' ) ); + add_action( 'profile_update', array( $this, 'role_update' ), 10, 2 ); } /** * Adds custom profile fields. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param object $user * @return void @@ -78,13 +83,13 @@ public function profile_fields( $user ) { $user_roles = (array) $user->roles; - $editable_roles = members_get_editable_role_names(); + $roles = members_get_roles(); - asort( $editable_roles ); + ksort( $roles ); wp_nonce_field( 'new_user_roles', 'members_new_user_roles_nonce' ); ?> -

    +

    @@ -92,16 +97,22 @@ public function profile_fields( $user ) { @@ -113,12 +124,13 @@ public function profile_fields( $user ) { * on a different hook, `profile_update`. Using the normal hooks on the edit user screen won't work * because WP will wipe out the role. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param int $user_id + * @param object $old_user_data * @return void */ - public function role_update( $user_id ) { + public function role_update( $user_id, $old_user_data ) { // If the current user can't promote users or edit this particular user, bail. if ( ! current_user_can( 'promote_users' ) || ! current_user_can( 'edit_user', $user_id ) ) @@ -129,13 +141,13 @@ public function role_update( $user_id ) { return; // Create a new user object. - $user = new WP_User( $user_id ); + //$user = new WP_User( $user_id ); // If we have an array of roles. if ( ! empty( $_POST['members_user_roles'] ) ) { // Get the current user roles. - $old_roles = (array) $user->roles; + $old_roles = (array) $old_user_data->roles; // Sanitize the posted roles. $new_roles = array_map( 'members_sanitize_role', $_POST['members_user_roles'] ); @@ -144,8 +156,8 @@ public function role_update( $user_id ) { foreach ( $new_roles as $new_role ) { // If the user doesn't already have the role, add it. - if ( ! in_array( $new_role, (array) $user->roles ) ) - $user->add_role( $new_role ); + if ( members_is_role_editable( $new_role ) && ! in_array( $new_role, (array) $old_user_data->roles ) ) + $old_user_data->add_role( $new_role ); } // Loop through the current user roles. @@ -153,26 +165,56 @@ public function role_update( $user_id ) { // If the role is editable and not in the new roles array, remove it. if ( members_is_role_editable( $old_role ) && ! in_array( $old_role, $new_roles ) ) - $user->remove_role( $old_role ); + $old_user_data->remove_role( $old_role ); } // If the posted roles are empty. } else { // Loop through the current user roles. - foreach ( (array) $user->roles as $old_role ) { + foreach ( (array) $old_user_data->roles as $old_role ) { // Remove the role if it is editable. if ( members_is_role_editable( $old_role ) ) - $user->remove_role( $old_role ); + $old_user_data->remove_role( $old_role ); } } } + /** + * Enqueue scripts. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function enqueue() { + + wp_enqueue_script( 'jquery' ); + } + + /** + * Enqueue the plugin admin CSS. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function print_scripts() { ?> + + + + /** * Returns the instance. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return object */ @@ -198,4 +240,4 @@ public static function get_instance() { } } -Members_Admin_User_Edit::get_instance(); +User_Edit::get_instance(); diff --git a/admin/class-user-new.php b/admin/class-user-new.php new file mode 100644 index 0000000..0d4270f --- /dev/null +++ b/admin/class-user-new.php @@ -0,0 +1,243 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members\Admin; + +/** + * Edit user screen class. + * + * @since 2.0.0 + * @access public + */ +final class User_New { + + /** + * Holds the instances of this class. + * + * @since 2.0.0 + * @access private + * @var object + */ + private static $instance; + + /** + * Constructor method. + * + * @since 2.0.0 + * @access private + * @return void + */ + private function __construct() {} + + /** + * Sets up needed actions/filters for the admin to initialize. + * + * @since 2.0.0 + * @access private + * @return void + */ + private function setup_actions() { + + // If multiple roles per user is not enabled, bail. + // + // @since 2.0.1 Added a check to not run on multisite. + // @link https://github.com/justintadlock/members/issues/153 + if ( ! members_multiple_user_roles_enabled() || is_multisite() ) + return; + + // Only run our customization on the 'user-edit.php' page in the admin. + add_action( 'load-user-new.php', array( $this, 'load' ) ); + + // Sets the new user's roles. + add_action( 'user_register', array( $this, 'user_register' ), 5 ); + } + + /** + * Adds actions/filters on load. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function load() { + + // Adds the profile fields. + add_action( 'user_new_form', array( $this, 'profile_fields' ) ); + + // Handle scripts. + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); + add_action( 'admin_footer', array( $this, 'print_scripts' ), 25 ); + } + + /** + * Adds custom profile fields. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function profile_fields() { + + if ( ! current_user_can( 'promote_users' ) ) + return; + + // Get the default user roles. + $new_user_roles = apply_filters( 'members_default_user_roles', array( get_option( 'default_role' ) ) ); + + // If the form was submitted but didn't go through, get the posted roles. + if ( isset( $_POST['createuser'] ) && ! empty( $_POST['members_user_roles'] ) ) + $new_user_roles = array_map( 'members_sanitize_role', $_POST['members_user_roles'] ); + + $roles = members_get_roles(); + + ksort( $roles ); + + wp_nonce_field( 'new_user_roles', 'members_new_user_roles_nonce' ); ?> + +
    -
      - $name ) : ?> -
    • - -
    • - -
    +
    +
      + + + name ) ) :?> +
    • + +
    • + + + +
    +
    + + + + + + + +
    +
    +
      + + + name ) ) :?> +
    • + +
    • + + + +
    +
    +
    + roles; + + // Sanitize the posted roles. + $new_roles = array_map( 'members_sanitize_role', $_POST['members_user_roles'] ); + + // Loop through the posted roles. + foreach ( $new_roles as $new_role ) { + + // If the user doesn't already have the role, add it. + if ( members_is_role_editable( $new_role ) && ! in_array( $new_role, (array) $user->roles ) ) + $user->add_role( $new_role ); + } + + // Loop through the current user roles. + foreach ( $old_roles as $old_role ) { + + // If the role is editable and not in the new roles array, remove it. + if ( members_is_role_editable( $old_role ) && ! in_array( $old_role, $new_roles ) ) + $user->remove_role( $old_role ); + } + + // If the posted roles are empty. + } else { + + // Loop through the current user roles. + foreach ( (array) $user->roles as $old_role ) { + + // Remove the role if it is editable. + if ( members_is_role_editable( $old_role ) ) + $user->remove_role( $old_role ); + } + } + } + + /** + * Enqueue scripts. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function enqueue() { + + wp_enqueue_script( 'jquery' ); + } + + /** + * Enqueue the plugin admin CSS. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function print_scripts() { ?> + + + + setup_actions(); + } + + return self::$instance; + } +} + +User_New::get_instance(); diff --git a/admin/config/addons.php b/admin/config/addons.php new file mode 100644 index 0000000..c5e17cf --- /dev/null +++ b/admin/config/addons.php @@ -0,0 +1,40 @@ + array( + 'url' => 'https://themehybrid.com/plugins/members-privacy-caps', + 'title' => 'Members - Privacy Caps', + 'excerpt' => 'Creates additional capabilities for control over WordPress’ privacy and personal data features (GDPR).' + ), + + 'members-admin-access' => array( + 'url' => 'https://themehybrid.com/plugins/members-admin-access', + 'title' => 'Members - Admin Access', + 'excerpt' => 'Allows site administrators to control which users have access to the WordPress admin via role.', + ), + + 'members-core-create-caps' => array( + 'url' => 'https://themehybrid.com/plugins/members-core-create-caps', + 'title' => 'Members - Core Create Caps', + 'excerpt' => 'Adds the create_posts and create_pages caps to posts/pages to separate them from their edit_* counterparts, providing more flexible editing capabilities.' + ), + + 'members-role-levels' => array( + 'url' => 'https://themehybrid.com/plugins/members-role-levels', + 'title' => 'Members - Role Levels', + 'excerpt' => 'Exposes the old user levels system, which fixes the WordPress author drop-down bug when users don’t have a role with one of the assigned levels.' + ), + + 'members-role-hierarchy' => array( + 'url' => 'https://themehybrid.com/plugins/members-role-hierarchy', + 'title' => 'Members - Role Hierarchy', + 'excerpt' => 'Add-on plugin for Members, which creates a hierarchical roles system.' + ), + + 'members-block-permissions' => array( + 'url' => '', + 'title' => 'Members - Block Permissions', + 'excerpt' => '

    COMING SOON!

    Hide or show blocks in the WordPress block editor (Gutenberg).

    ' + ) +); diff --git a/admin/functions-addons.php b/admin/functions-addons.php new file mode 100644 index 0000000..abe35e0 --- /dev/null +++ b/admin/functions-addons.php @@ -0,0 +1,113 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +# Register addons. +add_action( 'members_register_addons', 'members_register_default_addons', 5 ); + +/** + * Registers any addons stored globally with WordPress. + * + * @since 2.0.0 + * @access public + * @param object $wp_addons + * @return void + */ +function members_register_default_addons() { + + $data = include members_plugin()->dir . 'admin/config/addons.php'; + + // If we have an array of data, let's roll. + if ( ! empty( $data ) && is_array( $data ) ) { + + foreach ( $data as $addon => $options ) { + members_register_addon( $addon, $options ); + } + } +} + +/** + * Returns the instance of the addon registry. + * + * @since 2.0.0 + * @access public + * @return object + */ +function members_addon_registry() { + + return \Members\Registry::get_instance( 'addon' ); +} + +/** + * Returns all registered addons. + * + * @since 2.0.0 + * @access public + * @return array + */ +function members_get_addons() { + + return members_addon_registry()->get_collection(); +} + +/** + * Registers a addon. + * + * @since 2.0.0 + * @access public + * @param string $name + * @param array $args + * @return void + */ +function members_register_addon( $name, $args = array() ) { + + members_addon_registry()->register( $name, new \Members\Addon( $name, $args ) ); +} + +/** + * Unregisters a addon. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return void + */ +function members_unregister_addon( $name ) { + + members_addon_registry()->unregister( $name ); +} + +/** + * Returns a addon object. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return object + */ +function members_get_addon( $name ) { + + return members_addon_registry()->get( $name ); +} + +/** + * Checks if a addon object exists. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return bool + */ +function members_addon_exists( $name ) { + + return members_addon_registry()->exists( $name ); +} diff --git a/admin/functions-admin.php b/admin/functions-admin.php index 1c846c2..145f4ff 100644 --- a/admin/functions-admin.php +++ b/admin/functions-admin.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -14,10 +14,6 @@ add_action( 'admin_enqueue_scripts', 'members_admin_register_scripts', 0 ); add_action( 'admin_enqueue_scripts', 'members_admin_register_styles', 0 ); -# Custom manage users columns. -add_filter( 'manage_users_columns', 'members_manage_users_columns' ); -add_filter( 'manage_users_custom_column', 'members_manage_users_custom_column', 10, 3 ); - /** * Get an Underscore JS template. * @@ -27,7 +23,7 @@ * @return bool */ function members_get_underscore_template( $name ) { - require_once( members_plugin()->admin_dir . "tmpl/{$name}.php" ); + require_once( members_plugin()->dir . "admin/tmpl/{$name}.php" ); } /** @@ -41,8 +37,9 @@ function members_admin_register_scripts() { $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; - wp_register_script( 'members-settings', members_plugin()->js_uri . "settings{$min}.js", array( 'jquery' ), '', true ); - wp_register_script( 'members-edit-role', members_plugin()->js_uri . "edit-role{$min}.js", array( 'postbox', 'wp-util' ), '', true ); + wp_register_script( 'members-settings', members_plugin()->uri . "js/settings{$min}.js", array( 'jquery' ), '', true ); + wp_register_script( 'members-edit-post', members_plugin()->uri . "js/edit-post{$min}.js", array( 'jquery' ), '', true ); + wp_register_script( 'members-edit-role', members_plugin()->uri . "js/edit-role{$min}.js", array( 'postbox', 'wp-util' ), '', true ); // Localize our script with some text we want to pass in. $i18n = array( @@ -50,7 +47,8 @@ function members_admin_register_scripts() { 'button_role_ok' => esc_html__( 'OK', 'members' ), 'label_grant_cap' => esc_html__( 'Grant %s capability', 'members' ), 'label_deny_cap' => esc_html__( 'Deny %s capability', 'members' ), - 'ays_delete_role' => esc_html__( 'Are you sure you want to delete this role? This is a permanent action and cannot be undone.', 'members' ) + 'ays_delete_role' => esc_html__( 'Are you sure you want to delete this role? This is a permanent action and cannot be undone.', 'members' ), + 'hidden_caps' => members_get_hidden_caps() ); wp_localize_script( 'members-edit-role', 'members_i18n', $i18n ); @@ -67,7 +65,7 @@ function members_admin_register_styles() { $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; - wp_register_style( 'members-admin', members_plugin()->css_uri . "admin{$min}.css" ); + wp_register_style( 'members-admin', members_plugin()->uri . "css/admin{$min}.css" ); } /** @@ -112,7 +110,7 @@ function members_delete_role( $role ) { remove_role( $role ); // Remove the role from the role factory. - members_role_factory()->remove_role( $role ); + members_unregister_role( $role ); } /** @@ -128,68 +126,3 @@ function members_get_user_meta_keys() { return $wpdb->get_col( "SELECT meta_key FROM $wpdb->usermeta GROUP BY meta_key ORDER BY meta_key" ); } - -/** - * Adds custom columns to the `users.php` screen. - * - * @since 1.0.0 - * @access public - * @param array $columns - * @return array - */ -function members_manage_users_columns( $columns ) { - - // If multiple roles per user is not enabled, bail. - if ( ! members_multiple_user_roles_enabled() ) - return $columns; - - // Unset the core WP `role` column. - if ( isset( $columns['role'] ) ) - unset( $columns['role'] ); - - // Add our new roles column. - $columns['roles'] = esc_html__( 'Roles', 'members' ); - - // Move the core WP `posts` column to the end. - if ( isset( $columns['posts'] ) ) { - $p = $columns['posts']; - unset( $columns['posts'] ); - $columns['posts'] = $p; - } - - return $columns; -} - -/** - * Handles the output of the roles column on the `users.php` screen. - * - * @since 1.0.0 - * @access public - * @param string $output - * @param string $column - * @param int $user_id - * @return string - */ -function members_manage_users_custom_column( $output, $column, $user_id ) { - - if ( 'roles' === $column && members_multiple_user_roles_enabled() ) { - - $user = new WP_User( $user_id ); - - $user_roles = array(); - $output = esc_html__( 'None', 'members' ); - - if ( is_array( $user->roles ) ) { - - foreach ( $user->roles as $role ) { - - if ( members_role_exists( $role ) ) - $user_roles[] = members_translate_role( $role ); - } - - $output = join( ', ', $user_roles ); - } - } - - return $output; -} diff --git a/admin/functions-help.php b/admin/functions-help.php index 92d5009..a439630 100644 --- a/admin/functions-help.php +++ b/admin/functions-help.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -21,7 +21,7 @@ function members_get_help_sidebar_text() { // Get docs and help links. $docs_link = sprintf( '
  • %s
  • ', esc_html__( 'Documentation', 'members' ) ); - $help_link = sprintf( '
  • %s
  • ', esc_html__( 'Support Forums', 'members' ) ); + $help_link = sprintf( '
  • %s
  • ', esc_html__( 'Support Forums', 'members' ) ); // Return the text. return sprintf( @@ -108,6 +108,13 @@ function members_edit_role_help_overview_cb() { ?>

    + +

    + ' . esc_html__( 'Roles and Capabilities', 'members' ) . '' + ); ?> +

    - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members - * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - */ - -# Registers default groups. -add_action( 'init', 'members_register_role_groups', 15 ); - -/** - * Returns the instance of the `Members_Role_Group_Factory` object. Use this function to access the object. - * - * @see Members_Role_Group_Factory - * @since 1.0.0 - * @access public - * @return object - */ -function members_role_group_factory() { - return Members_Role_Group_Factory::get_instance(); -} - -/** - * Function for registering a role group. - * - * @see Members_Role_Group_Factory::register_group() - * @since 1.0.0 - * @access public - * @param string $name - * @param array $args - * @return void - */ -function members_register_role_group( $name, $args = array() ) { - members_role_group_factory()->register_group( $name, $args ); -} - -/** - * Unregisters a group. - * - * @see Members_Role_Group_Factory::unregister_group() - * @since 1.0.0 - * @access public - * @param string $name - * @return void - */ -function members_unregister_role_group( $name ) { - members_role_group_factory()->unregister_group( $name ); -} - -/** - * Checks if a group exists. - * - * @see Members_Role_Group_Factory::group_exists() - * @since 1.0.0 - * @access public - * @param string $name - * @return bool - */ -function members_role_group_exists( $name ) { - return members_role_group_factory()->group_exists( $name ); -} - -/** - * Returns an array of registered group objects. - * - * @see Members_Role_Group_Factory::group - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_role_groups() { - return members_role_group_factory()->groups; -} - -/** - * Returns a group object if it exists. Otherwise, `FALSE`. - * - * @see Members_Role_Group_Factory::get_group() - * @see Members_Role_Group - * @since 1.0.0 - * @access public - * @param string $name - * @return object|bool - */ -function members_get_role_group( $name ) { - return members_role_group_factory()->get_group( $name ); -} - -/** - * Registers the default role groups. - * - * @since 1.0.0 - * @access public - * @return void - */ -function members_register_role_groups() { - - // Get the current user. - $current_user = wp_get_current_user(); - - if ( is_object( $current_user ) ) { - - // Register the mine group. - members_register_role_group( 'mine', - array( - 'label' => esc_html__( 'Mine', 'members' ), - 'label_count' => _n_noop( 'Mine %s', 'Mine %s', 'members' ), - 'roles' => $current_user->roles, - ) - ); - } - - // Register the active group. - members_register_role_group( 'active', - array( - 'label' => esc_html__( 'Has Users', 'members' ), - 'label_count' => _n_noop( 'Has Users %s', 'Has Users %s', 'members' ), - 'roles' => array(), // These will be updated on the fly b/c it requires counting users. - ) - ); - - // Register the inactive group. - members_register_role_group( 'inactive', - array( - 'label' => esc_html__( 'No Users', 'members' ), - 'label_count' => _n_noop( 'No Users %s', 'No Users %s', 'members' ), - 'roles' => array(), // These will be updated on the fly b/c it requires counting users. - ) - ); - - // Register the editable group. - members_register_role_group( 'editable', - array( - 'label' => esc_html__( 'Editable', 'members' ), - 'label_count' => _n_noop( 'Editable %s', 'Editable %s', 'members' ), - 'roles' => members_get_editable_role_slugs(), - ) - ); - - // Register the uneditable group. - members_register_role_group( 'uneditable', - array( - 'label' => esc_html__( 'Uneditable', 'members' ), - 'label_count' => _n_noop( 'Uneditable %s', 'Uneditable %s', 'members' ), - 'roles' => members_get_uneditable_role_slugs(), - ) - ); - - // Register the WordPress group. - members_register_role_group( 'wordpress', - array( - 'label' => esc_html__( 'WordPress', 'members' ), - 'label_count' => _n_noop( 'WordPress %s', 'WordPress %s', 'members' ), - 'roles' => members_get_wordpress_role_slugs(), - ) - ); - - // Hook for registering role groups. Plugins should always register on this hook. - do_action( 'members_register_role_groups' ); -} diff --git a/admin/functions-settings.php b/admin/functions-settings.php new file mode 100644 index 0000000..de2583f --- /dev/null +++ b/admin/functions-settings.php @@ -0,0 +1,129 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +# Register settings views. +add_action( 'members_register_settings_views', 'members_register_default_settings_views', 5 ); + +/** + * Registers the plugin's built-in settings views. + * + * @since 2.0.0 + * @access public + * @param object $manager + * @return void + */ +function members_register_default_settings_views( $manager ) { + + // Bail if not on the settings screen. + if ( 'members-settings' !== $manager->name ) + return; + + // Register general settings view (default view). + $manager->register_view( + new \Members\Admin\View_General( + 'general', + array( + 'label' => esc_html__( 'General', 'members' ), + 'priority' => 0 + ) + ) + ); + + // Register add-ons view. + $manager->register_view( + new \Members\Admin\View_Addons( + 'add-ons', + array( + 'label' => esc_html__( 'Add-Ons', 'members' ), + 'priority' => 95 + ) + ) + ); + + // Register add-ons view. + $manager->register_view( + new \Members\Admin\View_Donate( + 'donate', + array( + 'label' => esc_html__( 'Help Fund Version 3.0', 'members' ), + 'priority' => 100 + ) + ) + ); +} + +/** + * Conditional function to check if on the plugin's settings page. + * + * @since 2.0.0 + * @access public + * @return bool + */ +function members_is_settings_page() { + + $screen = get_current_screen(); + + return is_object( $screen ) && 'settings_page_members-settings' === $screen->id; +} + +/** + * Returns the URL to the settings page. + * + * @since 2.0.0 + * @access public + * @return string + */ +function members_get_settings_page_url() { + + return add_query_arg( array( 'page' => 'members-settings' ), admin_url( 'options-general.php' ) ); +} + +/** + * Returns the URL to a settings view page. + * + * @since 2.0.0 + * @access public + * @param string $view + * @return string + */ +function members_get_settings_view_url( $view ) { + + return add_query_arg( array( 'view' => sanitize_key( $view ) ), members_get_settings_page_url() ); +} + +/** + * Returns the current settings view name. + * + * @since 2.0.0 + * @access public + * @return string + */ +function members_get_current_settings_view() { + + if ( ! members_is_settings_page() ) + return ''; + + return isset( $_GET['view'] ) ? sanitize_key( $_GET['view'] ) : 'general'; +} + +/** + * Conditional function to check if on a specific settings view page. + * + * @since 2.0.0 + * @access public + * @param string $view + * @return bool + */ +function members_is_settings_view( $view = '' ) { + + return members_is_settings_page() && $view === members_get_current_settings_view(); +} diff --git a/admin/tmpl/cap-control.php b/admin/tmpl/cap-control.php index 86d5278..e401d03 100644 --- a/admin/tmpl/cap-control.php +++ b/admin/tmpl/cap-control.php @@ -4,15 +4,15 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ ?> - + diff --git a/admin/tmpl/cap-section.php b/admin/tmpl/cap-section.php index 6d7d1c5..63d5ea6 100644 --- a/admin/tmpl/cap-section.php +++ b/admin/tmpl/cap-section.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ ?> diff --git a/admin/views/class-view-addons.php b/admin/views/class-view-addons.php new file mode 100644 index 0000000..bb7d06c --- /dev/null +++ b/admin/views/class-view-addons.php @@ -0,0 +1,238 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members\Admin; + +/** + * Sets up and handles the add-ons settings view. + * + * @since 2.0.0 + * @access public + */ +class View_Addons extends View { + + /** + * Enqueues scripts/styles. + * + * @since 2.2.0 + * @access public + * @return void + */ + public function enqueue() { + wp_enqueue_style( 'members-admin' ); + } + + /** + * Renders the settings page. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function template() { + + require_once( members_plugin()->dir . 'admin/class-addon.php' ); + require_once( members_plugin()->dir . 'admin/functions-addons.php' ); + + do_action( 'members_register_addons' ); + + $addons = members_get_addons(); ?> + +
    + +
    + +
    + +
    + + +
    +

    + +

    +

    + +

    +

    + +

    +
    +
    + +
    + +
    + +
    + +
    + + + + + + addon_card( $addon ); ?> + + + + + +
    +

    + +

    +
    + + + +
    + + + + + add_help_tab( + array( + 'id' => 'overview', + 'title' => esc_html__( 'Overview', 'members' ), + 'callback' => array( $this, 'help_tab_overview' ) + ) + ); + + // Roles/Caps help tab. + $screen->add_help_tab( + array( + 'id' => 'download', + 'title' => esc_html__( 'Download', 'members' ), + 'callback' => array( $this, 'help_tab_download' ) + ) + ); + + // Roles/Caps help tab. + $screen->add_help_tab( + array( + 'id' => 'purchase', + 'title' => esc_html__( 'Purchase', 'members' ), + 'callback' => array( $this, 'help_tab_purchase' ) + ) + ); + + // Set the help sidebar. + $screen->set_help_sidebar( members_get_help_sidebar_text() ); + } + + /** + * Displays the overview help tab. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function help_tab_overview() { ?> + +

    + +

    + + +

    + +

    + + +

    + +

    + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members\Admin; + +/** + * Sets up and handles the donate settings view. + * + * @since 2.2.0 + * @access public + */ +class View_Donate extends View { + + /** + * Enqueues scripts/styles. + * + * @since 2.2.0 + * @access public + * @return void + */ + public function enqueue() { + wp_enqueue_style( 'members-admin' ); + } + + /** + * Renders the settings page. + * + * @since 2.2.0 + * @access public + * @return void + */ + public function template() { ?> + +
    + +
    + +
    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    +

    + +

    + +
    + +
    + +
    + + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members\Admin; + +/** + * Sets up and handles the general settings view. + * + * @since 2.0.0 + * @access public + */ +class View_General extends View { + + /** + * Holds an array the plugin settings. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $settings = array(); + + /** + * Enqueue scripts/styles. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function enqueue() { + + wp_enqueue_script( 'members-settings' ); + } + + /** + * Registers the plugin settings. + * + * @since 2.0.0 + * @access public + * @return void + */ + function register_settings() { + + // Get the current plugin settings w/o the defaults. + $this->settings = get_option( 'members_settings' ); + + // Register the setting. + register_setting( 'members_settings', 'members_settings', array( $this, 'validate_settings' ) ); + + /* === Settings Sections === */ + + // Add settings sections. + add_settings_section( 'roles_caps', esc_html__( 'Roles and Capabilities', 'members' ), array( $this, 'section_roles_caps' ), 'members-settings' ); + add_settings_section( 'content_permissions', esc_html__( 'Content Permissions', 'members' ), '__return_false', 'members-settings' ); + add_settings_section( 'sidebar_widgets', esc_html__( 'Sidebar Widgets', 'members' ), '__return_false', 'members-settings' ); + add_settings_section( 'private_site', esc_html__( 'Private Site', 'members' ), '__return_false', 'members-settings' ); + + /* === Settings Fields === */ + + // Role manager fields. + add_settings_field( 'enable_role_manager', esc_html__( 'Role Manager', 'members' ), array( $this, 'field_enable_role_manager' ), 'members-settings', 'roles_caps' ); + add_settings_field( 'enable_multi_roles', esc_html__( 'Multiple User Roles', 'members' ), array( $this, 'field_enable_multi_roles' ), 'members-settings', 'roles_caps' ); + add_settings_field( 'explicit_denied_caps', esc_html__( 'Capabilities', 'members' ), array( $this, 'field_explicit_denied_caps' ), 'members-settings', 'roles_caps' ); + + // Content permissions fields. + add_settings_field( 'enable_content_permissions', esc_html__( 'Enable Permissions', 'members' ), array( $this, 'field_enable_content_permissions' ), 'members-settings', 'content_permissions' ); + add_settings_field( 'content_permissions_error', esc_html__( 'Error Message', 'members' ), array( $this, 'field_content_permissions_error' ), 'members-settings', 'content_permissions' ); + + // Widgets fields. + add_settings_field( 'widget_login', esc_html__( 'Login Widget', 'members' ), array( $this, 'field_widget_login' ), 'members-settings', 'sidebar_widgets' ); + add_settings_field( 'widget_users', esc_html__( 'Users Widget', 'members' ), array( $this, 'field_widget_users' ), 'members-settings', 'sidebar_widgets' ); + + // Private site fields. + add_settings_field( 'enable_private_site', esc_html__( 'Enable Private Site', 'members' ), array( $this, 'field_enable_private_site' ), 'members-settings', 'private_site' ); + add_settings_field( 'private_rest_api', esc_html__( 'REST API', 'members' ), array( $this, 'field_private_rest_api' ), 'members-settings', 'private_site' ); + add_settings_field( 'enable_private_feed', esc_html__( 'Disable Feed', 'members' ), array( $this, 'field_enable_private_feed' ), 'members-settings', 'private_site' ); + add_settings_field( 'private_feed_error', esc_html__( 'Feed Error Message', 'members' ), array( $this, 'field_private_feed_error' ), 'members-settings', 'private_site' ); + } + + /** + * Validates the plugin settings. + * + * @since 2.0.0 + * @access public + * @param array $input + * @return array + */ + function validate_settings( $settings ) { + + // Validate true/false checkboxes. + $settings['role_manager'] = ! empty( $settings['role_manager'] ) ? true : false; + $settings['explicit_denied_caps'] = ! empty( $settings['explicit_denied_caps'] ) ? true : false; + $settings['show_human_caps'] = ! empty( $settings['show_human_caps'] ) ? true : false; + $settings['multi_roles'] = ! empty( $settings['multi_roles'] ) ? true : false; + $settings['content_permissions'] = ! empty( $settings['content_permissions'] ) ? true : false; + $settings['login_form_widget'] = ! empty( $settings['login_form_widget'] ) ? true : false; + $settings['users_widget'] = ! empty( $settings['users_widget'] ) ? true : false; + $settings['private_blog'] = ! empty( $settings['private_blog'] ) ? true : false; + $settings['private_rest_api'] = ! empty( $settings['private_rest_api'] ) ? true : false; + $settings['private_feed'] = ! empty( $settings['private_feed'] ) ? true : false; + + // Kill evil scripts. + $settings['content_permissions_error'] = stripslashes( wp_filter_post_kses( addslashes( $settings['content_permissions_error'] ) ) ); + $settings['private_feed_error'] = stripslashes( wp_filter_post_kses( addslashes( $settings['private_feed_error'] ) ) ); + + // Return the validated/sanitized settings. + return $settings; + } + + /** + * Role/Caps section callback. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function section_roles_caps() { ?> + +

    + +

    + + + + + +
    + +

    + +

    + +

    + +

    + +
    + + + + + + + 'members_settings[content_permissions_error]', + 'drag_drop_upload' => true, + 'editor_height' => 250 + ) + ); + } + + /** + * Login widget field callback. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function field_widget_login() { ?> + + + + + + + + + + + + + + + 'members_settings[private_feed_error]', + 'drag_drop_upload' => true, + 'editor_height' => 250 + ) + ); + } + + /** + * Renders the settings page. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function template() { ?> + +
    + + + +
    + + add_help_tab( + array( + 'id' => 'roles-caps', + 'title' => esc_html__( 'Role and Capabilities', 'members' ), + 'callback' => array( $this, 'help_tab_roles_caps' ) + ) + ); + + // Content Permissions help tab. + $screen->add_help_tab( + array( + 'id' => 'content-permissions', + 'title' => esc_html__( 'Content Permissions', 'members' ), + 'callback' => array( $this, 'help_tab_content_permissions' ) + ) + ); + + // Widgets help tab. + $screen->add_help_tab( + array( + 'id' => 'sidebar-widgets', + 'title' => esc_html__( 'Sidebar Widgets', 'members' ), + 'callback' => array( $this, 'help_tab_sidebar_widgets' ) + ) + ); + + // Private Site help tab. + $screen->add_help_tab( + array( + 'id' => 'private-site', + 'title' => esc_html__( 'Private Site', 'members' ), + 'callback' => array( $this, 'help_tab_private_site' ) + ) + ); + + // Set the help sidebar. + $screen->set_help_sidebar( members_get_help_sidebar_text() ); + } + + /** + * Displays the roles/caps help tab. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function help_tab_roles_caps() { ?> + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + + +

    + restrict_content' ); ?> +

    + + +

    + +

    + + +

    + +

    + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members\Admin; + +/** + * Settings view base class. + * + * @since 2.0.0 + * @access public + */ +abstract class View { + + /** + * Name/ID for the group. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $name = ''; + + /** + * Internationalized text label for the group. + * + * @since 2.0.0 + * @access protected + * @var string + */ + public $label = ''; + + /** + * Priority (order) the control should be output. + * + * @since 2.0.0 + * @access public + * @var int + */ + public $priority = 10; + + /** + * A user role capability required to show the control. + * + * @since 2.0.0 + * @access public + * @var string|array + */ + public $capability = 'manage_options'; + + /** + * Magic method to use in case someone tries to output the object as a string. + * We'll just return the name. + * + * @since 2.0.0 + * @access public + * @return string + */ + public function __toString() { + return $this->name; + } + + /** + * Register a new object. + * + * @since 2.0.0 + * @access public + * @param string $name + * @param array $args { + * @type string $label Internationalized text label. + * @type string $icon Dashicon icon in the form of `dashicons-icon-name`. + * @type string $callback Callback function for outputting the content for the view. + * } + * @return void + */ + public function __construct( $name, $args = array() ) { + + foreach ( array_keys( get_object_vars( $this ) ) as $key ) { + + if ( isset( $args[ $key ] ) ) + $this->$key = $args[ $key ]; + } + + $this->name = sanitize_key( $name ); + } + + /** + * Runs on the `load-{$page}` hook + * + * @since 2.0.0 + * @access public + * @return void + */ + public function load() {} + + /** + * Enqueue scripts/styles for the control. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function enqueue() {} + + /** + * Register settings for the view. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function register_settings() {} + + /** + * Add help tabs for the view. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function add_help_tabs() {} + + /** + * Output the content for the view. + * + * @since 2.0.0 + * @access public + * @return void + */ + public function template() {} + + /** + * Checks if the control should be allowed at all. + * + * @since 2.0.0 + * @access public + * @return bool + */ + public function check_capabilities() { + + if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) + return false; + + return true; + } +} diff --git a/changelog.md b/changelog.md index 10753f6..31f87d3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,117 @@ # Change Log +## [2.2.0] - 2019-07-20 + +### Added + +- Settings page sub-view for donations. + +### Fixed + +- Makes sure that we have a post object when determining whether a user can view a given post. +- Makes sure that denied capabilities are an array when filtering `user_has_cap`. +- Typo in `readme.txt`. + +### Changed + +- Links user count on edit role screen to users by role page. +- Links user count on manage roles screen to users by role page. +- Updated the add-ons sub-view with design changes and a "pro" section. + +## [2.1.0] - 2018-04-19 + +### Added + +* `user_name` parameter for the `[members_access]` shortcode. +* `user_id` parameter for the `[members_access]` shortcode. +* `user_email` parameter for the `[members_access]` shortcode. +* `icon-members-core-create-caps.png` file for local access to the corresponding add-on thumbnail. + +### Fixed + +* Incorrect roles (or empty roles) appearing on sub-sites when running multisite. +* Invalid icon URL on the Settings > Members > Add-ons admin screen. +* Fix for WooCommerce empty page ID in the private site feature. + +### Changed + +* Bumped minimum PHP requirement to 5.6+. However, the code should still work on PHP 5.3. This is in preparation for a hard requirement of 5.6+ when version 3.0.0 is released. + +## [2.0.2] - 2017-11-06 + +### Fixed + +* Role labels should now be handled correctly where the plugin outputs them. This was not corrected in 2.0.1 as previously thought. + +### Security + +* Corrected a capability escalation issue on multisite where administrators could assign themselves network caps. Administrators are no longer given the `create_role`, `edit_role`, and `delete_role` caps on multisite. A Super Admin must manually set these caps if they want to grant them to a site administrator. Additionally, network caps are always blocked from being assigned. + +## [2.0.1] - 2017-09-28 + +### Changed + +* Cap groups registered by the plugin bumped up 5 in priority. Primarily, this was to make sure post type cap groups were registered earlier than the default of `10`. +* Better handling of the PHP notice. We're going to check prior to loading any other code and add an admin notice if the site doesn't meet minimum requirements. + +### Fixed + +* The `show_human_caps` setting was not properly saving because it wasn't accounted for in the validation callback. +* Make sure to use the correct `$old_user_data` variable when removing all roles from a user. Otherwise, this doesn't work. +* Super admins should be able to see any private site when multisite is enabled. +* Make sure that role labels (where the plugin outputs them) are the translated version. +* Multi-role selection has been removed for the Add New User screen on multisite installs. This was to address multiple issues in core with hardcoded values that the plugin couldn't filter. The feature may return in the future in a different form. Note that this doesn't change single-site installs. + +## [2.0.0] - 2017-07-19 + +### Added + +* Created a new admin view system, where there can be multiple views ("tabs", if you like) on the settings screen and other potential screens in the future. +* Added an "Add-Ons" view for the Members Settings page, which lists add-ons for the plugin. The default add-ons are pulled from Theme Hybrid. +* Created an add-ons API for other developers to list their own add-ons. +* Created a capability registration system and API for plugin developers to register their capabilities with Members. The major benefit of this is having internationalized labels for capabilities. +* Registered all of the core capabilities with human-readable, internationalized text labels so that users who do not speak English can benefit from capability translations. +* Added the `members_enable_{$post_type}_content_permissions` filter hook to allow developers to enable/disable content permissions per post type. +* Removed the core "Change Role" feature on the manage users screen and replaced it with an "Add Role" and "Remove Role" feature for bulk editing users. Note that this only appears when multiple roles is enabled. +* Add a multi-role checkbox on the Add User screen when multiple roles are enabled. +* Added a `members_has_post_roles()` conditional tag for checking if a post has content permissions roles assigned to it. +* Added the `operator` parameter, which accepts a value of `!`, for the `[members_access]` shortcode. This allows users to negate the role or cap they've passed in. + +### Changed + +* Roles states on the manage role screen now have array keys so they can more easily be overwritten. +* Created a `.wp-tab-panel` wrapper around the user roles checklist on the edit user screen. This is to prevent long lists of roles from taking up too much screen space. +* Moved the All capability tab to the bottom of the capability tabs list. +* Removed the All cap group, since it is not a true cap group. +* Removed the All, Mine, Active, Inactive, Editable, and Uneditable role groups. These are merely views for the role management screen. They are not true role groups. +* Created a new, internal registry class for storing various collections of data. Note that this does break back-compat for developers who were directly accessing factory classes in previous versions rather than using the wrapper functions. +* Old classes were added to the `Members` namespace or `Members\Admin` namespace. Again, this breaks back-compat for anyone not using the appropriate wrapper functions. This also bumps the requirement to PHP 5.3.0+. +* Changed the text in the Content Permissions meta box to not specifically use the term "post". +* Changed the Content Permissions meta box to have a tabbed UI so that it doesn't take up so much screen space. +* Replaced the basic textarea with the WP editor in the Content Permissions meta box. + +### Fixed + +* No longer save content permissions on autosave or for post revisions. +* Prevent users from adding the internal core WP `do_not_allow` capability to their role. +* On multisite with the private site feature enabled, block access to logged-in users who do not have access to the specific blog. +* Make sure comments show an error in the comments feed if they belong to a private post. + +### Security + +* General hardening and use of some better functions for escaping. +* Added a private REST API option for users who are utilizing the private site feature. Users will likely want to enable this. Otherwise, their "private site" is exposed via the REST API endpoints. + +## [1.1.3] - 2017-02-06 + +### Fixed + +* Removes the `customize_changeset` and `custom_css` post type cap groups so that they don't appear in the capability tabs on the edit role screen. + +### Security + +* Added a check that the role is editable before attempting to add a new role to a user. + ## [1.1.2] - 2016-06-20 ### Fixed diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2b1c001 --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name" : "justintadlock/members", + "description" : "A user and role management plugin that puts you in full control of your site's permissions. This plugin allows you to edit your roles and their capabilities, clone existing roles, assign multiple roles per user, block post content, or even make your site completely private.", + "keywords" : ["wordpress"], + "homepage" : "https://themehybrid.com/plugins/members", + "license" : "GPL-2.0+", + "type" : "wordpress-plugin", + "authors" : [ + { + "name" : "Justin Tadlock", + "email" : "justintadlock@gmail.com", + "homepage" : "http://justintadlock.com" + } + ], + "require" : { + "composer/installers" : "^1.0", + "php" : ">=5.3.0" + }, + "support" : { + "issues": "https://github.com/justintadlock/members/issues", + "forum" : "https://themehybrid.com/board/topics" + } +} diff --git a/css/admin.css b/css/admin.css index cb89050..0160385 100644 --- a/css/admin.css +++ b/css/admin.css @@ -1,3 +1,79 @@ +/* ====== Members Settings Screen. ====== */ + +.settings_page_members-settings .welcome-panel { + margin-top: 0; + padding: 40px 20px; +} + +.settings_page_members-settings .welcome-panel .about-description { + margin: 20px 0; +} + +.settings_page_members-settings .welcome-panel-content { + margin: 0; +} + +@media screen and (min-width: 870px) { + + .settings_page_members-settings .members-short-p { + max-width: 612px; + margin-left: auto; + margin-right: auto; + } + + .settings_page_members-settings .welcome-panel-content { + text-align: center; + } +} + +.settings_page_members-settings .welcome-panel .button.button-hero { + margin-left: auto; + margin-right: auto; +} + +.settings_page_members-settings .members-svg-wrap { + display: inline-block; + margin: 0 auto 20px; +} + +@media screen and (max-width: 870px) { + .settings_page_members-settings .welcome-panel { + padding: 20px; + } + + .settings_page_members-settings .members-svg-wrap { + float: left; + margin-right: 28px; + } + + .settings_page_members-settings.rtl .members-svg-wrap { + float: right; + margin-right: 0; + margin-left: 28px; + } +} + +.settings_page_members-settings .members-svg-link { + display: table-cell; + text-align: center; + width: 128px; + height: 128px; + background: #363b3f; + color: #fff; + padding: 24px 16px 16px; + border-radius: 50%; + box-sizing: border-box; + border: 4px solid #ffffff; + box-shadow: 0 0 0 4px #363b3f; +} + +.settings_page_members-settings .members-svg-link svg { + max-width: 84px; + max-height: 64px; + width: auto; + height: auto; +} + /* ====== Manage Roles Screen. ====== */ @media only screen and ( min-width: 783px ) { @@ -38,19 +114,21 @@ #tabcapsdiv { margin-top: 1em; } - #tabcapsdiv > h3 { + #tabcapsdiv > .hndle { padding: 10px; border-bottom: 1px solid #eee; } - #tabcapsdiv .inside { + #tabcapsdiv .inside, + #members-cp .inside { margin: 0; padding: 0; } /* Tabs wrapper. */ -.members-cap-tabs { +.members-cap-tabs, +.members-tabs { overflow: hidden; background: #fff; background: linear-gradient( 90deg, #fafafa 0%, #fafafa 20%, #fff 20%, #fff 100% ); @@ -58,14 +136,16 @@ @media only screen and ( max-width: 782px ) { - .members-cap-tabs { + .members-cap-tabs, + .members-tabs { background: linear-gradient( 90deg, #fafafa 0%, #fafafa 48px, #fff 48px, #fff 100% ); } } /* Tab nav. */ - .members-cap-tabs .members-tab-nav { + .members-cap-tabs .members-tab-nav, + .members-tabs .members-tab-nav { position: relative; float: left; list-style: none; @@ -78,7 +158,8 @@ box-sizing: border-box; } - .members-cap-tabs .members-tab-nav li { + .members-cap-tabs .members-tab-nav li, + .members-tabs .members-tab-nav li { display: block; position: relative; margin: 0; @@ -86,7 +167,8 @@ line-height: 20px; } - .members-cap-tabs .members-tab-nav li a { + .members-cap-tabs .members-tab-nav li a, + .members-tabs .members-tab-nav li a { display: block; margin: 0; padding: 10px; @@ -96,12 +178,14 @@ box-shadow: none; } - .members-cap-tabs .members-tab-nav li a .dashicons { + .members-cap-tabs .members-tab-nav li a .dashicons, + .members-tabs .members-tab-nav li a .dashicons { line-height: 20px; margin-right: 3px; } - .members-cap-tabs .members-tab-nav li[aria-selected="true"] a { + .members-cap-tabs .members-tab-nav li[aria-selected="true"] a, + .members-tabs .members-tab-nav li[aria-selected="true"] a { position: relative; font-weight: bold; color: #555; @@ -110,15 +194,18 @@ @media only screen and ( max-width: 782px ) { - .members-cap-tabs .members-tab-nav { width: 48px; } + .members-cap-tabs .members-tab-nav, + .members-tabs .members-tab-nav { width: 48px; } - .members-cap-tabs .members-tab-nav li a .dashicons { + .members-cap-tabs .members-tab-nav li a .dashicons, + .members-tabs .members-tab-nav li a .dashicons { width: 24px; height: 24px; font-size: 24px; line-height: 24px; } + .members-tab-nav li .dashicons::before, .members-tab-nav li .dashicons::before { width: 24px; height: 24px; @@ -136,7 +223,8 @@ /* Tab content wrapper */ - .members-cap-tabs .members-tab-wrap { + .members-cap-tabs .members-tab-wrap, + .members-tabs .members-tab-wrap { float: left; width: 80%; margin-left: -1px; @@ -144,11 +232,87 @@ @media only screen and ( max-width: 782px ) { - .members-cap-tabs .members-tab-wrap { + .members-cap-tabs .members-tab-wrap, + .members-tabs .members-tab-wrap { width: calc( 100% - 48px ); } } +#members-cp .members-tab-content { + padding: 10px; + border-left: 1px solid #e5e5e5; +} + +/* === Manager when in the side meta box. === */ + +@media only screen and ( min-width: 850px ) { + + #side-sortables .members-tabs { background: #fff; } + + #side-sortables .members-tabs .members-tab-wrap { width: 100%; } + + #side-sortables .members-tabs .members-tab-nav { + display: table; + width: 100%; + } + + #side-sortables .members-tabs .members-tab-nav li { + display: table-cell; + text-align: center; + border-right: 1px solid #eee; + } + + #side-sortables .members-tabs .members-tab-nav li:last-of-type { border-right: none; } + + #side-sortables .members-tabs .members-tab-nav li a { + padding: 10px 0; + } + + #side-sortables .members-tabs .members-tab-nav .dashicons { + width: 24px; + height: 24px; + font-size: 24px; + line-height: 24px; + } + + #side-sortables .members-tabs .members-tab-nav .dashicons::before { + width: 24px; + height: 24px; + } + + #side-sortables .members-tabs .members-tab-nav .label { + overflow: hidden; + position: absolute; + top: -1000em; + left: -1000em; + width: 1px; + height: 1px; + } + } + + + .members-tabs .members-tabs-label { + display : block !important; + font-weight : bold; + display : inline-block; + margin-bottom : 4px; + } + + .members-tabs .butterbean-control-checkbox .members-tabs-label { + display: inline !important; + } + + .members-tabs .members-tabs-description { + display : block; + font-style : italic; + margin-top : 4px; + } + + .members-tabs .members-tabs-label + .members-tabs-description { + margin-top : 0; + margin-bottom : 4px; + } + /* Tab tables. */ #tabcapsdiv table { diff --git a/css/admin.min.css b/css/admin.min.css index a7704e9..dc522be 100644 --- a/css/admin.min.css +++ b/css/admin.min.css @@ -1 +1 @@ -@media only screen and (min-width:783px){.users_page_roles .column-users,.users_page_roles .column-granted_caps,.users_page_roles .column-denied_caps{width:125px;text-align:center}}.members-title-div #titlewrap input{padding:0 8px;font-size:1.7em;line-height:normal;height:1.7em;width:100%;outline:0;margin:0 0 3px;background-color:#fff}.members-title-div input[name=role]{font-size:13px;height:22px;margin:0;width:16em}#tabcapsdiv{margin-top:1em}#tabcapsdiv>h3{padding:10px;border-bottom:1px solid #eee}#tabcapsdiv .inside{margin:0;padding:0}.members-cap-tabs{overflow:hidden;background:#fff;background:linear-gradient(90deg,#fafafa 0,#fafafa 20%,#fff 20%,#fff 100%)}@media only screen and (max-width:782px){.members-cap-tabs{background:linear-gradient(90deg,#fafafa 0,#fafafa 48px,#fff 48px,#fff 100%)}}.members-cap-tabs .members-tab-nav{position:relative;float:left;list-style:none;width:20%;line-height:1em;margin:0 0 -1px 0;padding:0;background-color:#fafafa;border-right:1px solid #eee;box-sizing:border-box}.members-cap-tabs .members-tab-nav li{display:block;position:relative;margin:0;padding:0;line-height:20px}.members-cap-tabs .members-tab-nav li a{display:block;margin:0;padding:10px;line-height:20px!important;text-decoration:none;border-bottom:1px solid #eee;box-shadow:none}.members-cap-tabs .members-tab-nav li a .dashicons{line-height:20px;margin-right:3px}.members-cap-tabs .members-tab-nav li[aria-selected=true] a{position:relative;font-weight:700;color:#555;background-color:#e0e0e0}@media only screen and (max-width:782px){.members-cap-tabs .members-tab-nav{width:48px}.members-cap-tabs .members-tab-nav li a .dashicons{width:24px;height:24px;font-size:24px;line-height:24px}.members-tab-nav li .dashicons::before{width:24px;height:24px}.members-tab-nav li .label{overflow:hidden;position:absolute;top:-1000em;left:-1000em;width:1px;height:1px}}.members-cap-tabs .members-tab-wrap{float:left;width:80%;margin-left:-1px}@media only screen and (max-width:782px){.members-cap-tabs .members-tab-wrap{width:calc(100% - 48px)}}#tabcapsdiv table{border-right:0;border-top:0;border-bottom:0}#tabcapsdiv table td,#tabcapsdiv table th{padding:10px;padding-bottom:10px;border-bottom:1px solid #eee;font-size:13px;line-height:20px}#tabcapsdiv table td{padding:9px}#tabcapsdiv tbody tr:last-of-type td{border-bottom:0}#tabcapsdiv tfoot th{border-color:#eee}@media only screen and (max-width:782px){#tabcapsdiv table td,#tabcapsdiv table th{line-height:24px}}.members-roles-select .column-grant,.members-roles-select .column-deny{width:70px!important;text-align:center;display:table-cell!important;clear:none!important}.members-cap-tabs button{display:inline;margin:-4px;line-height:inherit;padding:4px 8px;border:1px solid transparent;background:transparent;border-radius:0;outline:0;-webkit-transition:all .25s ease-out;-moz-transition:all .25s ease-out;-o-transition:all .25s ease-out;transition:all .25s ease-out}.members-cap-tabs button:hover,.members-cap-tabs button:focus{border-color:#eee;background:#fafafa;cursor:pointer}.members-cap-tabs button:active{color:#0073aa;border-color:#0073aa}.members-cap-tabs button+.dashicons{display:none;margin-top:1px;margin-bottom:-1px;line-height:inherit}.members-cap-tabs button:hover+.dashicons,.members-cap-tabs button:focus+.dashicons{display:inline-block}.members-tab-content .members-highlight{background-color:rgba(0,115,170,.05)}.members-tab-content tbody{-webkit-transition:all 2s ease-in-out;-moz-transition:all 2s ease-in-out;-o-transition:all 2s ease-in-out;transition:all 2s ease-in-out}.members-cp-role-list-wrap{overflow:auto;min-height:42px;max-height:200px;padding:0 .9em;border:solid 1px #dfdfdf;background-color:#fdfdfd} \ No newline at end of file +.settings_page_members-settings .welcome-panel{margin-top:0;padding:40px 20px}.settings_page_members-settings .welcome-panel .about-description{margin:20px 0}.settings_page_members-settings .welcome-panel-content{margin:0}@media screen and (min-width:870px){.settings_page_members-settings .members-short-p{max-width:612px;margin-left:auto;margin-right:auto}.settings_page_members-settings .welcome-panel-content{text-align:center}}.settings_page_members-settings .welcome-panel .button.button-hero{margin-left:auto;margin-right:auto}.settings_page_members-settings .members-svg-wrap{display:inline-block;margin:0 auto 20px}@media screen and (max-width:870px){.settings_page_members-settings .welcome-panel{padding:20px}.settings_page_members-settings .members-svg-wrap{float:left;margin-right:28px}.settings_page_members-settings.rtl .members-svg-wrap{float:right;margin-right:0;margin-left:28px}}.settings_page_members-settings .members-svg-link{display:table-cell;text-align:center;width:128px;height:128px;background:#363b3f;color:#fff;padding:24px 16px 16px;border-radius:50%;box-sizing:border-box;border:4px solid #fff;box-shadow:0 0 0 4px #363b3f}.settings_page_members-settings .members-svg-link svg{max-width:84px;max-height:64px;width:auto;height:auto}@media only screen and (min-width:783px){.users_page_roles .column-users,.users_page_roles .column-granted_caps,.users_page_roles .column-denied_caps{width:100px;text-align:center}}.members-title-div #titlewrap input{padding:0 8px;font-size:1.7em;line-height:normal;height:1.7em;width:100%;outline:none;margin:0 0 3px;background-color:#fff}.members-title-div input[name="role"]{font-size:13px;height:22px;margin:0;width:16em}#tabcapsdiv{margin-top:1em}#tabcapsdiv>.hndle{padding:10px;border-bottom:1px solid #eee}#tabcapsdiv .inside,#members-cp .inside{margin:0;padding:0}.members-cap-tabs,.members-tabs{overflow:hidden;background:#fff;background:linear-gradient(90deg,#fafafa 0%,#fafafa 20%,#fff 20%,#fff 100%)}@media only screen and (max-width:782px){.members-cap-tabs,.members-tabs{background:linear-gradient(90deg,#fafafa 0%,#fafafa 48px,#fff 48px,#fff 100%)}}.members-cap-tabs .members-tab-nav,.members-tabs .members-tab-nav{position:relative;float:left;list-style:none;width:20%;line-height:1em;margin:0 0 -1px 0;padding:0;background-color:#fafafa;border-right:1px solid #eee;box-sizing:border-box}.members-cap-tabs .members-tab-nav li,.members-tabs .members-tab-nav li{display:block;position:relative;margin:0;padding:0;line-height:20px}.members-cap-tabs .members-tab-nav li a,.members-tabs .members-tab-nav li a{display:block;margin:0;padding:10px;line-height:20px!important;text-decoration:none;border-bottom:1px solid #eee;box-shadow:none}.members-cap-tabs .members-tab-nav li a .dashicons,.members-tabs .members-tab-nav li a .dashicons{line-height:20px;margin-right:3px}.members-cap-tabs .members-tab-nav li[aria-selected="true"] a,.members-tabs .members-tab-nav li[aria-selected="true"] a{position:relative;font-weight:700;color:#555;background-color:#e0e0e0}@media only screen and (max-width:782px){.members-cap-tabs .members-tab-nav,.members-tabs .members-tab-nav{width:48px}.members-cap-tabs .members-tab-nav li a .dashicons,.members-tabs .members-tab-nav li a .dashicons{width:24px;height:24px;font-size:24px;line-height:24px}.members-tab-nav li .dashicons::before,.members-tab-nav li .dashicons::before{width:24px;height:24px}.members-tab-nav li .label{overflow:hidden;position:absolute;top:-1000em;left:-1000em;width:1px;height:1px}}.members-cap-tabs .members-tab-wrap,.members-tabs .members-tab-wrap{float:left;width:80%;margin-left:-1px}@media only screen and (max-width:782px){.members-cap-tabs .members-tab-wrap,.members-tabs .members-tab-wrap{width:calc(100% - 48px)}}#members-cp .members-tab-content{padding:10px;border-left:1px solid #e5e5e5}@media only screen and (min-width:850px){#side-sortables .members-tabs{background:#fff}#side-sortables .members-tabs .members-tab-wrap{width:100%}#side-sortables .members-tabs .members-tab-nav{display:table;width:100%}#side-sortables .members-tabs .members-tab-nav li{display:table-cell;text-align:center;border-right:1px solid #eee}#side-sortables .members-tabs .members-tab-nav li:last-of-type{border-right:none}#side-sortables .members-tabs .members-tab-nav li a{padding:10px 0}#side-sortables .members-tabs .members-tab-nav .dashicons{width:24px;height:24px;font-size:24px;line-height:24px}#side-sortables .members-tabs .members-tab-nav .dashicons::before{width:24px;height:24px}#side-sortables .members-tabs .members-tab-nav .label{overflow:hidden;position:absolute;top:-1000em;left:-1000em;width:1px;height:1px}}.members-tabs .members-tabs-label{display:block!important;font-weight:700;display:inline-block;margin-bottom:4px}.members-tabs .butterbean-control-checkbox .members-tabs-label{display:inline!important}.members-tabs .members-tabs-description{display:block;font-style:italic;margin-top:4px}.members-tabs .members-tabs-label + .members-tabs-description{margin-top:0;margin-bottom:4px}#tabcapsdiv table{border-right:none;border-top:none;border-bottom:none}#tabcapsdiv table td,#tabcapsdiv table th{padding:10px;padding-bottom:10px;border-bottom:1px solid #eee;font-size:13px;line-height:20px}#tabcapsdiv table td{padding:9px}#tabcapsdiv tbody tr:last-of-type td{border-bottom:none}#tabcapsdiv tfoot th{border-color:#eee}@media only screen and (max-width:782px){#tabcapsdiv table td,#tabcapsdiv table th{line-height:24px}}.members-roles-select .column-grant,.members-roles-select .column-deny{width:70px!important;text-align:center;display:table-cell!important;clear:none!important}.members-cap-tabs button{display:inline;margin:-4px;line-height:inherit;padding:4px 8px;border:1px solid transparent;background:transparent;border-radius:0;outline:none;-webkit-transition:all 0.25s ease-out;-moz-transition:all 0.25s ease-out;-o-transition:all 0.25s ease-out;transition:all 0.25s ease-out}.members-cap-tabs button:hover,.members-cap-tabs button:focus{border-color:#eee;background:#fafafa;cursor:pointer}.members-cap-tabs button:active{color:#0073aa;border-color:#0073aa}.members-cap-tabs button + .dashicons{display:none;margin-top:1px;margin-bottom:-1px;line-height:inherit}.members-cap-tabs button:hover + .dashicons,.members-cap-tabs button:focus + .dashicons{display:inline-block}.members-tab-content .members-highlight{background-color:rgba(0,115,170,.05)}.members-tab-content tbody{-webkit-transition:all 2s ease-in-out;-moz-transition:all 2s ease-in-out;-o-transition:all 2s ease-in-out;transition:all 2s ease-in-out}.members-cp-role-list-wrap{overflow:auto;min-height:42px;max-height:200px;padding:0 .9em;border:solid 1px rgb(223,223,223);background-color:rgb(253,253,253)} diff --git a/img/icon-addon.png b/img/icon-addon.png new file mode 100644 index 0000000..ffc0c1b Binary files /dev/null and b/img/icon-addon.png differ diff --git a/img/members-admin-access.svg b/img/members-admin-access.svg new file mode 100644 index 0000000..51f7e60 --- /dev/null +++ b/img/members-admin-access.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/members-block-permissions.svg b/img/members-block-permissions.svg new file mode 100644 index 0000000..78b27d8 --- /dev/null +++ b/img/members-block-permissions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/members-core-create-caps.svg b/img/members-core-create-caps.svg new file mode 100644 index 0000000..cb4dc56 --- /dev/null +++ b/img/members-core-create-caps.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/members-privacy-caps.svg b/img/members-privacy-caps.svg new file mode 100644 index 0000000..905d0fe --- /dev/null +++ b/img/members-privacy-caps.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/members-role-hierarchy.svg b/img/members-role-hierarchy.svg new file mode 100644 index 0000000..845df8f --- /dev/null +++ b/img/members-role-hierarchy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/members-role-levels.svg b/img/members-role-levels.svg new file mode 100644 index 0000000..1245a44 --- /dev/null +++ b/img/members-role-levels.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/members.svg b/img/members.svg new file mode 100644 index 0000000..99981d3 --- /dev/null +++ b/img/members.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/class-cap-group.php b/inc/class-cap-group.php similarity index 70% rename from admin/class-cap-group.php rename to inc/class-cap-group.php index d6def5e..e7bfa94 100644 --- a/admin/class-cap-group.php +++ b/inc/class-cap-group.php @@ -4,25 +4,27 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members; + /** * Capability group object class. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -final class Members_Cap_Group { +final class Cap_Group { /** * Name/ID for the group. * - * @since 1.0.0 - * @access protected + * @since 2.0.0 + * @access public * @var string */ public $name = ''; @@ -30,8 +32,8 @@ final class Members_Cap_Group { /** * Internationalized text label for the group. * - * @since 1.0.0 - * @access protected + * @since 2.0.0 + * @access public * @var string */ public $label = ''; @@ -39,8 +41,8 @@ final class Members_Cap_Group { /** * Icon for the group. This can be a dashicons class or a custom class. * - * @since 1.0.0 - * @access protected + * @since 2.0.0 + * @access public * @var string */ public $icon = 'dashicons-admin-generic'; @@ -48,26 +50,26 @@ final class Members_Cap_Group { /** * Capabilities for the group. * - * @since 1.0.0 - * @access protected + * @since 2.0.0 + * @access public * @var array */ - public $caps = array( 'read' ); + public $caps = array(); /** - * Whether to merge this groups caps with the added caps array. + * Sort order priority. * - * @since 1.0.0 - * @access protected - * @var bool + * @since 2.0.0 + * @access public + * @var int */ - public $merge_added = true; + public $priority = 10; /** * Whether to remove previously-added caps from this group's caps. * - * @since 1.0.0 - * @access protected + * @since 2.0.0 + * @access public * @var bool */ public $diff_added = false; @@ -76,7 +78,7 @@ final class Members_Cap_Group { * Magic method to use in case someone tries to output the object as a string. * We'll just return the name. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return string */ @@ -87,7 +89,7 @@ public function __toString() { /** * Register a new object. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @param string $name * @param array $args { @@ -109,6 +111,10 @@ public function __construct( $name, $args = array() ) { $this->name = sanitize_key( $name ); + $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => $this->name ) ) ); + + $this->caps = array_unique( array_merge( $this->caps, $registered_caps ) ); + $this->caps = members_remove_hidden_caps( $this->caps ); } } diff --git a/inc/class-capability.php b/inc/class-capability.php new file mode 100644 index 0000000..0319354 --- /dev/null +++ b/inc/class-capability.php @@ -0,0 +1,81 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members; + +/** + * Capability class. + * + * @since 2.0.0 + * @access public + */ +class Capability { + + /** + * The capability name. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $name = ''; + + /** + * The capability label. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $label = ''; + + /** + * The group the capability belongs to. + * + * @see Members_Cap_Group + * @since 2.0.0 + * @access public + * @var string + */ + public $group = ''; + + /** + * Return the role string in attempts to use the object as a string. + * + * @since 2.0.0 + * @access public + * @return string + */ + public function __toString() { + return $this->name; + } + + /** + * Creates a new role object. + * + * @since 2.0.0 + * @access public + * @global object $wp_roles + * @param string $role + * @return void + */ + public function __construct( $name, $args = array() ) { + + foreach ( array_keys( get_object_vars( $this ) ) as $key ) { + + if ( isset( $args[ $key ] ) ) + $this->$key = $args[ $key ]; + } + + $this->name = sanitize_key( $name ); + } +} diff --git a/inc/class-registry.php b/inc/class-registry.php new file mode 100644 index 0000000..31b9941 --- /dev/null +++ b/inc/class-registry.php @@ -0,0 +1,149 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members; + +/** + * Base registry class. + * + * @since 2.0.0 + * @access public + */ +class Registry { + + /** + * Registry instances. + * + * @since 2.0.0 + * @access private + * @var array + */ + private static $instances = array(); + + /** + * Array of items in the collection. + * + * @since 2.0.0 + * @access protected + * @var array + */ + protected $collection = array(); + + /** + * Constructor method. + * + * @since 2.0.0 + * @access protected + * @return void + */ + protected function __construct() {} + + /** + * Lock down `__clone()`. + * + * @since 2.0.0 + * @access private + * @return void + */ + private function __clone() {} + + /** + * Lock down `__wakeup()`. + * + * @since 2.0.0 + * @access private + * @return void + */ + private function __wakeup() {} + + /** + * Register an item. + * + * @since 2.0.0 + * @access public + * @param string $name + * @param mixed $value + * @return void + */ + public function register( $name, $value ) { + + if ( ! $this->exists( $name ) ) + $this->collection[ $name ] = $value; + } + + /** + * Unregisters an item. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return void + */ + public function unregister( $name ) { + + if ( $this->exists( $name ) ) + unset( $this->collection[ $name ] ); + } + + /** + * Checks if an item exists. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return bool + */ + public function exists( $name ) { + + return isset( $this->collection[ $name ] ); + } + + /** + * Returns an item. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return mixed + */ + public function get( $name ) { + + return $this->exists( $name ) ? $this->collection[ $name ] : false; + } + + /** + * Returns the entire collection. + * + * @since 2.0.0 + * @access public + * @return array + */ + public function get_collection() { + + return $this->collection; + } + + /** + * Returns the instance. + * + * @since 2.0.0 + * @access public + * @return object + */ + final public static function get_instance( $name = '' ) { + + if ( ! isset( self::$instances[ $name ] ) ) + self::$instances[ $name ] = new static(); + + return self::$instances[ $name ]; + } +} diff --git a/inc/class-role-factory.php b/inc/class-role-factory.php deleted file mode 100644 index 6757c3b..0000000 --- a/inc/class-role-factory.php +++ /dev/null @@ -1,165 +0,0 @@ - - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members - * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - */ - -/** - * Role factory class. - * - * @since 1.0.0 - * @access public - */ -final class Members_Role_Factory { - - /** - * Array of roles added. - * - * @since 1.0.0 - * @access public - * @var array - */ - public $roles = array(); - - /** - * Array of editable roles. - * - * @since 1.0.0 - * @access public - * @var array - */ - public $editable = array(); - - /** - * Array of uneditable roles. - * - * @since 1.0.0 - * @access public - * @var array - */ - public $uneditable = array(); - - /** - * Array of core WordPress roles. - * - * @since 1.0.0 - * @access public - * @var array - */ - public $wordpress = array(); - - /** - * Private constructor method to prevent a new instance of the object. - * - * @since 1.0.0 - * @access public - * @return void - */ - private function __construct() {} - - /** - * Adds a role object. - * - * @since 1.0.0 - * @access public - * @param string $role - * @return object - */ - public function add_role( $role ) { - - // If the role exists with WP but hasn't been added. - if ( members_role_exists( $role ) ) { - - // Get the role object. - $this->roles[ $role ] = new Members_Role( $role ); - - // Check if role is editable. - if ( $this->roles[ $role ]->is_editable ) - $this->editable[ $role ] = $this->roles[ $role ]; - else - $this->uneditable[ $role ] = $this->roles[ $role ]; - - // Is WP role? - if ( members_is_wordpress_role( $role ) ) - $this->wordpress[ $role ] = $this->roles[ $role ]; - } - } - - /** - * Returns a single role object. - * - * @since 1.0.0 - * @access public - * @param string $role - * @return object - */ - public function get_role( $role ) { - - return isset( $this->roles[ $role ] ) ? $this->roles[ $role ] : false; - } - - /** - * Removes a role object (doesn't remove from DB). - * - * @since 1.1.0 - * @access public - * @param string $role - * @return void - */ - public function remove_role( $role ) { - - if ( isset( $this->roles[ $role ] ) ) - unset( $this->roles[ $role ] ); - } - - /** - * Returns an array of role objects. - * - * @since 1.0.0 - * @access public - * @return array - */ - public function get_roles() { - return $this->roles; - } - - /** - * Adds all the WP roles as role objects. Rather than running this elsewhere, we're just - * going to call this directly within the class when it is first constructed. - * - * @since 1.0.0 - * @access public - * @return void - */ - protected function setup_roles() { - - foreach ( $GLOBALS['wp_roles']->role_names as $role => $name ) - $this->add_role( $role ); - } - - /** - * Returns the instance. - * - * @since 3.0.0 - * @access public - * @return object - */ - public static function get_instance() { - - static $instance = null; - - if ( is_null( $instance ) ) { - $instance = new Members_Role_Factory; - $instance->setup_roles(); - } - - return $instance; - } -} diff --git a/inc/class-role-group.php b/inc/class-role-group.php new file mode 100644 index 0000000..f46f889 --- /dev/null +++ b/inc/class-role-group.php @@ -0,0 +1,104 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +namespace Members; + +/** + * Role group object class. + * + * @since 2.0.0 + * @access public + */ +final class Role_Group { + + /** + * Name/ID for the group. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $name = ''; + + /** + * Internationalized text label for the group. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $label = ''; + + /** + * Internationalized text label for the group + the count in the form of + * `_n_noop( 'Singular Name %s', 'Plural Name %s', $textdomain )` + * + * @since 2.0.0 + * @access public + * @var string + */ + public $label_count = ''; + + /** + * Array of roles that belong to the group. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $roles = array(); + + /** + * Whether to create a view for the group on the Manage Roles screen. + * + * @since 2.0.0 + * @access public + * @var bool + */ + public $show_in_view_list = true; + + /** + * Magic method to use in case someone tries to output the object as a string. + * We'll just return the name. + * + * @since 2.0.0 + * @access public + * @return string + */ + public function __toString() { + return $this->name; + } + + /** + * Register a new object. + * + * @since 2.0.0 + * @access public + * @param string $name + * @param array $args + * @return void + */ + public function __construct( $name, $args = array() ) { + + foreach ( array_keys( get_object_vars( $this ) ) as $key ) { + + if ( isset( $args[ $key ] ) ) + $this->$key = $args[ $key ]; + } + + $this->name = sanitize_key( $name ); + + $registered_roles = array_keys( wp_list_filter( members_get_roles(), array( 'group' => $this->name ) ) ); + + $this->roles = array_unique( array_merge( $this->roles, $registered_roles ) ); + } +} diff --git a/inc/class-role.php b/inc/class-role.php index f689802..e1ace22 100644 --- a/inc/class-role.php +++ b/inc/class-role.php @@ -5,60 +5,54 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members; + /** * Role class. * - * @since 1.0.0 + * @since 2.0.0 * @access public */ -class Members_Role { - - /** - * The role/slug. - * - * @since 1.0.0 - * @access public - * @var string - */ - public $slug = ''; +class Role { /** * The role name. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ public $name = ''; /** - * Whether the role can be edited. + * The role label. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @var bool + * @var string */ - public $is_editable = false; + public $label = ''; /** - * Whether the role is a core WP role. + * The group the role belongs to. * - * @since 1.0.0 + * @see Members\Role_Group + * @since 2.0.0 * @access public - * @var bool + * @var string */ - public $is_wordpress_role = false; + public $group = ''; /** * Whether the role has caps (granted). * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var bool */ @@ -67,7 +61,7 @@ class Members_Role { /** * Capability count for the role. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var int */ @@ -76,16 +70,16 @@ class Members_Role { /** * Capability count for the role. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var int */ public $denied_cap_count = 0; /** - * Array of capabilities that the role has. + * Array of capabilities that the role has in the form of `array( $cap => $bool )`. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -94,7 +88,7 @@ class Members_Role { /** * Array of granted capabilities that the role has. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -103,7 +97,7 @@ class Members_Role { /** * Array of denied capabilities that the role has. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var array */ @@ -112,71 +106,73 @@ class Members_Role { /** * Return the role string in attempts to use the object as a string. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return string */ public function __toString() { - return $this->slug; + return $this->name; } /** * Creates a new role object. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @global object $wp_roles * @param string $role + * @param array $args * @return void */ - public function __construct( $role ) { - global $wp_roles; + public function __construct( $name, $args = array() ) { - // Get the WP role object. - $_role = get_role( $role ); + foreach ( array_keys( get_object_vars( $this ) ) as $key ) { - // Set the slug. - $this->slug = $_role->name; + if ( isset( $args[ $key ] ) ) + $this->$key = $args[ $key ]; + } - // Set the role name. - if ( isset( $wp_roles->role_names[ $role ] ) ) - $this->name = members_translate_role( $role ); + $this->name = members_sanitize_role( $name ); - // Check whether the role is editable. - $editable_roles = function_exists( 'get_editable_roles' ) ? get_editable_roles() : apply_filters( 'editable_roles', $wp_roles->roles ); - $this->is_editable = array_key_exists( $role, $editable_roles ); + if ( $this->caps ) { - // Loop through the role's caps. - foreach ( (array) $_role->capabilities as $cap => $grant ) { + // Validate cap values as booleans in case they are stored as strings. + $this->caps = array_map( 'members_validate_boolean', $this->caps ); - // Validate any boolean grant/denied in case they are stored as strings. - $grant = members_validate_boolean( $grant ); + // Get granted and denied caps. + $this->granted_caps = array_keys( $this->caps, true ); + $this->denied_caps = array_keys( $this->caps, false ); - // Add to all caps array. - $this->caps[ $cap ] = $grant; + // Remove user levels from granted/denied caps. + $this->granted_caps = members_remove_old_levels( $this->granted_caps ); + $this->denied_caps = members_remove_old_levels( $this->denied_caps ); - // If a granted cap. - if ( true === $grant ) - $this->granted_caps[] = $cap; + // Remove hidden caps from granted/denied caps. + $this->granted_caps = members_remove_hidden_caps( $this->granted_caps ); + $this->denied_caps = members_remove_hidden_caps( $this->denied_caps ); - // If a denied cap. - elseif ( false === $grant ) - $this->denied_caps[] = $cap; - } + // Set the cap count. + $this->granted_cap_count = count( $this->granted_caps ); + $this->denied_cap_count = count( $this->denied_caps ); - // Remove user levels from granted/denied caps. - $this->granted_caps = members_remove_old_levels( $this->granted_caps ); - $this->denied_caps = members_remove_old_levels( $this->denied_caps ); + // Check if we have caps. + $this->has_caps = 0 < $this->granted_cap_count; + } + } - // Remove hidden caps from granted/denied caps. - $this->granted_caps = members_remove_hidden_caps( $this->granted_caps ); - $this->denied_caps = members_remove_hidden_caps( $this->denied_caps ); + /** + * Magic method for getting media object properties. Let's keep from failing if a theme + * author attempts to access a property that doesn't exist. + * + * @since 2.0.2 + * @access public + * @param string $property + * @return mixed + */ + public function get( $property ) { - // Set the cap count. - $this->granted_cap_count = count( $this->granted_caps ); - $this->denied_cap_count = count( $this->denied_caps ); + if ( 'label' === $property ) + return members_translate_role( $this->name ); - // Check if we have caps. - $this->has_caps = 0 < $this->granted_cap_count; + return isset( $this->$property ) ? $this->$property : false; } } diff --git a/inc/class-widget-login.php b/inc/class-widget-login.php index 67c544f..30959bb 100644 --- a/inc/class-widget-login.php +++ b/inc/class-widget-login.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members; + /** * Login form widget class. * - * @since 0.1.0 + * @since 2.0.0 * @access public */ -class Members_Widget_Login extends WP_Widget { +class Widget_Login extends \WP_Widget { /** * Default arguments for the widget settings. * - * @since 1.0.3 + * @since 2.0.0 * @access public * @var array */ @@ -30,7 +32,7 @@ class Members_Widget_Login extends WP_Widget { /** * Set up the widget's unique name, ID, class, description, and other options. * - * @since 0.2.5 + * @since 2.0.0 * @access public * @return void */ @@ -54,11 +56,11 @@ public function __construct() { // Set up the defaults. $this->defaults = array( - 'title' => esc_attr__( 'Log In', 'members' ), - 'label_username' => esc_attr__( 'Username', 'members' ), - 'label_password' => esc_attr__( 'Password', 'members' ), - 'label_log_in' => esc_attr__( 'Log In', 'members' ), - 'label_remember' => esc_attr__('Remember Me', 'members' ), + 'title' => esc_attr__( 'Log In', 'members' ), + 'label_username' => esc_attr__( 'Username', 'members' ), + 'label_password' => esc_attr__( 'Password', 'members' ), + 'label_log_in' => esc_attr__( 'Log In', 'members' ), + 'label_remember' => esc_attr__( 'Remember Me', 'members' ), 'form_id' => 'loginform', 'id_username' => 'user_login', 'id_password' => 'user_pass', @@ -76,7 +78,7 @@ public function __construct() { /** * Outputs the widget based on the arguments input through the widget controls. * - * @since 0.1.0 + * @since 2.0.0 * @access public * @param array $sidebar * @param array $instance @@ -97,8 +99,8 @@ function widget( $sidebar, $instance ) { 'label_log_in' => esc_html( $instance['label_log_in'] ), 'id_username' => esc_attr( $instance['id_username'] ), 'id_password' => esc_attr( $instance['id_password'] ), - 'id_remember' => esc_attr( $instance['id_submit'] ), - 'id_submit' => esc_attr( $instance['remember'] ), + 'id_remember' => esc_attr( $instance['id_remember'] ), + 'id_submit' => esc_attr( $instance['id_submit'] ), 'value_username' => esc_attr( $instance['value_username'] ), 'remember' => ! empty( $instance['remember'] ) ? true : false, @@ -154,7 +156,7 @@ function widget( $sidebar, $instance ) { /** * Sanitizes/Validates widget options before being saved. * - * @since 0.1.0 + * @since 2.0.0 * @access public * @param array $new_instance * @param array $old_instance @@ -191,7 +193,7 @@ function update( $new_instance, $old_instance ) { /** * Displays the widget control options in the Widgets admin screen. * - * @since 0.1.0 + * @since 2.0.0 * @access public * @param array $instance * @return void @@ -255,13 +257,13 @@ function form( $instance ) {

    @@ -271,12 +273,12 @@ function form( $instance ) {

    - +

    - +

    diff --git a/inc/class-widget-users.php b/inc/class-widget-users.php index f90d6d5..24f1f28 100644 --- a/inc/class-widget-users.php +++ b/inc/class-widget-users.php @@ -4,24 +4,26 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +namespace Members; + /** * Users widget archive class. * - * @since 0.1.0 + * @since 2.0.0 * @access public */ -class Members_Widget_Users extends WP_Widget { +class Widget_Users extends \WP_Widget { /** * Default arguments for the widget settings. * - * @since 1.0.3 + * @since 2.0.0 * @access public * @var array */ @@ -30,7 +32,7 @@ class Members_Widget_Users extends WP_Widget { /** * Set up the widget's unique name, ID, class, description, and other options. * - * @since 0.2.5 + * @since 2.0.0 * @access public * @return void */ @@ -71,7 +73,7 @@ public function __construct() { /** * Outputs the widget based on the arguments input through the widget controls. * - * @since 0.1.0 + * @since 2.0.0 * @access public * @param array $sidebar * @param array $instance @@ -136,7 +138,7 @@ function widget( $sidebar, $instance ) { /** * Sanitizes/Validates widget options before being saved. * - * @since 0.1.0 + * @since 2.0.0 * @access public * @param array $new_instance * @param array $old_instance @@ -169,7 +171,7 @@ function update( $new_instance, $old_instance ) { /** * Displays the widget control options in the Widgets admin screen. * - * @since 0.1.0 + * @since 2.0.0 * @access public * @param array $instance * @return void @@ -197,9 +199,8 @@ function form( $instance ) { $meta_key = array_merge( array( '' ), (array) members_get_user_meta_keys() ); - $roles = members_get_role_names(); - asort( $roles ); - $roles = array_merge( array( '' => '' ), $roles ); ?> + $roles = members_get_roles(); + asort( $roles ); ?>
    @@ -226,8 +227,9 @@ function form( $instance ) {

    diff --git a/inc/functions-admin-bar.php b/inc/functions-admin-bar.php index f0769b7..00db921 100644 --- a/inc/functions-admin-bar.php +++ b/inc/functions-admin-bar.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ diff --git a/admin/functions-cap-groups.php b/inc/functions-cap-groups.php similarity index 52% rename from admin/functions-cap-groups.php rename to inc/functions-cap-groups.php index 28edb60..65f4d1d 100644 --- a/admin/functions-cap-groups.php +++ b/inc/functions-cap-groups.php @@ -4,118 +4,44 @@ * * @package Members * @subpackage Admin - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ # Registers default groups. -add_action( 'init', 'members_register_cap_groups', 15 ); +add_action( 'init', 'members_register_cap_groups', 95 ); +add_action( 'members_register_cap_groups', 'members_register_default_cap_groups', 5 ); /** - * Returns the instance of the `Members_Cap_Group_Factory` object. Use this function to access the object. + * Fires the cap group registration action hook. * - * @see Members_Cap_Group_Factory * @since 1.0.0 * @access public - * @return object - */ -function members_cap_group_factory() { - return Members_Cap_Group_Factory::get_instance(); -} - -/** - * Function for registering a cap group. - * - * @see Members_Cap_Group_Factory::register_group() - * @since 1.0.0 - * @access public - * @param string $name - * @param array $args - * @return void - */ -function members_register_cap_group( $name, $args = array() ) { - members_cap_group_factory()->register_group( $name, $args ); -} - -/** - * Unregisters a group. - * - * @see Members_Cap_Group_Factory::unregister_group() - * @since 1.0.0 - * @access public - * @param string $name * @return void */ -function members_unregister_cap_group( $name ) { - members_cap_group_factory()->unregister_group( $name ); -} - -/** - * Checks if a group exists. - * - * @see Members_Cap_Group_Factory::group_exists() - * @since 1.0.0 - * @access public - * @param string $name - * @return bool - */ -function members_cap_group_exists( $name ) { - return members_cap_group_factory()->group_exists( $name ); -} - -/** - * Returns an array of registered group objects. - * - * @see Members_Cap_Group_Factory::group - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_cap_groups() { - return members_cap_group_factory()->groups; -} +function members_register_cap_groups() { -/** - * Returns a group object if it exists. Otherwise, `FALSE`. - * - * @see Members_Cap_Group_Factory::get_group() - * @see Members_Cap_Group - * @since 1.0.0 - * @access public - * @param string $name - * @return object|bool - */ -function members_get_cap_group( $name ) { - return members_cap_group_factory()->get_group( $name ); + // Hook for registering cap groups. Plugins should always register on this hook. + do_action( 'members_register_cap_groups' ); } /** * Registers the default cap groups. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return void */ -function members_register_cap_groups() { - - // Register the all group. - members_register_cap_group( 'all', - array( - 'label' => esc_html__( 'All', 'members' ), - 'caps' => members_get_all_group_caps(), - 'icon' => 'dashicons-plus', - 'merge_added' => false - ) - ); +function members_register_default_cap_groups() { // Registers the general group. members_register_cap_group( 'general', array( - 'label' => esc_html__( 'General', 'members' ), - 'caps' => members_get_general_group_caps(), - 'icon' => 'dashicons-wordpress' + 'label' => esc_html__( 'General', 'members' ), + 'icon' => 'dashicons-wordpress', + 'priority' => 5 ) ); @@ -123,7 +49,7 @@ function members_register_cap_groups() { foreach ( get_post_types( array(), 'objects' ) as $type ) { // Skip revisions and nave menu items. - if ( in_array( $type->name, array( 'revision', 'nav_menu_item' ) ) ) + if ( in_array( $type->name, array( 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset' ) ) ) continue; // Get the caps for the post type. @@ -152,9 +78,10 @@ function members_register_cap_groups() { // Register the post type cap group. members_register_cap_group( "type-{$type->name}", array( - 'label' => $type->labels->name, - 'caps' => $has_caps, - 'icon' => $icon + 'label' => $type->labels->name, + 'caps' => $has_caps, + 'icon' => $icon, + 'priority' => 10 ) ); } @@ -165,34 +92,35 @@ function members_register_cap_groups() { 'label' => esc_html__( 'Taxonomies', 'members' ), 'caps' => members_get_taxonomy_group_caps(), 'icon' => 'dashicons-tag', - 'diff_added' => true + 'diff_added' => true, + 'priority' => 15 ) ); // Register the theme group. members_register_cap_group( 'theme', array( - 'label' => esc_html__( 'Appearance', 'members' ), - 'caps' => members_get_theme_group_caps(), - 'icon' => 'dashicons-admin-appearance' + 'label' => esc_html__( 'Appearance', 'members' ), + 'icon' => 'dashicons-admin-appearance', + 'priority' => 20 ) ); // Register the plugin group. members_register_cap_group( 'plugin', array( - 'label' => esc_html__( 'Plugins', 'members' ), - 'caps' => members_get_plugin_group_caps(), - 'icon' => 'dashicons-admin-plugins' + 'label' => esc_html__( 'Plugins', 'members' ), + 'icon' => 'dashicons-admin-plugins', + 'priority' => 25 ) ); // Register the user group. members_register_cap_group( 'user', array( - 'label' => esc_html__( 'Users', 'members' ), - 'caps' => members_get_user_group_caps(), - 'icon' => 'dashicons-admin-users' + 'label' => esc_html__( 'Users', 'members' ), + 'icon' => 'dashicons-admin-users', + 'priority' => 30 ) ); @@ -202,78 +130,87 @@ function members_register_cap_groups() { 'label' => esc_html__( 'Custom', 'members' ), 'caps' => members_get_capabilities(), 'icon' => 'dashicons-admin-generic', - 'diff_added' => true + 'diff_added' => true, + 'priority' => 995 ) ); +} - // Hook for registering cap groups. Plugins should always register on this hook. - do_action( 'members_register_cap_groups' ); - - // Check if the `all` group is registered. - if ( members_cap_group_exists( 'all' ) ) { - - // Set up an empty caps array and get the `all` group object. - $caps = array(); - $_group = members_get_cap_group( 'all' ); +/** + * Returns the instance of cap group registry. + * + * @since 2.0.0 + * @access public + * @return object + */ +function members_cap_group_registry() { - // Get the caps from every registered group. - foreach ( members_get_cap_groups() as $group ) - $caps = array_merge( $caps, $group->caps ); + return \Members\Registry::get_instance( 'cap_group' ); +} - // Sort the caps alphabetically. - asort( $caps ); +/** + * Function for registering a cap group. + * + * @since 1.0.0 + * @access public + * @param string $name + * @param array $args + * @return void + */ +function members_register_cap_group( $name, $args = array() ) { - // Assign all caps to the `all` group. - $_group->caps = array_unique( $caps ); - } + members_cap_group_registry()->register( $name, new \Members\Cap_Group( $name, $args ) ); +} - // Check if the `custom` group is registered and there's possibly other non-default groups. - if ( has_action( 'members_register_cap_groups' ) && members_cap_group_exists( 'custom' ) ) { +/** + * Unregisters a group. + * + * @since 1.0.0 + * @access public + * @param string $name + * @return void + */ +function members_unregister_cap_group( $name ) { - // Get the custom group object. - $custom = members_cap_group_factory()->groups[ 'custom' ]; + members_cap_group_registry()->unregister( $name ); +} - // Unset the custom group object. - unset( members_cap_group_factory()->groups[ 'custom' ] ); +/** + * Checks if a group exists. + * + * @since 1.0.0 + * @access public + * @param string $name + * @return bool + */ +function members_cap_group_exists( $name ) { - // Move the custom group object to the end. - members_cap_group_factory()->groups[ 'custom' ] = $custom; - } + return members_cap_group_registry()->exists( $name ); } /** - * Returns the caps for the all capability group. + * Returns an array of registered group objects. * * @since 1.0.0 * @access public * @return array */ -function members_get_all_group_caps() { +function members_get_cap_groups() { - return members_get_capabilities(); + return members_cap_group_registry()->get_collection(); } /** - * Returns the caps for the general capability group. + * Returns a group object if it exists. Otherwise, `FALSE`. * * @since 1.0.0 * @access public - * @return array + * @param string $name + * @return object|bool */ -function members_get_general_group_caps() { - - return array( - 'edit_dashboard', - 'edit_files', - 'export', - 'import', - 'manage_links', - 'manage_options', - 'moderate_comments', - 'read', - 'unfiltered_html', - 'update_core', - ); +function members_get_cap_group( $name ) { + + return members_cap_group_registry()->get( $name ); } /** @@ -311,6 +248,11 @@ function members_get_post_type_group_caps( $post_type = 'post' ) { if ( 'attachment' === $post_type ) $caps[] = 'unfiltered_upload'; + $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => "type-{$post_type}" ) ) ); + + if ( $registered_caps ) + array_merge( $caps, $registered_caps ); + // Make sure there are no duplicates and return. return array_unique( $caps ); } @@ -324,6 +266,16 @@ function members_get_post_type_group_caps( $post_type = 'post' ) { */ function members_get_taxonomy_group_caps() { + $do_not_add = array( + 'assign_categories', + 'edit_categories', + 'delete_categories', + 'assign_post_tags', + 'edit_post_tags', + 'delete_post_tags', + 'manage_post_tags' + ); + $taxi = get_taxonomies( array(), 'objects' ); $caps = array(); @@ -331,68 +283,12 @@ function members_get_taxonomy_group_caps() { foreach ( $taxi as $tax ) $caps = array_merge( $caps, array_values( (array) $tax->cap ) ); - return array_unique( $caps ); -} + $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'taxonomy' ) ) ); -/** - * Returns the caps for the theme capability group. - * - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_theme_group_caps() { - - return array( - 'delete_themes', - 'edit_theme_options', - 'edit_themes', - 'install_themes', - 'switch_themes', - 'update_themes', - ); -} + if ( $registered_caps ) + array_merge( $caps, $registered_caps ); -/** - * Returns the caps for the plugin capability group. - * - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_plugin_group_caps() { - - return array( - 'activate_plugins', - 'delete_plugins', - 'edit_plugins', - 'install_plugins', - 'update_plugins', - ); -} - -/** - * Returns the caps for the user capability group. - * - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_user_group_caps() { - - return array( - 'add_users', - 'create_roles', - 'create_users', - 'delete_roles', - 'delete_users', - 'edit_roles', - 'edit_users', - 'list_roles', - 'list_users', - 'promote_users', - 'remove_users', - ); + return array_diff( array_unique( $caps ), $do_not_add ); } /** @@ -404,5 +300,12 @@ function members_get_user_group_caps() { */ function members_get_custom_group_caps() { - return members_get_capabilities(); + $caps = members_get_capabilities(); + + $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'custom' ) ) ); + + if ( $registered_caps ) + array_merge( $caps, $registered_caps ); + + return array_unique( $caps ); } diff --git a/inc/functions-capabilities.php b/inc/functions-capabilities.php index 1d03da9..2c92f82 100644 --- a/inc/functions-capabilities.php +++ b/inc/functions-capabilities.php @@ -4,38 +4,245 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +# Register capabilities. +add_action( 'init', 'members_register_caps', 95 ); +add_action( 'members_register_caps', 'members_register_default_caps', 5 ); + # Disables the old user levels from capabilities array. add_filter( 'members_get_capabilities', 'members_remove_old_levels' ); add_filter( 'members_get_capabilities', 'members_remove_hidden_caps' ); /** - * Function for sanitizing a capability. + * Fires the action hook for registering capabilities. + * + * @since 2.0.0 + * @access public + * @return void + */ +function members_register_caps() { + + do_action( 'members_register_caps' ); + + // The following is a quick way to register capabilities that technically + // exist (i.e., caps that have been added to a role). These are caps that + // we don't know about because they haven't been registered. + + $role_caps = array_values( members_get_role_capabilities() ); + $unregistered = array_diff( $role_caps, array_keys( members_get_caps() ) ); + + foreach ( $unregistered as $cap ) + members_register_cap( $cap, array( 'label' => $cap ) ); + +} + +/** + * Registers all of our default caps. In particular, the plugin registers its own caps plus core + * WP's caps. + * + * @since 2.0.0 + * @access public + * @return void + */ +function members_register_default_caps() { + + $caps = array(); + + // General caps. + $caps['edit_dashboard'] = array( 'label' => __( 'Edit Dashboard', 'members' ), 'group' => 'general' ); + $caps['edit_files'] = array( 'label' => __( 'Edit Files', 'members' ), 'group' => 'general' ); + $caps['export'] = array( 'label' => __( 'Export', 'members' ), 'group' => 'general' ); + $caps['import'] = array( 'label' => __( 'Import', 'members' ), 'group' => 'general' ); + $caps['manage_links'] = array( 'label' => __( 'Manage Links', 'members' ), 'group' => 'general' ); + $caps['manage_options'] = array( 'label' => __( 'Manage Options', 'members' ), 'group' => 'general' ); + $caps['moderate_comments'] = array( 'label' => __( 'Moderate Comments', 'members' ), 'group' => 'general' ); + $caps['read'] = array( 'label' => __( 'Read', 'members' ), 'group' => 'general' ); + $caps['unfiltered_html'] = array( 'label' => __( 'Unfiltered HTML', 'members' ), 'group' => 'general' ); + $caps['update_core'] = array( 'label' => __( 'Update Core', 'members' ), 'group' => 'general' ); + + // Post caps. + $caps['delete_others_posts'] = array( 'label' => __( "Delete Others' Posts", 'members' ), 'group' => 'type-post' ); + $caps['delete_posts'] = array( 'label' => __( 'Delete Posts', 'members' ), 'group' => 'type-post' ); + $caps['delete_private_posts'] = array( 'label' => __( 'Delete Private Posts', 'members' ), 'group' => 'type-post' ); + $caps['delete_published_posts'] = array( 'label' => __( 'Delete Published Posts', 'members' ), 'group' => 'type-post' ); + $caps['edit_others_posts'] = array( 'label' => __( "Edit Others' Posts", 'members' ), 'group' => 'type-post' ); + $caps['edit_posts'] = array( 'label' => __( 'Edit Posts', 'members' ), 'group' => 'type-post' ); + $caps['edit_private_posts'] = array( 'label' => __( 'Edit Private Posts', 'members' ), 'group' => 'type-post' ); + $caps['edit_published_posts'] = array( 'label' => __( 'Edit Published Posts', 'members' ), 'group' => 'type-post' ); + $caps['publish_posts'] = array( 'label' => __( 'Publish Posts', 'members' ), 'group' => 'type-post' ); + $caps['read_private_posts'] = array( 'label' => __( 'Read Private Posts', 'members' ), 'group' => 'type-post' ); + + // Page caps. + $caps['delete_others_pages'] = array( 'label' => __( "Delete Others' Pages", 'members' ), 'group' => 'type-page' ); + $caps['delete_pages'] = array( 'label' => __( 'Delete Pages', 'members' ), 'group' => 'type-page' ); + $caps['delete_private_pages'] = array( 'label' => __( 'Delete Private Pages', 'members' ), 'group' => 'type-page' ); + $caps['delete_published_pages'] = array( 'label' => __( 'Delete Published Pages', 'members' ), 'group' => 'type-page' ); + $caps['edit_others_pages'] = array( 'label' => __( "Edit Others' Pages", 'members' ), 'group' => 'type-page' ); + $caps['edit_pages'] = array( 'label' => __( 'Edit Pages', 'members' ), 'group' => 'type-page' ); + $caps['edit_private_pages'] = array( 'label' => __( 'Edit Private Pages', 'members' ), 'group' => 'type-page' ); + $caps['edit_published_pages'] = array( 'label' => __( 'Edit Published Pages', 'members' ), 'group' => 'type-page' ); + $caps['publish_pages'] = array( 'label' => __( 'Publish Pages', 'members' ), 'group' => 'type-page' ); + $caps['read_private_pages'] = array( 'label' => __( 'Read Private Pages', 'members' ), 'group' => 'type-page' ); + + // Attachment caps. + $caps['upload_files'] = array( 'label' => __( 'Upload Files', 'members' ), 'group' => 'type-attachment' ); + + // Taxonomy caps. + $caps['manage_categories'] = array( 'label' => __( 'Manage Categories', 'members' ), 'group' => 'taxonomy' ); + + // Theme caps. + $caps['delete_themes'] = array( 'label' => __( 'Delete Themes', 'members' ), 'group' => 'theme' ); + $caps['edit_theme_options'] = array( 'label' => __( 'Edit Theme Options', 'members' ), 'group' => 'theme' ); + $caps['edit_themes'] = array( 'label' => __( 'Edit Themes', 'members' ), 'group' => 'theme' ); + $caps['install_themes'] = array( 'label' => __( 'Install Themes', 'members' ), 'group' => 'theme' ); + $caps['switch_themes'] = array( 'label' => __( 'Switch Themes', 'members' ), 'group' => 'theme' ); + $caps['update_themes'] = array( 'label' => __( 'Update Themes', 'members' ), 'group' => 'theme' ); + + // Plugin caps. + $caps['activate_plugins'] = array( 'label' => __( 'Activate Plugins', 'members' ), 'group' => 'plugin' ); + $caps['delete_plugins'] = array( 'label' => __( 'Delete Plugins', 'members' ), 'group' => 'plugin' ); + $caps['edit_plugins'] = array( 'label' => __( 'Edit Plugins', 'members' ), 'group' => 'plugin' ); + $caps['install_plugins'] = array( 'label' => __( 'Install Plugins', 'members' ), 'group' => 'plugin' ); + $caps['update_plugins'] = array( 'label' => __( 'Update Plugins', 'members' ), 'group' => 'plugin' ); + + // User caps. + $caps['create_roles'] = array( 'label' => __( 'Create Roles', 'members' ), 'group' => 'user' ); + $caps['create_users'] = array( 'label' => __( 'Create Users', 'members' ), 'group' => 'user' ); + $caps['delete_roles'] = array( 'label' => __( 'Delete Roles', 'members' ), 'group' => 'user' ); + $caps['delete_users'] = array( 'label' => __( 'Delete Users', 'members' ), 'group' => 'user' ); + $caps['edit_roles'] = array( 'label' => __( 'Edit Roles', 'members' ), 'group' => 'user' ); + $caps['edit_users'] = array( 'label' => __( 'Edit Users', 'members' ), 'group' => 'user' ); + $caps['list_roles'] = array( 'label' => __( 'List Roles', 'members' ), 'group' => 'user' ); + $caps['list_users'] = array( 'label' => __( 'List Users', 'members' ), 'group' => 'user' ); + $caps['promote_users'] = array( 'label' => __( 'Promote Users', 'members' ), 'group' => 'user' ); + $caps['remove_users'] = array( 'label' => __( 'Remove Users', 'members' ), 'group' => 'user' ); + + // Custom caps. + $caps['restrict_content'] = array( 'label' => __( 'Restrict Content', 'members' ), 'group' => 'custom' ); + + // Register each of the capabilities. + foreach ( $caps as $name => $args ) + members_register_cap( $name, $args ); + + // === Category and Tag caps. === + // These are mapped to `manage_categories` in a default WP install. However, it's possible + // for another plugin to map these differently and handle them correctly. So, we're only + // going to register the caps if they've been assigned to a role. There's no other way + // to reliably detect if they've been mapped. + + $role_caps = array_values( members_get_role_capabilities() ); + $tax_caps = array(); + + $tax_caps['assign_categories'] = array( 'label' => __( 'Assign Categories', 'members' ), 'group' => 'taxonomy' ); + $tax_caps['edit_categories'] = array( 'label' => __( 'Edit Categories', 'members' ), 'group' => 'taxonomy' ); + $tax_caps['delete_categories'] = array( 'label' => __( 'Delete Categories', 'members' ), 'group' => 'taxonomy' ); + $tax_caps['assign_post_tags'] = array( 'label' => __( 'Assign Post Tags', 'members' ), 'group' => 'taxonomy' ); + $tax_caps['edit_post_tags'] = array( 'label' => __( 'Edit Post Tags', 'members' ), 'group' => 'taxonomy' ); + $tax_caps['delete_post_tags'] = array( 'label' => __( 'Delete Post Tags', 'members' ), 'group' => 'taxonomy' ); + $tax_caps['manage_post_tags'] = array( 'label' => __( 'Manage Post Tags', 'members' ), 'group' => 'taxonomy' ); + + foreach ( $tax_caps as $tax_cap => $args ) { + + if ( in_array( $tax_cap, $role_caps ) ) + members_register_cap( $tax_cap, $args ); + } +} + +/** + * Returns the instance of the capability registry. + * + * @since 2.0.0 + * @access public + * @return object + */ +function members_capability_registry() { + + return \Members\Registry::get_instance( 'cap' ); +} + +/** + * Returns all registered caps. + * + * @since 2.0.0 + * @access public + * @return array + */ +function members_get_caps() { + + return members_capability_registry()->get_collection(); +} + +/** + * Registers a capability. + * + * @since 2.0.0 + * @access public + * @param string $name + * @param array $args + * @return void + */ +function members_register_cap( $name, $args = array() ) { + + members_capability_registry()->register( $name, new \Members\Capability( $name, $args ) ); +} + +/** + * Unregisters a capability. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return void + */ +function members_unregister_cap( $name ) { + + members_capability_registry()->unregister( $name ); +} + +/** + * Returns a capability object. + * + * @since 2.0.0 + * @access public + * @param string $name + * @return object + */ +function members_get_cap( $name ) { + + return members_capability_registry()->get( $name ); +} + +/** + * Checks if a capability object exists. + * + * @note In 2.0.0, the function was changed to only check from registered caps. * * @since 1.0.0 * @access public - * @param string $cap - * @return string + * @param string $name + * @return bool */ -function members_sanitize_cap( $cap ) { - return apply_filters( 'members_sanitize_cap', sanitize_key( $cap ) ); +function members_cap_exists( $name ) { + + return members_capability_registry()->exists( $name ); } /** - * Conditional tag for checking whether a capability exists. + * Function for sanitizing a capability. * * @since 1.0.0 * @access public * @param string $cap - * @return bool + * @return string */ -function members_cap_exists( $cap ) { - return in_array( $cap, members_get_capabilities() ); +function members_sanitize_cap( $cap ) { + + return apply_filters( 'members_sanitize_cap', sanitize_key( $cap ) ); } /** @@ -49,7 +256,7 @@ function members_cap_exists( $cap ) { */ function members_is_cap_editable( $cap ) { - $uneditable = array_keys( members_get_uneditable_role_names() ); + $uneditable = array_keys( members_get_uneditable_roles() ); return ! in_array( $cap, members_get_wp_capabilities() ) && ! array_intersect( $uneditable, members_get_cap_roles( $cap ) ); } @@ -88,15 +295,8 @@ function members_get_cap_roles( $cap ) { */ function members_get_capabilities() { - // Merge the default WP, role, and plugin caps together. - $capabilities = array_merge( - members_get_wp_capabilities(), - members_get_role_capabilities(), - members_get_plugin_capabilities() - ); - // Apply filters to the array of capabilities. - $capabilities = apply_filters( 'members_get_capabilities', $capabilities ); + $capabilities = apply_filters( 'members_get_capabilities', array_keys( members_get_caps() ) ); // Sort the capabilities alphabetically. sort( $capabilities ); @@ -139,97 +339,6 @@ function members_get_role_capabilities() { return array_unique( $capabilities ); } -/** - * Additional capabilities provided by the Members plugin that gives users permissions to handle - * certain features of the plugin. - * - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_plugin_capabilities() { - - return array( - 'list_roles', // View roles list. - 'create_roles', // Create new roles. - 'delete_roles', // Delete roles. - 'edit_roles', // Edit a role's caps. - 'restrict_content' // Restrict content (content permissions component). - ); -} - -/** - * Make sure we keep the default capabilities in case users screw 'em up. A user could easily - * remove a useful WordPress capability from all roles. When this happens, the capability is no - * longer stored in any of the roles, so it basically doesn't exist. This function will house - * all of the default WordPress capabilities in case this scenario comes into play. - * - * For those reading this note, yes, I did "accidentally" remove all capabilities from my - * administrator account when developing this plugin. And yes, that was fun putting back - * together. ;) - * - * @link http://codex.wordpress.org/Roles_and_Capabilities#Capabilities - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_wp_capabilities() { - - return array( - 'activate_plugins', - 'add_users', - 'create_users', - 'delete_others_pages', - 'delete_others_posts', - 'delete_pages', - 'delete_plugins', - 'delete_posts', - 'delete_private_pages', - 'delete_private_posts', - 'delete_published_pages', - 'delete_published_posts', - 'delete_themes', - 'delete_users', - 'edit_dashboard', - 'edit_files', - 'edit_others_pages', - 'edit_others_posts', - 'edit_pages', - 'edit_plugins', - 'edit_posts', - 'edit_private_pages', - 'edit_private_posts', - 'edit_published_pages', - 'edit_published_posts', - 'edit_theme_options', - 'edit_themes', - 'edit_users', - 'export', - 'import', - 'install_plugins', - 'install_themes', - 'list_users', - 'manage_categories', - 'manage_links', - 'manage_options', - 'moderate_comments', - 'promote_users', - 'publish_pages', - 'publish_posts', - 'read', - 'read_private_pages', - 'read_private_posts', - 'remove_users', - 'switch_themes', - 'unfiltered_html', - 'unfiltered_upload', - 'update_core', - 'update_plugins', - 'update_themes', - 'upload_files' - ); -} - /** * Checks if a specific capability has been given to at least one role. If it has, return true. * Else, return false. @@ -260,6 +369,26 @@ function members_get_hidden_caps() { $caps = array(); + // This is always a hidden cap and should never be added to the caps list. + $caps[] = 'do_not_allow'; + + // Network-level caps. + // These shouldn't show on single-site installs anyway. + // On multisite installs, they should be handled by a network-specific role manager. + $caps[] = 'create_sites'; + $caps[] = 'delete_sites'; + $caps[] = 'manage_network'; + $caps[] = 'manage_sites'; + $caps[] = 'manage_network_users'; + $caps[] = 'manage_network_plugins'; + $caps[] = 'manage_network_themes'; + $caps[] = 'manage_network_options'; + $caps[] = 'upgrade_network'; + + // This cap is needed on single site to set up a multisite network. + if ( is_multisite() ) + $caps[] = 'setup_network'; + // Unfiltered uploads. if ( is_multisite() || ! defined( 'ALLOW_UNFILTERED_UPLOADS' ) || ! ALLOW_UNFILTERED_UPLOADS ) $caps[] = 'unfiltered_upload'; @@ -303,6 +432,7 @@ function members_get_hidden_caps() { * @return array */ function members_remove_hidden_caps( $caps ) { + return apply_filters( 'members_remove_hidden_caps', true ) ? array_diff( $caps, members_get_hidden_caps() ) : $caps; } @@ -342,6 +472,7 @@ function members_get_old_levels() { * @return array */ function members_remove_old_levels( $caps ) { + return apply_filters( 'members_remove_old_levels', true ) ? array_diff( $caps, members_get_old_levels() ) : $caps; } diff --git a/inc/functions-content-permissions.php b/inc/functions-content-permissions.php index 13d5f43..6fb3739 100644 --- a/inc/functions-content-permissions.php +++ b/inc/functions-content-permissions.php @@ -10,6 +10,20 @@ # Enable the content permissions features. add_action( 'after_setup_theme', 'members_enable_content_permissions', 0 ); +/** + * Conditional check to determine if a post any permissions rules assigned + * to it. + * + * @since 2.0.0 + * @access public + * @param $post_id + * @return bool + */ +function members_has_post_permissions( $post_id = '' ) { + + return members_has_post_roles( $post_id ); +} + /** * Returns an array of the roles for a given post. * @@ -22,6 +36,24 @@ function members_get_post_roles( $post_id ) { return get_post_meta( $post_id, '_members_access_role', false ); } +/** + * Conditional check to determine if a post has roles assigned to it. + * + * @since 2.0.0 + * @access public + * @param int $post_id + * @return bool + */ +function members_has_post_roles( $post_id = '' ) { + + if ( ! $post_id ) + $post_id = get_the_ID(); + + $roles = members_get_post_roles( $post_id ); + + return ! empty( $roles ); +} + /** * Adds a single role to a post's access roles. * @@ -32,6 +64,7 @@ function members_get_post_roles( $post_id ) { * @return int|false */ function members_add_post_role( $post_id, $role ) { + return add_post_meta( $post_id, '_members_access_role', $role, false ); } @@ -45,6 +78,7 @@ function members_add_post_role( $post_id, $role ) { * @return bool */ function members_remove_post_role( $post_id, $role ) { + return delete_post_meta( $post_id, '_members_access_role', $role ); } @@ -90,6 +124,7 @@ function members_set_post_roles( $post_id, $roles ) { * @return bool */ function members_delete_post_roles( $post_id ) { + return delete_post_meta( $post_id, '_members_access_role' ); } @@ -112,7 +147,7 @@ function members_enable_content_permissions() { add_filter( 'get_the_excerpt', 'members_content_permissions_protect', 95 ); add_filter( 'the_excerpt', 'members_content_permissions_protect', 95 ); add_filter( 'the_content_feed', 'members_content_permissions_protect', 95 ); - add_filter( 'comment_text_rss', 'members_content_permissions_protect', 95 ); + add_filter( 'get_comment_text', 'members_content_permissions_protect', 95 ); // Filter the comments template to make sure comments aren't shown to users without access. add_filter( 'comments_template', 'members_content_permissions_comments', 95 ); @@ -162,7 +197,7 @@ function members_content_permissions_comments( $template ) { $has_template = locate_template( array( 'comments-no-access.php' ) ); // If the template was found, use it. Otherwise, fall back to the Members comments.php template. - $template = $has_template ? $has_template : members_plugin()->templates_dir . 'comments.php'; + $template = $has_template ? $has_template : members_plugin()->dir . 'templates/comments.php'; // Allow devs to overwrite the comments template. $template = apply_filters( 'members_comments_template', $template ); @@ -204,6 +239,7 @@ function members_get_post_error_message( $post_id ) { * @return string */ function members_get_post_access_message( $post_id ) { + return get_post_meta( $post_id, '_members_access_error', true ); } @@ -217,6 +253,7 @@ function members_get_post_access_message( $post_id ) { * @return bool */ function members_set_post_access_message( $post_id, $message ) { + return update_post_meta( $post_id, '_members_access_error', $message ); } @@ -229,6 +266,7 @@ function members_set_post_access_message( $post_id, $message ) { * @return bool */ function members_delete_post_access_message( $post_id ) { + return delete_post_meta( $post_id, '_members_access_error' ); } diff --git a/inc/functions-deprecated.php b/inc/functions-deprecated.php index 586af5b..afaf047 100644 --- a/inc/functions-deprecated.php +++ b/inc/functions-deprecated.php @@ -4,29 +4,397 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +/** + * Returns an array of role names. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_role_names() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + $roles = array(); + + foreach ( members_get_roles() as $role ) + $roles[ $role->name ] = $role->name; + + return $roles; +} + +/** + * Returns an array of the role names of roles that have users. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_active_role_names() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + $has_users = array(); + + foreach ( members_get_active_roles() as $role ) + $has_users[ $role ] = members_get_role( $role )->get( 'label' ); + + return $has_users; +} + +/** + * Returns an array of the role names of roles that do not have users. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_inactive_role_names() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return array_diff( members_get_role_names(), members_get_active_role_names() ); +} + +/** + * Returns an array of editable role names. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_editable_role_names() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + $editable = array(); + + foreach ( members_role_registry()->editable as $role ) + $editable[ $role->slug ] = $role->name; + + return $editable; +} + +/** + * Returns an array of editable roles. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_editable_role_slugs() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return array_keys( members_role_registry()->editable ); +} + +/** + * Returns an array of uneditable role names. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_uneditable_role_names() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + $uneditable = array(); + + foreach ( members_role_registry()->uneditable as $role ) + $uneditable[ $role->slug ] = $role->name; + + return $uneditable; +} + +/** + * Returns an array of uneditable roles. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_uneditable_role_slugs() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return array_keys( members_role_registry()->uneditable ); +} + +/** + * Returns an array of core WordPress role names. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_wordpress_role_names() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + $names = array(); + + foreach ( members_role_registry()->wordpress as $role ) + $names[ $role->slug ] = $role->name; + + return $names; +} + +/** + * Returns an array of core WP roles. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_wordpress_role_slugs() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return array_keys( members_role_registry()->wordpress ); +} + +/** + * Returns the human-readable role name. + * + * @since 1.0.0 + * @access public + * @param string $role + * @return string + */ +function members_get_role_name( $role ) { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return members_role_registry()->get( $role )->name; +} + +/** + * Returns an array of roles. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_role_slugs() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return array_keys( members_get_roles() ); +} + +/** + * Returns an array of the roles that have users. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_active_role_slugs() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + $has_users = array(); + + foreach ( members_get_role_user_count() as $role => $count ) { + + if ( 0 < $count ) + $has_users[] = $role; + } + + return $has_users; +} + +/** + * Returns an array of the roles that have no users. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_inactive_role_slugs() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return array_diff( array_keys( members_get_roles() ), members_get_active_roles() ); +} + +/** + * Returns the caps for the all capability group. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_all_group_caps() { + _deprecated_function( __FUNCTION__, '2.0.0', '' ); + + return members_get_capabilities(); +} + +/** + * Returns the caps for the general capability group. + * + * @since 1.0.0 + * @deprecated 2.0.0 + * @access public + * @return array + */ +function members_get_general_group_caps() { + + return array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'general' ) ) ); +} + +/** + * Returns the caps for the theme capability group. + * + * @since 1.0.0 + * @deprecated 2.0.0 + * @access public + * @return array + */ +function members_get_theme_group_caps() { + + return array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'theme' ) ) ); +} + +/** + * Returns the caps for the plugin capability group. + * + * @since 1.0.0 + * @deprecated 2.0.0 + * @access public + * @return array + */ +function members_get_plugin_group_caps() { + + return array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'plugin' ) ) ); +} + +/** + * Returns the caps for the user capability group. + * + * @since 1.0.0 + * @deprecated 2.0.0 + * @access public + * @return array + */ +function members_get_user_group_caps() { + + return array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'user' ) ) ); +} + +/** + * Additional capabilities provided by the Members plugin that gives users permissions to handle + * certain features of the plugin. + * + * @since 1.0.0 + * @deprecated 2.0.0 + * @access public + * @return array + */ +function members_get_plugin_capabilities() { + + return array( + 'list_roles', // View roles list. + 'create_roles', // Create new roles. + 'delete_roles', // Delete roles. + 'edit_roles', // Edit a role's caps. + 'restrict_content' // Restrict content (content permissions component). + ); +} + +/** + * Make sure we keep the default capabilities in case users screw 'em up. A user could easily + * remove a useful WordPress capability from all roles. When this happens, the capability is no + * longer stored in any of the roles, so it basically doesn't exist. This function will house + * all of the default WordPress capabilities in case this scenario comes into play. + * + * For those reading this note, yes, I did "accidentally" remove all capabilities from my + * administrator account when developing this plugin. And yes, that was fun putting back + * together. ;) + * + * @link http://codex.wordpress.org/Roles_and_Capabilities#Capabilities + * @since 1.0.0 + * @deprecated 2.0.0 + * @access public + * @return array + */ +function members_get_wp_capabilities() { + + return array( + 'activate_plugins', + 'add_users', + 'assign_categories', + 'assign_post_tags', + 'create_users', + 'delete_categories', + 'delete_others_pages', + 'delete_others_posts', + 'delete_pages', + 'delete_plugins', + 'delete_posts', + 'delete_post_tags', + 'delete_private_pages', + 'delete_private_posts', + 'delete_published_pages', + 'delete_published_posts', + 'delete_themes', + 'delete_users', + 'edit_categories', + 'edit_dashboard', + 'edit_files', + 'edit_others_pages', + 'edit_others_posts', + 'edit_pages', + 'edit_plugins', + 'edit_posts', + 'edit_post_tags', + 'edit_private_pages', + 'edit_private_posts', + 'edit_published_pages', + 'edit_published_posts', + 'edit_theme_options', + 'edit_themes', + 'edit_users', + 'export', + 'import', + 'install_plugins', + 'install_themes', + 'list_users', + 'manage_categories', + 'manage_links', + 'manage_options', + 'manage_post_tags', + 'moderate_comments', + 'promote_users', + 'publish_pages', + 'publish_posts', + 'read', + 'read_private_pages', + 'read_private_posts', + 'remove_users', + 'switch_themes', + 'unfiltered_html', + 'unfiltered_upload', + 'update_core', + 'update_plugins', + 'update_themes', + 'upload_files' + ); +} + /** * @since 0.2.0 * @deprecated 1.0.0 */ -function members_get_active_roles() { - _deprecated_function( __FUNCTION__, '1.0.0', 'members_get_active_role_names' ); - return members_get_active_role_names(); -} +//function members_get_active_roles() { +// _deprecated_function( __FUNCTION__, '1.0.0', 'members_get_active_role_names' ); +// return members_get_active_role_names(); +//} /** * @since 0.2.0 * @deprecated 1.0.0 */ -function members_get_inactive_roles() { - _deprecated_function( __FUNCTION__, '1.0.0', 'members_get_inactive_role_names' ); - return members_get_inactive_role_names(); -} +//function members_get_inactive_roles() { +// _deprecated_function( __FUNCTION__, '1.0.0', 'members_get_inactive_role_names' ); +// return members_get_inactive_role_names(); +//} /** * @since 0.2.0 @@ -55,6 +423,14 @@ function members_get_additional_capabilities() { return members_get_plugin_capabilities(); } +/* ====== Functions removed in the 2.0 branch. ====== */ + +function members_role_factory() {} +function members_role_group_factory() {} +function members_cap_group_factory() {} +function members_manage_users_columns() {} +function members_manage_users_custom_column() {} + /* ====== Functions removed in the 1.0 branch. ====== */ if ( ! function_exists( 'has_role' ) ) { function has_role() {} } diff --git a/inc/functions-options.php b/inc/functions-options.php index 16ef8f1..4a29086 100644 --- a/inc/functions-options.php +++ b/inc/functions-options.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -18,6 +18,7 @@ * @return bool */ function members_role_manager_enabled() { + return apply_filters( 'members_role_manager_enabled', members_get_setting( 'role_manager' ) ); } @@ -30,9 +31,22 @@ function members_role_manager_enabled() { * @return bool */ function members_explicitly_deny_caps() { + return apply_filters( 'members_explicitly_deny_caps', members_get_setting( 'explicit_denied_caps' ) ); } +/** + * Whether to show human-readable caps. + * + * @since 2.0.0 + * @access public + * @return bool + */ +function members_show_human_caps() { + + return apply_filters( 'members_show_human_caps', members_get_setting( 'show_human_caps' ) ); +} + /** * Conditional check to see if the role manager is enabled. * @@ -41,6 +55,7 @@ function members_explicitly_deny_caps() { * @return bool */ function members_multiple_user_roles_enabled() { + return apply_filters( 'members_multiple_roles_enabled', members_get_setting( 'multi_roles' ) ); } @@ -52,6 +67,7 @@ function members_multiple_user_roles_enabled() { * @return bool */ function members_content_permissions_enabled() { + return apply_filters( 'members_content_permissions_enabled', members_get_setting( 'content_permissions' ) ); } @@ -63,6 +79,7 @@ function members_content_permissions_enabled() { * @return bool */ function members_login_widget_enabled() { + return apply_filters( 'members_login_widget_enabled', members_get_setting( 'login_form_widget' ) ); } @@ -74,6 +91,7 @@ function members_login_widget_enabled() { * @return bool */ function members_users_widget_enabled() { + return apply_filters( 'members_users_widget_enabled', members_get_setting( 'users_widget' ) ); } @@ -119,5 +137,9 @@ function members_get_default_settings() { // @since 1.0.0 'explicit_denied_caps' => true, 'multi_roles' => true, + + // @since 2.0.0 + 'show_human_caps' => true, + 'private_rest_api' => false, ); } diff --git a/inc/functions-private-site.php b/inc/functions-private-site.php index 14fe3fc..3380859 100644 --- a/inc/functions-private-site.php +++ b/inc/functions-private-site.php @@ -6,9 +6,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -30,6 +30,9 @@ add_filter( 'members_feed_error_message', 'do_shortcode', 30 ); add_filter( 'members_feed_error_message', 'shortcode_unautop', 35 ); +# Authenticate when accessing the REST API. +add_filter( 'rest_authentication_errors', 'members_private_rest_api', 95 ); + /** * Conditional tag to see if we have a private blog. * @@ -38,7 +41,8 @@ * @return bool */ function members_is_private_blog() { - return members_get_setting( 'private_blog' ); + + return apply_filters( 'members_is_private_blog', members_get_setting( 'private_blog' ) ); } /** @@ -49,7 +53,20 @@ function members_is_private_blog() { * @return bool */ function members_is_private_feed() { - return members_get_setting( 'private_feed' ); + + return apply_filters( 'members_is_private_feed', members_get_setting( 'private_feed' ) ); +} + +/** + * Conditional tag to see if we have a private REST API + * + * @since 2.0.0 + * @access public + * @return bool + */ +function members_is_private_rest_api() { + + return apply_filters( 'members_is_private_rest_api', members_get_setting( 'private_rest_api' ) ); } /** @@ -61,19 +78,49 @@ function members_is_private_feed() { */ function members_please_log_in() { - // Check if the private blog feature is active and if the user is not logged in. - if ( members_is_private_blog() && ! is_user_logged_in() ) { + // If private blog is not enabled, bail. + if ( ! members_is_private_blog() ) + return; + + // If this is a multisite instance and the user is logged into the network. + if ( is_multisite() && is_user_logged_in() && ! is_user_member_of_blog() && ! is_super_admin() ) { + members_ms_private_blog_die(); + } - // If using BuddyPress and on the register/activate page, don't do anything. - if ( function_exists( 'bp_is_current_component' ) && ( bp_is_current_component( 'register' ) || bp_is_current_component( 'activate' ) ) ) - return; + // Check if the private blog feature is active and if the user is not logged in. + if ( ! is_user_logged_in() && members_is_private_page() ) { - // Redirect to the login page. auth_redirect(); exit; } } +/** + * Function for determining whether a page should be public even though we're in private + * site mode. Plugin devs can filter this to make specific pages public. + * + * @since 2.0.0 + * @access public + * @return bool + */ +function members_is_private_page() { + + $is_private = true; + + if ( function_exists( 'bp_is_current_component' ) && ( bp_is_current_component( 'register' ) || bp_is_current_component( 'activate' ) ) ) + $is_private = false; + + // WooCommerce support. + if ( class_exists( 'WooCommerce' ) ) { + $page_id = get_option( 'woocommerce_myaccount_page_id' ); + + if ( $page_id && is_page( $page_id ) ) + $is_private = false; + } + + return apply_filters( 'members_is_private_page', $is_private ); +} + /** * Blocks feed items if the user has selected the private feed feature. * @@ -98,3 +145,64 @@ function members_get_private_feed_message() { return apply_filters( 'members_feed_error_message', members_get_setting( 'private_feed_error' ) ); } + +/** + * Returns an error if the REST API is accessed by an unauthenticated user. + * + * @link https://developer.wordpress.org/rest-api/using-the-rest-api/frequently-asked-questions/#require-authentication-for-all-requests + * @since 2.0.0 + * @access public + * @param object $result + * @return object + */ +function members_private_rest_api( $result ) { + + if ( empty( $result ) && members_is_private_rest_api() && ! is_user_logged_in() ) { + + return new WP_Error( + 'rest_not_logged_in', + esc_html( + apply_filters( + 'members_rest_api_error_message', + __( 'You are not currently logged in.', 'members' ) + ) + ), + array( 'status' => 401 ) + ); + } + + return $result; +} + +/** + * Outputs an error message if a user attempts to access a site that they do not have + * access to on multisite. + * + * @since 2.0.0 + * @access public + * @return void + */ +function members_ms_private_blog_die() { + + $blogs = get_blogs_of_user( get_current_user_id() ); + + $blogname = get_bloginfo( 'name' ); + + $message = __( 'You do not currently have access to the "%s" site. If you believe you should have access, please contact your network administrator.', 'members' ); + + if ( empty( $blogs ) ) + wp_die( sprintf( $message, $blogname ), 403 ); + + $output = '

    ' . sprintf( $message, $blogname ) . '

    '; + + $output .= sprintf( '

    %s

    ', __( 'If you reached this page by accident and meant to visit one of your own sites, try one of the following links.', 'members' ) ); + + $output .= '
      '; + + foreach ( $blogs as $blog ) + $output .= sprintf( '
    • %s
    • ', esc_url( get_home_url( $blog->userblog_id ) ), esc_html( $blog->blogname ) ); + + $output .= '
    '; + + wp_die( $output, 403 ); +} diff --git a/inc/functions-role-groups.php b/inc/functions-role-groups.php new file mode 100644 index 0000000..24c47f7 --- /dev/null +++ b/inc/functions-role-groups.php @@ -0,0 +1,124 @@ + + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +# Registers default groups. +add_action( 'init', 'members_register_role_groups', 95 ); +add_action( 'members_register_role_groups', 'members_register_default_role_groups', 5 ); + +/** + * Fires the role group registration action hook. + * + * @since 1.0.0 + * @access public + * @return void + */ +function members_register_role_groups() { + + do_action( 'members_register_role_groups' ); +} + + +/** + * Registers the default role groups. + * + * @since 2.0.0 + * @access public + * @return void + */ +function members_register_default_role_groups() { + + // Register the WordPress group. + members_register_role_group( 'wordpress', + array( + 'label' => esc_html__( 'WordPress', 'members' ), + 'label_count' => _n_noop( 'WordPress %s', 'WordPress %s', 'members' ), + 'roles' => members_get_wordpress_roles(), + ) + ); +} + +/** + * Returns the instance of the role group registry. + * + * @since 2.0.0 + * @access public + * @return object + */ +function members_role_group_registry() { + + return \Members\Registry::get_instance( 'role_group' ); +} + +/** + * Function for registering a role group. + * + * @since 1.0.0 + * @access public + * @param string $name + * @param array $args + * @return void + */ +function members_register_role_group( $name, $args = array() ) { + + members_role_group_registry()->register( $name, new \Members\Role_Group( $name, $args ) ); +} + +/** + * Unregisters a group. + * + * @since 1.0.0 + * @access public + * @param string $name + * @return void + */ +function members_unregister_role_group( $name ) { + + members_role_group_registry()->unregister( $name ); +} + +/** + * Checks if a group exists. + * + * @since 1.0.0 + * @access public + * @param string $name + * @return bool + */ +function members_role_group_exists( $name ) { + + return members_role_group_registry()->exists( $name ); +} + +/** + * Returns an array of registered group objects. + * + * @since 1.0.0 + * @access public + * @return array + */ +function members_get_role_groups() { + + return members_role_group_registry()->get_collection(); +} + +/** + * Returns a group object if it exists. Otherwise, `FALSE`. + * + * @since 1.0.0 + * @access public + * @param string $name + * @return object|bool + */ +function members_get_role_group( $name ) { + + return members_role_group_registry()->get( $name ); +} diff --git a/inc/functions-roles.php b/inc/functions-roles.php index eb2d4fb..01a0596 100644 --- a/inc/functions-roles.php +++ b/inc/functions-roles.php @@ -4,241 +4,224 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ -/** - * Returns the instance of the `Members_Role_Factory`. - * - * @since 1.0.0 - * @access public - * @param string - * @return bool - */ -function members_role_factory() { - return Members_Role_Factory::get_instance(); -} - -/* ====== Multiple Role Functions ====== */ +# Register roles. +add_action( 'wp_roles_init', 'members_register_roles', 95 ); +add_action( 'members_register_roles', 'members_register_default_roles', 5 ); /** - * Returns a count of all the available roles for the site. + * Fires the role registration action hook. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return int + * @param object $wp_roles + * @return void */ -function members_get_role_count() { - return count( $GLOBALS['wp_roles']->role_names ); -} +function members_register_roles( $wp_roles ) { -/** - * Returns an array of `Members_Role` objects. - * - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_roles() { - return members_role_factory()->roles; + do_action( 'members_register_roles', $wp_roles ); } /** - * Returns an array of role names. + * Registers any roles stored globally with WordPress. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return array + * @param object $wp_roles + * @return void */ -function members_get_role_names() { - $roles = array(); +function members_register_default_roles( $wp_roles ) { - foreach ( members_role_factory()->roles as $role ) - $roles[ $role->slug ] = $role->name; + foreach ( $wp_roles->roles as $name => $object ) { - return $roles; -} + $args = array( + 'label' => $object['name'], + 'caps' => $object['capabilities'] + ); -/** - * Returns an array of roles. - * - * @since 1.0.0 - * @access public - * @return array - */ -function members_get_role_slugs() { - return array_keys( members_role_factory()->roles ); + members_register_role( $name, $args ); + } + + // Unset any roles that were registered previously but are not currently available. + foreach ( members_get_roles() as $role ) { + + if ( ! isset( $wp_roles->roles[ $role->name ] ) ) + members_unregister_role( $role->name ); + } } /** - * Returns an array of the role names of roles that have users. + * Returns the instance of the role registry. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return array + * @return object */ -function members_get_active_role_names() { - $has_users = array(); +function members_role_registry() { - foreach ( members_get_active_role_slugs() as $role ) - $has_users[ $role ] = members_get_role_name( $role ); - - return $has_users; + return \Members\Registry::get_instance( 'role' ); } /** - * Returns an array of the roles that have users. + * Returns all registered roles. * * @since 1.0.0 * @access public * @return array */ -function members_get_active_role_slugs() { - - $has_users = array(); - - foreach ( members_get_role_user_count() as $role => $count ) { - - if ( 0 < $count ) - $has_users[] = $role; - } +function members_get_roles() { - return $has_users; + return members_role_registry()->get_collection(); } /** - * Returns an array of the role names of roles that do not have users. + * Registers a role. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return array + * @param string $name + * @param array $args + * @return void */ -function members_get_inactive_role_names() { - return array_diff( members_get_role_names(), members_get_active_role_names() ); +function members_register_role( $name, $args = array() ) { + + members_role_registry()->register( $name, new \Members\Role( $name, $args ) ); } /** - * Returns an array of the roles that have no users. + * Unregisters a role. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return array + * @param string $name + * @return void */ -function members_get_inactive_role_slugs() { - return array_diff( members_get_role_slugs(), members_get_active_role_slugs() ); +function members_unregister_role( $name ) { + + members_role_registry()->unregister( $name ); } /** - * Returns an array of editable role names. + * Returns a role object. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return array + * @param string $name + * @return object */ -function members_get_editable_role_names() { - $editable = array(); +function members_get_role( $name ) { - foreach ( members_role_factory()->editable as $role ) - $editable[ $role->slug ] = $role->name; - - return $editable; + return members_role_registry()->get( $name ); } /** - * Returns an array of editable roles. + * Checks if a role object exists. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @return array + * @param string $name + * @return bool */ -function members_get_editable_role_slugs() { - return array_keys( members_role_factory()->editable ); +function members_role_exists( $name ) { + + return members_role_registry()->exists( $name ); } +/* ====== Multiple Role Functions ====== */ + /** - * Returns an array of uneditable role names. + * Returns an array of editable roles. * - * @since 1.0.0 + * @since 2.0.0 * @access public + * @global array $wp_roles * @return array */ -function members_get_uneditable_role_names() { - $uneditable = array(); +function members_get_editable_roles() { + global $wp_roles; - foreach ( members_role_factory()->uneditable as $role ) - $uneditable[ $role->slug ] = $role->name; + $editable = function_exists( 'get_editable_roles' ) ? get_editable_roles() : apply_filters( 'editable_roles', $wp_roles->roles ); - return $uneditable; + return array_keys( $editable ); } /** * Returns an array of uneditable roles. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return array */ -function members_get_uneditable_role_slugs() { - return array_keys( members_role_factory()->uneditable ); +function members_get_uneditable_roles() { + + return array_diff( array_keys( members_get_roles() ), members_get_editable_roles() ); } /** - * Returns an array of core WordPress role names. + * Returns an array of core WP roles. Note that we remove any that are not registered. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return array */ -function members_get_wordpress_role_names() { - $names = array(); +function members_get_wordpress_roles() { - foreach ( members_role_factory()->wordpress as $role ) - $names[ $role->slug ] = $role->name; + $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); - return $names; + return array_intersect( $roles, array_keys( members_get_roles() ) ); } /** - * Returns an array of core WP roles. + * Returns an array of the roles that have users. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @return array */ -function members_get_wordpress_role_slugs() { - return array_keys( members_role_factory()->wordpress ); -} +function members_get_active_roles() { -/* ====== Single Role Functions ====== */ + $has_users = array(); + + foreach ( members_get_role_user_count() as $role => $count ) { + + if ( 0 < $count ) + $has_users[] = $role; + } + + return $has_users; +} /** - * Conditional tag to check if a role exists. + * Returns an array of the roles that have no users. * - * @since 1.0.0 + * @since 2.0.0 * @access public - * @param string - * @return bool + * @return array */ -function members_role_exists( $role ) { - return $GLOBALS['wp_roles']->is_role( $role ); +function members_get_inactive_roles() { + + return array_diff( array_keys( members_get_roles() ), members_get_active_roles() ); } /** - * Gets a Members role object. + * Returns a count of all the available roles for the site. * - * @see Members_Role * @since 1.0.0 * @access public - * @param string - * @return object + * @return int */ -function members_get_role( $role ) { - return members_role_factory()->get_role( $role ); +function members_get_role_count() { + + return count( $GLOBALS['wp_roles']->role_names ); } +/* ====== Single Role Functions ====== */ + /** * Sanitizes a role name. This is a wrapper for the `sanitize_key()` WordPress function. Only * alphanumeric characters and underscores are allowed. Hyphens are also replaced with underscores. @@ -248,8 +231,10 @@ function members_get_role( $role ) { * @return int */ function members_sanitize_role( $role ) { + $_role = strtolower( $role ); $_role = preg_replace( '/[^a-z0-9_\-\s]/', '', $_role ); + return apply_filters( 'members_sanitize_role', str_replace( ' ', '_', $_role ), $role ); } @@ -269,7 +254,23 @@ function members_sanitize_role( $role ) { function members_translate_role( $role ) { global $wp_roles; - return apply_filters( 'members_translate_role', translate_user_role( $wp_roles->role_names[ $role ] ), $role ); + return members_translate_role_hook( $wp_roles->role_names[ $role ], $role ); +} + +/** + * Hook for translating user roles. I needed to separate this from the primary + * `members_translate_role()` function in case `$wp_roles` was not yet available + * but both the role and role label were. + * + * @since 2.0.1 + * @access public + * @param string $label + * @param string $role + * @return string + */ +function members_translate_role_hook( $label, $role ) { + + return apply_filters( 'members_translate_role', translate_user_role( $label ), $role ); } /** @@ -280,7 +281,8 @@ function members_translate_role( $role ) { * @return bool */ function members_role_has_users( $role ) { - return in_array( $role, members_get_active_role_slugs() ); + + return in_array( $role, members_get_active_roles() ); } /** @@ -291,7 +293,8 @@ function members_role_has_users( $role ) { * @return bool */ function members_role_has_caps( $role ) { - return members_role_factory()->get_role( $role )->has_caps; + + return members_get_role( $role )->has_caps; } /** @@ -333,7 +336,8 @@ function members_get_role_user_count( $role = '' ) { * @return int */ function members_get_role_granted_cap_count( $role ) { - return members_role_factory()->get_role( $role )->granted_cap_count; + + return members_get_role( $role )->granted_cap_count; } /** @@ -345,19 +349,8 @@ function members_get_role_granted_cap_count( $role ) { * @return int */ function members_get_role_denied_cap_count( $role ) { - return members_role_factory()->get_role( $role )->denied_cap_count; -} -/** - * Returns the human-readable role name. - * - * @since 1.0.0 - * @access public - * @param string $role - * @return string - */ -function members_get_role_name( $role ) { - return members_role_factory()->get_role( $role )->name; + return members_get_role( $role )->denied_cap_count; } /** @@ -369,11 +362,12 @@ function members_get_role_name( $role ) { * @return bool */ function members_is_role_editable( $role ) { - return members_role_factory()->get_role( $role )->is_editable; + + return in_array( $role, members_get_editable_roles() ); } /** - * Conditional tag to check whether a role is a core WordPress URL. + * Conditional tag to check whether a role is a core WordPress role. * * @since 1.0.0 * @access public @@ -381,6 +375,7 @@ function members_is_role_editable( $role ) { * @return bool */ function members_is_wordpress_role( $role ) { + return in_array( $role, array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ) ); } @@ -394,6 +389,7 @@ function members_is_wordpress_role( $role ) { * @return string */ function members_get_new_role_url() { + return add_query_arg( 'page', 'role-new', admin_url( 'users.php' ) ); } @@ -406,6 +402,7 @@ function members_get_new_role_url() { * @return string */ function members_get_clone_role_url( $role ) { + return add_query_arg( 'clone', $role, members_get_new_role_url() ); } @@ -417,6 +414,7 @@ function members_get_clone_role_url( $role ) { * @return string */ function members_get_edit_roles_url() { + return add_query_arg( 'page', 'roles', admin_url( 'users.php' ) ); } @@ -429,7 +427,8 @@ function members_get_edit_roles_url() { * @return string */ function members_get_role_view_url( $view ) { - return add_query_arg( 'role_view', $view, members_get_edit_roles_url() ); + + return add_query_arg( 'view', $view, members_get_edit_roles_url() ); } /** @@ -441,6 +440,7 @@ function members_get_role_view_url( $view ) { * @return string */ function members_get_edit_role_url( $role ) { + return add_query_arg( array( 'action' => 'edit', 'role' => $role ), members_get_edit_roles_url() ); } @@ -453,6 +453,7 @@ function members_get_edit_role_url( $role ) { * @return string */ function members_get_delete_role_url( $role ) { + $url = add_query_arg( array( 'action' => 'delete', 'role' => $role ), members_get_edit_roles_url() ); return wp_nonce_url( $url, 'delete_role', 'members_delete_role_nonce' ); @@ -467,5 +468,6 @@ function members_get_delete_role_url( $role ) { * @return string */ function members_get_role_users_url( $role ) { + return admin_url( add_query_arg( 'role', $role, 'users.php' ) ); } diff --git a/inc/functions-shortcodes.php b/inc/functions-shortcodes.php index ba6d3f1..dda5621 100644 --- a/inc/functions-shortcodes.php +++ b/inc/functions-shortcodes.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -38,6 +38,9 @@ function members_register_shortcodes() { add_shortcode( 'members_logged_in', 'members_is_user_logged_in_shortcode' ); add_shortcode( 'is_user_logged_in', 'members_is_user_logged_in_shortcode' ); // @deprecated 1.0.0 + // Add the `[members_not_logged_in]` shortcode. + add_shortcode( 'members_not_logged_in', 'members_not_logged_in_shortcode' ); + // @deprecated 0.2.0. add_shortcode( 'get_avatar', 'members_get_avatar_shortcode' ); add_shortcode( 'avatar', 'members_get_avatar_shortcode' ); @@ -58,6 +61,20 @@ function members_is_user_logged_in_shortcode( $attr, $content = null ) { return is_feed() || ! is_user_logged_in() || is_null( $content ) ? '' : do_shortcode( $content ); } +/** + * Displays content if the user viewing it is not currently logged in. + * + * @since 2.0.0 + * @access public + * @param array $attr + * @param string $content + * @return string + */ +function members_not_logged_in_shortcode( $attr, $content = null ) { + + return is_user_logged_in() || is_null( $content ) ? '' : do_shortcode( $content ); +} + /** * Content that should only be shown in feed readers. Can be useful for displaying * feed-specific items. @@ -91,28 +108,34 @@ function members_access_check_shortcode( $attr, $content = null ) { if ( is_null( $content ) || is_feed() ) return ''; + $user_can = false; + // Set up the default attributes. $defaults = array( 'capability' => '', // Single capability or comma-separated multiple capabilities. 'role' => '', // Single role or comma-separated multiple roles. + 'user_id' => '', // Single user ID or comma-separated multiple IDs. + 'user_name' => '', // Single user name or comma-separated multiple names. + 'user_email' => '', // Single user email or comma-separated multiple emails. + 'operator' => 'or' // Only the `!` operator is supported for now. Everything else falls back to `or`. ); // Merge the input attributes and the defaults. $attr = shortcode_atts( $defaults, $attr, 'members_access' ); + // Get the operator. + $operator = strtolower( $attr['operator'] ); + // If the current user has the capability, show the content. if ( $attr['capability'] ) { // Get the capabilities. $caps = explode( ',', $attr['capability'] ); - // Loop through each capability. - foreach ( $caps as $cap ) { + if ( '!' === $operator ) + return members_current_user_can_any( $caps ) ? '' : do_shortcode( $content ); - // If the current user can perform the capability, return the content. - if ( current_user_can( trim( $cap ) ) ) - return do_shortcode( $content ); - } + return members_current_user_can_any( $caps ) ? do_shortcode( $content ) : ''; } // If the current user has the role, show the content. @@ -121,13 +144,60 @@ function members_access_check_shortcode( $attr, $content = null ) { // Get the roles. $roles = explode( ',', $attr['role'] ); - // Loop through each of the roles. - foreach ( $roles as $role ) { + if ( '!' === $operator ) + return members_current_user_has_role( $roles ) ? '' : do_shortcode( $content ); + + return members_current_user_has_role( $roles ) ? do_shortcode( $content ) : ''; + } + + $user_id = 0; + $user_name = $user_email = ''; + + if ( is_user_logged_in() ) { - // If the current user has the role, return the content. - if ( members_current_user_has_role( trim( $role ) ) ) - return do_shortcode( $content ); + $user = wp_get_current_user(); + $user_id = get_current_user_id(); + $user_name = $user->user_login; + $user_email = $user->user_email; + } + + // If the current user has one of the user ids. + if ( $attr['user_id'] ) { + + // Get the user IDs. + $ids = array_map( 'trim', explode( ',', $attr['user_id'] ) ); + + if ( '!' === $operator ) { + return in_array( $user_id, $ids ) ? '' : do_shortcode( $content ); + } + + return in_array( $user_id, $ids ) ? do_shortcode( $content ) : ''; + } + + // If the current user has one of the user names. + if ( $attr['user_name'] ) { + + // Get the user names. + $names = array_map( 'trim', explode( ',', $attr['user_name'] ) ); + + if ( '!' === $operator ) { + return in_array( $user_name, $names ) ? '' : do_shortcode( $content ); } + + return in_array( $user_name, $names ) ? do_shortcode( $content ) : ''; + } + + // If the current user has one of the user emails. + if ( $attr['user_email'] ) { + + // Get the user emails. + $emails = array_map( 'trim', explode( ',', $attr['user_email'] ) ); + + if ( '!' === $operator ) { + return in_array( $user_email, $emails ) ? '' : do_shortcode( $content ); + } + + return in_array( $user_email, $emails ) ? do_shortcode( $content ) : ''; } // Return an empty string if we've made it to this point. @@ -142,5 +212,6 @@ function members_access_check_shortcode( $attr, $content = null ) { * @return string */ function members_login_form_shortcode() { + return wp_login_form( array( 'echo' => false ) ); } diff --git a/inc/functions-users.php b/inc/functions-users.php index cbf567a..8a5e1bd 100644 --- a/inc/functions-users.php +++ b/inc/functions-users.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -38,7 +38,7 @@ function members_user_has_cap_filter( $allcaps, $caps, $args, $user ) { return $allcaps; // Get the denied caps. - $denied_caps = array_keys( $allcaps, false ); + $denied_caps = array_keys( (array) $allcaps, false ); // Loop through the user's roles and find any denied caps. foreach ( (array) $user->roles as $role ) { @@ -47,8 +47,12 @@ function members_user_has_cap_filter( $allcaps, $caps, $args, $user ) { $role_obj = get_role( $role ); // If we have an object, merge it's denied caps. - if ( ! is_null( $role_obj ) ) - $denied_caps = array_merge( $denied_caps, array_keys( $role_obj->capabilities, false ) ); + if ( ! is_null( $role_obj ) ) { + $denied_caps = array_merge( + (array) $denied_caps, + array_keys( (array) $role_obj->capabilities, false ) + ); + } } // If there are any denied caps, make sure they take precedence. @@ -68,15 +72,21 @@ function members_user_has_cap_filter( $allcaps, $caps, $args, $user ) { * * @since 1.0.0 * @access public - * @param int $user_id - * @param string $role + * @param int $user_id + * @param string|array $roles * @return bool */ -function members_user_has_role( $user_id, $role ) { +function members_user_has_role( $user_id, $roles ) { $user = new WP_User( $user_id ); - return in_array( $role, (array) $user->roles ); + foreach ( (array) $roles as $role ) { + + if ( in_array( $role, (array) $user->roles ) ) + return true; + } + + return false; } /** @@ -84,12 +94,52 @@ function members_user_has_role( $user_id, $role ) { * * @since 1.0.0 * @access public - * @param string $role + * @param string|array $roles + * @return bool + */ +function members_current_user_has_role( $roles ) { + + return is_user_logged_in() ? members_user_has_role( get_current_user_id(), $roles ) : false; +} + +/** + * Wrapper for `current_user_can()` that checks if the user can perform any action. + * Accepts an array of caps instead of a single cap. + * + * @since 2.0.0 + * @access public + * @param array $caps + * @return bool + */ +function members_current_user_can_any( $caps = array() ) { + + foreach ( $caps as $cap ) { + + if ( current_user_can( $cap ) ) + return true; + } + + return false; +} + +/** + * Wrapper for `current_user_can()` that checks if the user can perform all actions. + * Accepts an array of caps instead of a single cap. + * + * @since 2.0.0 + * @access public + * @param array $caps * @return bool */ -function members_current_user_has_role( $role ) { +function members_current_user_can_all( $caps = array() ) { + + foreach ( $caps as $cap ) { + + if ( ! current_user_can( $cap ) ) + return false; + } - return is_user_logged_in() ? members_user_has_role( get_current_user_id(), $role ) : false; + return true; } /** @@ -107,7 +157,7 @@ function members_get_user_role_names( $user_id ) { $names = array(); foreach ( $user->roles as $role ) - $names[ $role ] = members_get_role_name( $role ); + $names[ $role ] = members_get_role( $role )->get( 'label' ); return $names; } diff --git a/inc/functions-widgets.php b/inc/functions-widgets.php index 9cf0625..ca35ae2 100644 --- a/inc/functions-widgets.php +++ b/inc/functions-widgets.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -25,16 +25,16 @@ function members_register_widgets() { // If the login form widget is enabled. if ( members_login_widget_enabled() ) { - require_once( members_plugin()->inc_dir . 'class-widget-login.php' ); + require_once( members_plugin()->dir . 'inc/class-widget-login.php' ); - register_widget( 'Members_Widget_Login' ); + register_widget( '\Members\Widget_Login' ); } // If the users widget is enabled. if ( members_users_widget_enabled() ) { - require_once( members_plugin()->inc_dir . 'class-widget-users.php' ); + require_once( members_plugin()->dir . 'inc/class-widget-users.php' ); - register_widget( 'Members_Widget_users' ); + register_widget( '\Members\Widget_Users' ); } } diff --git a/inc/functions.php b/inc/functions.php index 0d89927..55b689d 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -20,5 +20,21 @@ * @return bool */ function members_validate_boolean( $val ) { + return filter_var( $val, FILTER_VALIDATE_BOOLEAN ); } + + +/** + * Helper function for sorting objects by priority. + * + * @since 2.0.0 + * @access protected + * @param object $a + * @param object $b + * @return int + */ +function members_priority_sort( $a, $b ) { + + return $a->priority - $b->priority; +} diff --git a/inc/template.php b/inc/template.php index b118eda..29df3fe 100644 --- a/inc/template.php +++ b/inc/template.php @@ -4,9 +4,9 @@ * * @package Members * @subpackage Includes - * @author Justin Tadlock - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -29,8 +29,12 @@ function members_can_user_view_post( $user_id, $post_id = '' ) { // If no post ID is given, assume we're in The Loop and get the current post's ID. - if ( ! $post_id ) + if ( ! $post_id ) { $post_id = get_the_ID(); + } + + // Get post object. + $post = get_post( $post_id ); // Assume the user can view the post at this point. */ $can_view = true; @@ -38,7 +42,7 @@ function members_can_user_view_post( $user_id, $post_id = '' ) { // The plugin is only going to handle permissions if the 'content permissions' feature // is active. If not active, the user can always view the post. However, developers // can roll their own handling of this and filter `members_can_user_view_post`. - if ( members_content_permissions_enabled() ) { + if ( $post instanceof \WP_Post && members_content_permissions_enabled() ) { // Get the roles selected by the user. $roles = members_get_post_roles( $post_id ); @@ -55,9 +59,6 @@ function members_can_user_view_post( $user_id, $post_id = '' ) { // to disprove this. $can_view = false; - // Get the post object. - $post = get_post( $post_id ); - // Get the post type object. $post_type = get_post_type_object( $post->post_type ); @@ -92,11 +93,15 @@ function members_can_user_view_post( $user_id, $post_id = '' ) { // Set to `FALSE` to avoid hierarchical checking. if ( apply_filters( 'members_check_parent_post_permission', $check_parent, $post_id, $user_id ) ) { - $parent_id = get_post( $post_id )->post_parent; + if ( $post instanceof \WP_Post ) { - // If the post has a parent, check if the user has permission to view it. - if ( 0 < $parent_id ) - $can_view = members_can_user_view_post( $user_id, $parent_id ); + $parent_id = $post->post_parent; + + // If the post has a parent, check if the user has permission to view it. + if ( 0 < $parent_id ) { + $can_view = members_can_user_view_post( $user_id, $parent_id ); + } + } } // Allow developers to overwrite the final return value. diff --git a/js/edit-post.js b/js/edit-post.js new file mode 100644 index 0000000..d80606f --- /dev/null +++ b/js/edit-post.js @@ -0,0 +1,38 @@ +( function() { + + /* ====== Tabs ====== */ + + // Hides the tab content. + jQuery( '.members-tabs .members-tab-content' ).hide(); + + // Shows the first tab's content. + jQuery( '.members-tabs .members-tab-content:first-child' ).show(); + + // Makes the 'aria-selected' attribute true for the first tab nav item. + jQuery( '.members-tab-nav :first-child' ).attr( 'aria-selected', 'true' ); + + // When a tab nav item is clicked. + jQuery( '.members-tab-nav li a' ).click( + function( j ) { + + // Prevent the default browser action when a link is clicked. + j.preventDefault(); + + // Get the `href` attribute of the item. + var href = jQuery( this ).attr( 'href' ); + + // Hide all tab content. + jQuery( this ).parents( '.members-tabs' ).find( '.members-tab-content' ).hide(); + + // Find the tab content that matches the tab nav item and show it. + jQuery( this ).parents( '.members-tabs' ).find( href ).show(); + + // Set the `aria-selected` attribute to false for all tab nav items. + jQuery( this ).parents( '.members-tabs' ).find( '.members-tab-title' ).attr( 'aria-selected', 'false' ); + + // Set the `aria-selected` attribute to true for this tab nav item. + jQuery( this ).parent().attr( 'aria-selected', 'true' ); + } + ); // click() + +}() ); diff --git a/js/edit-post.min.js b/js/edit-post.min.js new file mode 100644 index 0000000..8a192a9 --- /dev/null +++ b/js/edit-post.min.js @@ -0,0 +1 @@ +!function(){jQuery(".members-tabs .members-tab-content").hide(),jQuery(".members-tabs .members-tab-content:first-child").show(),jQuery(".members-tab-nav :first-child").attr("aria-selected","true"),jQuery(".members-tab-nav li a").click(function(e){e.preventDefault();var t=jQuery(this).attr("href");jQuery(this).parents(".members-tabs").find(".members-tab-content").hide(),jQuery(this).parents(".members-tabs").find(t).show(),jQuery(this).parents(".members-tabs").find(".members-tab-title").attr("aria-selected","false"),jQuery(this).parent().attr("aria-selected","true")})}(); \ No newline at end of file diff --git a/js/edit-role.js b/js/edit-role.js index dc62509..0f2ae5a 100644 --- a/js/edit-role.js +++ b/js/edit-role.js @@ -325,7 +325,8 @@ jQuery( document ).ready( function() { function() { // If there's a value in the input, enable the add new button. - if ( jQuery( this ).val() ) { + //if ( 'do_not_allow' !== jQuery( this ).val() ) { + if ( -1 === jQuery.inArray( jQuery( this ).val(), members_i18n.hidden_caps ) ) { jQuery( '#members-add-new-cap' ).prop( 'disabled', false ); @@ -363,6 +364,12 @@ jQuery( document ).ready( function() { // If there's a new cap value. if ( new_cap ) { + // Don't allow the 'do_not_allow' cap. + //if ( 'do_not_allow' === new_cap ) { + if ( -1 !== jQuery.inArray( jQuery( this ).val(), members_i18n.hidden_caps ) ) { + return; + } + // Trigger a click event on the "custom" tab in the edit caps box. jQuery( 'a[href="#members-tab-custom"]' ).trigger( 'click' ); @@ -377,7 +384,7 @@ jQuery( document ).ready( function() { name : { grant : 'grant-new-caps[]', deny : 'deny-new-caps[]' }, is_granted_cap : true, is_denied_cap : false, - label : { grant : members_i18n.label_grant_cap, deny : members_i18n.label_deny_cap } + label : { cap : new_cap, grant : members_i18n.label_grant_cap, deny : members_i18n.label_deny_cap } }; // Prepend our template to the "custom" edit caps tab content. diff --git a/js/edit-role.min.js b/js/edit-role.min.js index 3031c7c..d7011c6 100644 --- a/js/edit-role.min.js +++ b/js/edit-role.min.js @@ -1 +1 @@ -jQuery(document).ready(function(){function e(e){e=e.toLowerCase().trim().replace(/<.*?>/g,"").replace(/\s/g,"_").replace(/[^a-zA-Z0-9_]/g,""),jQuery(".role-slug").text(e)}function r(){var e=jQuery("#members-tab-all input[data-grant-cap]:checked").length,r=jQuery("#members-tab-all input[data-deny-cap]:checked").length,t=jQuery('#members-tab-custom input[name="grant-new-caps[]"]:checked').length,a=jQuery('#members-tab-custom input[name="deny-new-caps[]"]:checked').length;jQuery("#submitdiv .granted-count").text(e+t),jQuery("#submitdiv .denied-count").text(r+a)}function t(e){var r="grant",t="deny";jQuery(e).attr("data-deny-cap")&&(r="deny",t="grant");var a=jQuery(e).attr("data-"+r+"-cap");jQuery(e).prop("checked")?(jQuery("input[data-"+r+'-cap="'+a+'"]').not(e).prop("checked",!0),jQuery("input[data-"+t+'-cap="'+a+'"]').prop("checked",!1)):jQuery("input[data-"+r+'-cap="'+a+'"]').not(e).prop("checked",!1)}jQuery(".members-delete-role-link").click(function(){return window.confirm(members_i18n.ays_delete_role)}),jQuery('input[name="role_name"]').keyup(function(){jQuery('input[name="role"]').val()||e(this.value)}),jQuery('input[name="role"], .role-ok-button').hide(),jQuery(document).on("click",".role-edit-button.closed",function(){jQuery(this).removeClass("closed").addClass("open").text(members_i18n.button_role_ok),jQuery('input[name="role"]').show(),jQuery('input[name="role"]').trigger("focus"),jQuery('input[name="role"]').attr("value",jQuery(".role-slug").text())}),jQuery(document).on("click",".role-edit-button.open",function(){jQuery(this).removeClass("open").addClass("closed").text(members_i18n.button_role_edit),jQuery('input[name="role"]').hide();var r=jQuery('input[name="role"]').val();r?e(r):e(jQuery('input[name="role_name"]').val())}),jQuery('input[name="role"]').keypress(function(e){return 13===e.keyCode?(jQuery(".role-edit-button").click().trigger("focus"),e.preventDefault(),!1):void 0}),jQuery('.users_page_role-new input[name="role_name"]').val()||jQuery(".users_page_role-new #publish").prop("disabled",!0),jQuery('.users_page_role-new input[name="role_name"]').on("input",function(){jQuery(this).val()?jQuery(".users_page_role-new #publish").prop("disabled",!1):jQuery(".users_page_role-new #publish").prop("disabled",!0)});var a=wp.template("members-cap-section"),n=wp.template("members-cap-control");"undefined"!=typeof members_sections&&"undefined"!=typeof members_controls&&(_.each(members_sections,function(e){jQuery(".members-tab-wrap").append(a(e))}),_.each(members_controls,function(e){jQuery("#members-tab-"+e.section+" tbody").append(n(e))})),jQuery(".members-cap-tabs .members-tab-content").hide(),jQuery(".members-cap-tabs .members-tab-content:first-child").show(),jQuery(".members-tab-nav :first-child").attr("aria-selected","true"),jQuery(".members-which-tab").text(jQuery(".members-tab-nav :first-child a").text()),jQuery(".members-tab-nav li a").click(function(e){e.preventDefault();var r=jQuery(this).attr("href");jQuery(this).parents(".members-cap-tabs").find(".members-tab-content").hide(),jQuery(this).parents(".members-cap-tabs").find(r).show(),jQuery(this).parents(".members-cap-tabs").find(".members-tab-title").attr("aria-selected","false"),jQuery(this).parent().attr("aria-selected","true"),jQuery(".members-which-tab").text(jQuery(this).text())}),r(),jQuery(document).on("change",".members-cap-checklist input[data-grant-cap], .members-cap-checklist input[data-deny-cap]",function(){t(this),r()}),jQuery(document).on("click",".editable-role .members-cap-checklist button",function(){var e=jQuery(this).closest(".members-cap-checklist"),r=jQuery(e).find("input[data-grant-cap]"),t=jQuery(e).find("input[data-deny-cap]");jQuery(r).prop("checked")?(jQuery(r).prop("checked",!1),jQuery(t).prop("checked",!0).change()):jQuery(t).prop("checked")?(jQuery(r).prop("checked",!1),jQuery(t).prop("checked",!1).change()):jQuery(r).prop("checked",!0).change()}),jQuery(document).on("hover",".editable-role .members-cap-checklist button",function(){jQuery(".members-cap-checklist button:focus").not(this).blur()}),postboxes.add_postbox_toggles(pagenow),jQuery("#newcapdiv button.handlediv").attr("type","button"),jQuery("#members-add-new-cap").prop("disabled",!0),jQuery("#members-new-cap-field").on("input",function(){jQuery(this).val()?jQuery("#members-add-new-cap").prop("disabled",!1):jQuery("#members-add-new-cap").prop("disabled",!0)}),jQuery("#members-new-cap-field").keypress(function(e){return 13===e.keyCode?(jQuery("#members-add-new-cap").click(),e.preventDefault(),!1):void 0}),jQuery("#members-add-new-cap").click(function(){var e=jQuery("#members-new-cap-field").val();if(e=e.trim().replace(/<.*?>/g,"").replace(/\s/g,"_").replace(/[^a-zA-Z0-9_]/g,"")){jQuery('a[href="#members-tab-custom"]').trigger("click"),members_i18n.label_grant_cap=members_i18n.label_grant_cap.replace(/%s/g,""+e+""),members_i18n.label_deny_cap=members_i18n.label_deny_cap.replace(/%s/g,""+e+"");var r={cap:e,readonly:"",name:{grant:"grant-new-caps[]",deny:"deny-new-caps[]"},is_granted_cap:!0,is_denied_cap:!1,label:{grant:members_i18n.label_grant_cap,deny:members_i18n.label_deny_cap}};jQuery("#members-tab-custom tbody").prepend(n(r));var t=jQuery('[data-grant-cap="'+e+'"]').parents(".members-cap-checklist");jQuery(t).addClass("members-highlight"),setTimeout(function(){jQuery(t).removeClass("members-highlight")},500),jQuery("#members-new-cap-field").val(""),jQuery("#members-add-new-cap").prop("disabled",!0),jQuery('.members-cap-checklist input[data-grant-cap="'+e+'"]').trigger("change")}})}); \ No newline at end of file +jQuery(document).ready(function(){function e(e){e=e.toLowerCase().trim().replace(/<.*?>/g,"").replace(/\s/g,"_").replace(/[^a-zA-Z0-9_]/g,""),jQuery(".role-slug").text(e)}function r(){var e=jQuery("#members-tab-all input[data-grant-cap]:checked").length,r=jQuery("#members-tab-all input[data-deny-cap]:checked").length,t=jQuery('#members-tab-custom input[name="grant-new-caps[]"]:checked').length,a=jQuery('#members-tab-custom input[name="deny-new-caps[]"]:checked').length;jQuery("#submitdiv .granted-count").text(e+t),jQuery("#submitdiv .denied-count").text(r+a)}function t(e){var r="grant",t="deny";jQuery(e).attr("data-deny-cap")&&(r="deny",t="grant");var a=jQuery(e).attr("data-"+r+"-cap");jQuery(e).prop("checked")?(jQuery("input[data-"+r+'-cap="'+a+'"]').not(e).prop("checked",!0),jQuery("input[data-"+t+'-cap="'+a+'"]').prop("checked",!1)):jQuery("input[data-"+r+'-cap="'+a+'"]').not(e).prop("checked",!1)}jQuery(".members-delete-role-link").click(function(){return window.confirm(members_i18n.ays_delete_role)}),jQuery('input[name="role_name"]').keyup(function(){jQuery('input[name="role"]').val()||e(this.value)}),jQuery('input[name="role"], .role-ok-button').hide(),jQuery(document).on("click",".role-edit-button.closed",function(){jQuery(this).removeClass("closed").addClass("open").text(members_i18n.button_role_ok),jQuery('input[name="role"]').show(),jQuery('input[name="role"]').trigger("focus"),jQuery('input[name="role"]').attr("value",jQuery(".role-slug").text())}),jQuery(document).on("click",".role-edit-button.open",function(){jQuery(this).removeClass("open").addClass("closed").text(members_i18n.button_role_edit),jQuery('input[name="role"]').hide();var r=jQuery('input[name="role"]').val();e(r?r:jQuery('input[name="role_name"]').val())}),jQuery('input[name="role"]').keypress(function(e){if(13===e.keyCode)return jQuery(".role-edit-button").click().trigger("focus"),e.preventDefault(),!1}),jQuery('.users_page_role-new input[name="role_name"]').val()||jQuery(".users_page_role-new #publish").prop("disabled",!0),jQuery('.users_page_role-new input[name="role_name"]').on("input",function(){jQuery(this).val()?jQuery(".users_page_role-new #publish").prop("disabled",!1):jQuery(".users_page_role-new #publish").prop("disabled",!0)});var a=wp.template("members-cap-section"),n=wp.template("members-cap-control");"undefined"!=typeof members_sections&&"undefined"!=typeof members_controls&&(_.each(members_sections,function(e){jQuery(".members-tab-wrap").append(a(e))}),_.each(members_controls,function(e){jQuery("#members-tab-"+e.section+" tbody").append(n(e))})),jQuery(".members-cap-tabs .members-tab-content").hide(),jQuery(".members-cap-tabs .members-tab-content:first-child").show(),jQuery(".members-tab-nav :first-child").attr("aria-selected","true"),jQuery(".members-which-tab").text(jQuery(".members-tab-nav :first-child a").text()),jQuery(".members-tab-nav li a").click(function(e){e.preventDefault();var r=jQuery(this).attr("href");jQuery(this).parents(".members-cap-tabs").find(".members-tab-content").hide(),jQuery(this).parents(".members-cap-tabs").find(r).show(),jQuery(this).parents(".members-cap-tabs").find(".members-tab-title").attr("aria-selected","false"),jQuery(this).parent().attr("aria-selected","true"),jQuery(".members-which-tab").text(jQuery(this).text())}),r(),jQuery(document).on("change",".members-cap-checklist input[data-grant-cap], .members-cap-checklist input[data-deny-cap]",function(){t(this),r()}),jQuery(document).on("click",".editable-role .members-cap-checklist button",function(){var e=jQuery(this).closest(".members-cap-checklist"),r=jQuery(e).find("input[data-grant-cap]"),t=jQuery(e).find("input[data-deny-cap]");jQuery(r).prop("checked")?(jQuery(r).prop("checked",!1),jQuery(t).prop("checked",!0).change()):jQuery(t).prop("checked")?(jQuery(r).prop("checked",!1),jQuery(t).prop("checked",!1).change()):jQuery(r).prop("checked",!0).change()}),jQuery(document).on("hover",".editable-role .members-cap-checklist button",function(){jQuery(".members-cap-checklist button:focus").not(this).blur()}),postboxes.add_postbox_toggles(pagenow),jQuery("#newcapdiv button.handlediv").attr("type","button"),jQuery("#members-add-new-cap").prop("disabled",!0),jQuery("#members-new-cap-field").on("input",function(){-1===jQuery.inArray(jQuery(this).val(),members_i18n.hidden_caps)?jQuery("#members-add-new-cap").prop("disabled",!1):jQuery("#members-add-new-cap").prop("disabled",!0)}),jQuery("#members-new-cap-field").keypress(function(e){if(13===e.keyCode)return jQuery("#members-add-new-cap").click(),e.preventDefault(),!1}),jQuery("#members-add-new-cap").click(function(){var e=jQuery("#members-new-cap-field").val();if(e=e.trim().replace(/<.*?>/g,"").replace(/\s/g,"_").replace(/[^a-zA-Z0-9_]/g,"")){if(-1!==jQuery.inArray(jQuery(this).val(),members_i18n.hidden_caps))return;jQuery('a[href="#members-tab-custom"]').trigger("click"),members_i18n.label_grant_cap=members_i18n.label_grant_cap.replace(/%s/g,""+e+""),members_i18n.label_deny_cap=members_i18n.label_deny_cap.replace(/%s/g,""+e+"");var r={cap:e,readonly:"",name:{grant:"grant-new-caps[]",deny:"deny-new-caps[]"},is_granted_cap:!0,is_denied_cap:!1,label:{cap:e,grant:members_i18n.label_grant_cap,deny:members_i18n.label_deny_cap}};jQuery("#members-tab-custom tbody").prepend(n(r));var t=jQuery('[data-grant-cap="'+e+'"]').parents(".members-cap-checklist");jQuery(t).addClass("members-highlight"),setTimeout(function(){jQuery(t).removeClass("members-highlight")},500),jQuery("#members-new-cap-field").val(""),jQuery("#members-add-new-cap").prop("disabled",!0),jQuery('.members-cap-checklist input[data-grant-cap="'+e+'"]').trigger("change")}})}); \ No newline at end of file diff --git a/lang/members.pot b/lang/members.pot new file mode 100644 index 0000000..d6fb307 --- /dev/null +++ b/lang/members.pot @@ -0,0 +1,1304 @@ +# Copyright (C) 2019 Members +# This file is distributed under the same license as the Members package. +msgid "" +msgstr "" +"Project-Id-Version: Members 2.2.0-alpha\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/members\n" +"POT-Creation-Date: 2019-07-20 01:10:06+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" + +#: _assets/class-cap-edit.php:92 admin/class-manage-users.php:161 +#: admin/class-manage-users.php:236 admin/class-role-edit.php:92 +#: members.php:129 members.php:140 +msgid "Whoah, partner!" +msgstr "" + +#: _assets/class-cap-edit.php:99 +msgid "The requested cap to edit does not exist." +msgstr "" + +#: _assets/class-cap-edit.php:235 +msgid "%s cap updated." +msgstr "" + +#: _assets/class-cap-edit.php:239 +msgid "The %s cap is not editable. This means that it is most likely added via another plugin for a special use or that you do not have permission to edit it." +msgstr "" + +#: _assets/class-cap-edit.php:247 +msgid "The %s cap has been created." +msgstr "" + +#: _assets/class-cap-edit.php:309 +msgid "Edit Cap" +msgstr "" + +#: _assets/class-cap-edit.php:312 _assets/class-caps.php:175 +msgctxt "cap" +msgid "Add New" +msgstr "" + +#: _assets/class-cap-edit.php:331 +msgid "Capability Label" +msgstr "" + +#: _assets/class-cap-edit.php:337 +msgid "Capability:" +msgstr "" + +#: _assets/class-cap-edit.php:344 _assets/class-cap-new.php:185 +#: _assets/class-caps.php:70 admin/class-manage-roles.php:86 +#: admin/class-manage-roles.php:92 admin/class-manage-users.php:360 +#: admin/class-meta-box-content-permissions.php:174 admin/class-roles.php:170 +#: admin/class-user-edit.php:92 +msgid "Roles" +msgstr "" + +#: _assets/class-cap-edit.php:351 _assets/class-cap-edit.php:358 +#: admin/class-meta-box-publish-role.php:67 admin/class-roles.php:68 +#: inc/functions-admin-bar.php:35 +msgid "Role" +msgstr "" + +#: _assets/class-cap-edit.php:352 _assets/class-cap-edit.php:359 +#: admin/tmpl/cap-section.php:20 admin/tmpl/cap-section.php:28 +msgid "Grant" +msgstr "" + +#: _assets/class-cap-edit.php:353 _assets/class-cap-edit.php:360 +#: admin/tmpl/cap-section.php:21 admin/tmpl/cap-section.php:29 +msgid "Deny" +msgstr "" + +#: _assets/class-cap-list-table.php:377 admin/class-role-list-table.php:355 +#: admin/class-role-new.php:335 admin/functions-admin.php:46 +msgid "Edit" +msgstr "" + +#: _assets/class-cap-list-table.php:381 _assets/class-cap-list-table.php:520 +#: _assets/class-capability-list-table.php:264 +#: _assets/class-capability-list-table.php:387 +#: admin/class-role-list-table.php:359 admin/class-role-list-table.php:508 +msgid "Delete" +msgstr "" + +#: _assets/class-cap-list-table.php:387 admin/class-role-list-table.php:365 +msgid "View" +msgstr "" + +#: _assets/class-cap-list-table.php:452 +#: _assets/class-capability-list-table.php:319 +#: admin/class-role-list-table.php:418 +msgid "All %s" +msgid_plural "All %s" +msgstr[0] "" +msgstr[1] "" + +#: _assets/class-cap-list-table.php:464 +#: _assets/class-capability-list-table.php:320 +#: admin/class-role-list-table.php:423 +msgid "Mine %s" +msgid_plural "Mine %s" +msgstr[0] "" +msgstr[1] "" + +#: _assets/class-cap-list-table.php:538 +msgid "All Groups" +msgstr "" + +#: _assets/class-cap-list-table.php:547 +#: _assets/class-capability-list-table.php:359 +msgid "Filter" +msgstr "" + +#: _assets/class-cap-new.php:64 _assets/class-cap-new.php:158 +msgid "Add New Capability" +msgstr "" + +#: _assets/class-cap-new.php:102 +msgid "Please input a valid capability." +msgstr "" + +#: _assets/class-cap-new.php:106 +msgid "The %s capability already exists." +msgstr "" + +#: _assets/class-cap-new.php:110 +msgid "Please select at least one role." +msgstr "" + +#: _assets/class-cap-new.php:123 +msgid "The %s capability has been created." +msgstr "" + +#: _assets/class-cap-new.php:172 _assets/class-caps.php:68 +#: admin/tmpl/cap-section.php:19 admin/tmpl/cap-section.php:27 +msgid "Capability" +msgstr "" + +#: _assets/class-cap-new.php:178 +msgid "The capability should be unique and contain only alphanumeric characters and underscores." +msgstr "" + +#: _assets/class-cap-new.php:189 +msgid "Select at least one role. Because of the way capabilities work in WordPress, they can only exist if assigned to a role." +msgstr "" + +#: _assets/class-cap-new.php:209 +msgid "Add Capability" +msgstr "" + +#: _assets/class-capability-list-table.php:321 inc/functions-role-groups.php:43 +msgid "WordPress %s" +msgid_plural "WordPress %s" +msgstr[0] "" +msgstr[1] "" + +#: _assets/class-capability-list-table.php:322 +msgid "Members %s" +msgid_plural "Members %s" +msgstr[0] "" +msgstr[1] "" + +#: _assets/class-capability-list-table.php:349 +msgid "View all roles" +msgstr "" + +#: _assets/class-caps.php:67 +msgid "Label" +msgstr "" + +#: _assets/class-caps.php:69 +msgid "Groups" +msgstr "" + +#: _assets/class-caps.php:114 +msgid "Selected caps deleted." +msgstr "" + +#: _assets/class-caps.php:133 +msgid "%s cap deleted." +msgstr "" + +#: _assets/class-caps.php:172 _assets/class-manage-caps.php:86 +#: _assets/class-manage-caps.php:92 _assets/page-capabilities.php:45 +#: _assets/page-capabilities.php:93 admin/views/class-view-general.php:72 +msgid "Capabilities" +msgstr "" + +#: _assets/class-caps.php:212 admin/class-roles.php:210 +#: admin/functions-help.php:46 admin/views/class-view-addons.php:173 +msgid "Overview" +msgstr "" + +#: _assets/class-caps.php:221 admin/class-roles.php:219 +msgid "Screen Content" +msgstr "" + +#: _assets/class-caps.php:230 admin/class-roles.php:228 +msgid "Available Actions" +msgstr "" + +#: _assets/class-caps.php:239 admin/class-roles.php:237 +msgid "Bulk Actions" +msgstr "" + +#: _assets/class-caps.php:258 +msgid "This screen provides access to all of your user caps. Caps are a method of grouping users. They are made up of capabilities (caps), which give permission to users to perform specific actions on the site." +msgstr "" + +#: _assets/class-caps.php:272 admin/class-roles.php:270 +msgid "You can customize the display of this screen‘s contents in a number of ways:" +msgstr "" + +#: _assets/class-caps.php:276 +msgid "You can hide/display columns based on your needs and decide how many caps to list per screen using the Screen Options tab." +msgstr "" + +#: _assets/class-caps.php:277 +msgid "You can filter the list of caps by types using the text links in the upper left. The default view is to show all caps." +msgstr "" + +#: _assets/class-caps.php:291 +msgid "Hovering over a row in the caps list will display action links that allow you to manage your cap. You can perform the following actions:" +msgstr "" + +#: _assets/class-caps.php:295 +msgid "Edit takes you to the editing screen for that cap. You can also reach that screen by clicking on the cap name." +msgstr "" + +#: _assets/class-caps.php:296 +msgid "Delete removes your cap from this list and permanently deletes it." +msgstr "" + +#: _assets/class-caps.php:297 +msgid "Clone copies the cap and takes you to the new cap screen to further edit it." +msgstr "" + +#: _assets/class-caps.php:298 +msgid "Users takes you to the users screen and lists the users that have that cap." +msgstr "" + +#: _assets/class-caps.php:312 +msgid "You can permanently delete multiple caps at once. Select the caps you want to act on using the checkboxes, then select the action you want to take from the Bulk Actions menu and click Apply." +msgstr "" + +#: _assets/class-manage-caps.php:89 +msgid "Edit Capability" +msgstr "" + +#: _assets/page-capabilities.php:69 +msgid "%s capability deleted. Not really. This is just a message to output until the functionality is working." +msgstr "" + +#: _assets/page-capabilities.php:73 +msgid "No capability selected to delete." +msgstr "" + +#: _assets/page-capabilities.php:96 +msgid "Add New" +msgstr "" + +#: _assets/page-capabilities.php:116 +msgid "Are you sure you want to delete this capability? This is a permanent action and cannot be undone." +msgstr "" + +#: admin/class-cap-control.php:118 admin/functions-admin.php:48 +msgid "Grant %s capability" +msgstr "" + +#: admin/class-cap-control.php:119 admin/functions-admin.php:49 +msgid "Deny %s capability" +msgstr "" + +#: admin/class-cap-tabs.php:165 +msgid "All" +msgstr "" + +#: admin/class-cap-tabs.php:194 +msgid "Edit Capabilities: %s" +msgstr "" + +#: admin/class-manage-roles.php:89 admin/class-role-edit.php:298 +msgid "Edit Role" +msgstr "" + +#: admin/class-manage-users.php:97 +msgid "Role added to selected users." +msgstr "" + +#: admin/class-manage-users.php:102 +msgid "Role removed from selected users." +msgstr "" + +#: admin/class-manage-users.php:106 +msgid "The current user’s role must have user editing capabilities." +msgstr "" + +#: admin/class-manage-users.php:107 +msgid "Role removed from other selected users." +msgstr "" + +#: admin/class-manage-users.php:162 admin/class-manage-users.php:237 +msgid "One of the selected users is not a member of this site." +msgstr "" + +#: admin/class-manage-users.php:326 admin/class-manage-users.php:330 +msgid "Add role…" +msgstr "" + +#: admin/class-manage-users.php:334 +msgid "Add" +msgstr "" + +#: admin/class-manage-users.php:337 admin/class-manage-users.php:341 +msgid "Remove role…" +msgstr "" + +#: admin/class-manage-users.php:345 +msgid "Remove" +msgstr "" + +#: admin/class-manage-users.php:382 +msgid "None" +msgstr "" + +#: admin/class-meta-box-content-permissions.php:115 +#: admin/views/class-view-general.php:63 admin/views/class-view-general.php:363 +msgid "Content Permissions" +msgstr "" + +#: admin/class-meta-box-content-permissions.php:180 +#: admin/views/class-view-general.php:76 +msgid "Error Message" +msgstr "" + +#: admin/class-meta-box-content-permissions.php:190 +msgid "Limit access to the content to users of the selected roles." +msgstr "" + +#: admin/class-meta-box-content-permissions.php:210 +msgid "If no roles are selected, everyone can view the content. The author, any users who can edit the content, and users with the %s capability can view the content regardless of role." +msgstr "" + +#: admin/class-meta-box-custom-cap.php:73 admin/functions-help.php:94 +msgid "Custom Capability" +msgstr "" + +#: admin/class-meta-box-custom-cap.php:90 +msgctxt "capability" +msgid "Add New" +msgstr "" + +#: admin/class-meta-box-publish-role.php:104 +msgid "%s User" +msgid_plural "%s Users" +msgstr[0] "" +msgstr[1] "" + +#: admin/class-meta-box-publish-role.php:110 +msgid "Users:" +msgstr "" + +#: admin/class-meta-box-publish-role.php:117 +msgid "Granted:" +msgstr "" + +#: admin/class-meta-box-publish-role.php:123 +msgid "Denied:" +msgstr "" + +#: admin/class-meta-box-publish-role.php:134 +msgctxt "delete role" +msgid "Delete" +msgstr "" + +#: admin/class-meta-box-publish-role.php:141 +msgid "Update" +msgstr "" + +#: admin/class-meta-box-publish-role.php:141 +msgid "Add Role" +msgstr "" + +#: admin/class-role-edit.php:99 +msgid "The requested role to edit does not exist." +msgstr "" + +#: admin/class-role-edit.php:226 +msgid "%s role updated." +msgstr "" + +#: admin/class-role-edit.php:230 +msgid "The %s role is not editable. This means that it is most likely added via another plugin for a special use or that you do not have permission to edit it." +msgstr "" + +#: admin/class-role-edit.php:234 +msgid "The %s role is typically the most important role on the site. Please take extreme caution that you do not inadvertently remove necessary capabilities." +msgstr "" + +#: admin/class-role-edit.php:238 admin/class-role-new.php:226 +msgid "The %s role has been created." +msgstr "" + +#: admin/class-role-edit.php:301 admin/class-roles.php:173 +msgctxt "role" +msgid "Add New" +msgstr "" + +#: admin/class-role-edit.php:320 admin/class-role-new.php:327 +#: admin/class-roles.php:67 admin/functions-help.php:62 +msgid "Role Name" +msgstr "" + +#: admin/class-role-edit.php:326 admin/class-role-new.php:333 +#: inc/class-widget-users.php:228 +msgid "Role:" +msgstr "" + +#: admin/class-role-list-table.php:232 +msgid "Default Role" +msgstr "" + +#: admin/class-role-list-table.php:236 +msgid "Your Role" +msgstr "" + +#: admin/class-role-list-table.php:370 +msgid "Clone" +msgstr "" + +#: admin/class-role-list-table.php:374 +msgid "Change Default" +msgstr "" + +#: admin/class-role-list-table.php:378 admin/class-roles.php:69 +#: inc/class-widget-users.php:59 inc/functions-cap-groups.php:121 +msgid "Users" +msgstr "" + +#: admin/class-role-list-table.php:428 +msgid "Has Users %s" +msgid_plural "Has Users %s" +msgstr[0] "" +msgstr[1] "" + +#: admin/class-role-list-table.php:433 +msgid "No Users %s" +msgid_plural "No Users %s" +msgstr[0] "" +msgstr[1] "" + +#: admin/class-role-list-table.php:438 +msgid "Editable %s" +msgid_plural "Editable %s" +msgstr[0] "" +msgstr[1] "" + +#: admin/class-role-list-table.php:443 +msgid "Uneditable %s" +msgid_plural "Uneditable %s" +msgstr[0] "" +msgstr[1] "" + +#: admin/class-role-new.php:109 admin/class-role-new.php:310 +msgid "Add New Role" +msgstr "" + +#: admin/class-role-new.php:235 +msgid "You must enter a valid role." +msgstr "" + +#: admin/class-role-new.php:239 +msgid "The %s role already exists." +msgstr "" + +#: admin/class-role-new.php:243 +msgid "You must enter a valid role name." +msgstr "" + +#: admin/class-role-new.php:310 +msgid "Clone Role" +msgstr "" + +#: admin/class-role-new.php:328 +msgid "%s Clone" +msgstr "" + +#: admin/class-role-new.php:328 +msgid "Enter role name" +msgstr "" + +#: admin/class-roles.php:70 +msgid "Granted" +msgstr "" + +#: admin/class-roles.php:71 +msgid "Denied" +msgstr "" + +#: admin/class-roles.php:112 +msgid "Selected roles deleted." +msgstr "" + +#: admin/class-roles.php:131 +msgid "%s role deleted." +msgstr "" + +#: admin/class-roles.php:256 +msgid "This screen provides access to all of your user roles. Roles are a method of grouping users. They are made up of capabilities (caps), which give permission to users to perform specific actions on the site." +msgstr "" + +#: admin/class-roles.php:274 +msgid "You can hide/display columns based on your needs and decide how many roles to list per screen using the Screen Options tab." +msgstr "" + +#: admin/class-roles.php:275 +msgid "You can filter the list of roles by types using the text links in the upper left. The default view is to show all roles." +msgstr "" + +#: admin/class-roles.php:289 +msgid "Hovering over a row in the roles list will display action links that allow you to manage your role. You can perform the following actions:" +msgstr "" + +#: admin/class-roles.php:293 +msgid "Edit takes you to the editing screen for that role. You can also reach that screen by clicking on the role name." +msgstr "" + +#: admin/class-roles.php:294 +msgid "Delete removes your role from this list and permanently deletes it." +msgstr "" + +#: admin/class-roles.php:295 +msgid "Clone copies the role and takes you to the new role screen to further edit it." +msgstr "" + +#: admin/class-roles.php:296 +msgid "Users takes you to the users screen and lists the users that have that role." +msgstr "" + +#: admin/class-roles.php:310 +msgid "You can permanently delete multiple roles at once. Select the roles you want to act on using the checkboxes, then select the action you want to take from the Bulk Actions menu and click Apply." +msgstr "" + +#: admin/class-settings.php:175 admin/class-settings.php:176 +#: admin/class-settings.php:276 +msgctxt "admin screen" +msgid "Members" +msgstr "" + +#: admin/class-user-edit.php:97 admin/class-user-new.php:109 +msgid "User Roles" +msgstr "" + +#: admin/functions-admin.php:47 +msgid "OK" +msgstr "" + +#: admin/functions-admin.php:50 +msgid "Are you sure you want to delete this role? This is a permanent action and cannot be undone." +msgstr "" + +#: admin/functions-help.php:23 +msgid "Documentation" +msgstr "" + +#: admin/functions-help.php:24 +msgid "Support Forums" +msgstr "" + +#: admin/functions-help.php:29 +msgid "For more information:" +msgstr "" + +#: admin/functions-help.php:78 +msgid "Edit Capabilities" +msgstr "" + +#: admin/functions-help.php:109 +msgid "This screen allows you to edit an individual role and its capabilities." +msgstr "" + +#: admin/functions-help.php:114 +msgid "Visit the %s page in the WordPress Codex to see a complete list of roles, capabilities, and their definitions." +msgstr "" + +#: admin/functions-help.php:115 admin/views/class-view-general.php:62 +msgid "Roles and Capabilities" +msgstr "" + +#: admin/functions-help.php:130 +msgid "The role name field allows you to enter a human-readable name for your role." +msgstr "" + +#: admin/functions-help.php:134 +msgid "The machine-readable version of the role appears below the name field, which you can edit. This can only have lowercase letters, numbers, or underscores." +msgstr "" + +#: admin/functions-help.php:148 +msgid "The capabilities edit box is made up of tabs that separate capabilities into groups. You may take the following actions for each capability:" +msgstr "" + +#: admin/functions-help.php:152 +msgid "Grant allows you to grant the role a capability." +msgstr "" + +#: admin/functions-help.php:153 +msgid "Deny allows you to explicitly deny the role a capability." +msgstr "" + +#: admin/functions-help.php:154 +msgid "You may also opt to neither grant nor deny the role a capability." +msgstr "" + +#: admin/functions-help.php:168 +msgid "The custom capability box allows you to create a custom capability for the role. After hitting the Add New button, it will add the capability to the Custom tab in the Edit Capabilities box." +msgstr "" + +#: admin/functions-settings.php:35 inc/functions-cap-groups.php:42 +msgid "General" +msgstr "" + +#: admin/functions-settings.php:46 +msgid "Add-Ons" +msgstr "" + +#: admin/functions-settings.php:57 +msgid "Help Fund Version 3.0" +msgstr "" + +#: admin/views/class-view-addons.php:65 +msgid "Go Pro With Members Add-ons" +msgstr "" + +#: admin/views/class-view-addons.php:68 +msgid "Take your membership site to the next level with add-ons. Pro users also enjoy live chat support and support forum access." +msgstr "" + +#: admin/views/class-view-addons.php:71 +msgid "Upgrade To Pro" +msgstr "" + +#: admin/views/class-view-addons.php:74 +msgid "Purchase Support-Only Membership" +msgstr "" + +#: admin/views/class-view-addons.php:99 +msgid "There are currently no add-ons to show. Please try again later." +msgstr "" + +#: admin/views/class-view-addons.php:182 +msgid "Download" +msgstr "" + +#: admin/views/class-view-addons.php:191 +msgid "Purchase" +msgstr "" + +#: admin/views/class-view-addons.php:210 +msgid "The Add-Ons screen allows you to view available add-ons for the Members plugin. You can download some plugins directly. Others may be available to purchase." +msgstr "" + +#: admin/views/class-view-addons.php:224 +msgid "Some plugins may be available for direct download. In such cases, you can click the download button to get a ZIP file of the plugin." +msgstr "" + +#: admin/views/class-view-addons.php:238 +msgid "Some add-ons may require purchase before downloading them. Clicking the purchase button will take you off-site to view the add-on in more detail." +msgstr "" + +#: admin/views/class-view-donate.php:50 +msgid "Donate Toward Future Development" +msgstr "" + +#: admin/views/class-view-donate.php:54 +msgid "The Members plugin needs funding to cover development costs toward version 3.0." +msgstr "" + +#: admin/views/class-view-donate.php:58 +msgid "Members itself will always remain free as long as I'm able to work on it. However, it is easily my largest and most complex plugin. A major update takes 100s of hours of development. If every user would donate just $1, it would fund fulltime development of this plugin for at least 3 years. Of course, it's not a reality that everyone is able donate. Pitching in any amount will help." +msgstr "" + +#: admin/views/class-view-donate.php:62 +msgid "Donate Via PayPal" +msgstr "" + +#: admin/views/class-view-donate.php:65 +msgid "Learn More" +msgstr "" + +#: admin/views/class-view-general.php:64 admin/views/class-view-general.php:372 +msgid "Sidebar Widgets" +msgstr "" + +#: admin/views/class-view-general.php:65 admin/views/class-view-general.php:381 +msgid "Private Site" +msgstr "" + +#: admin/views/class-view-general.php:70 +msgid "Role Manager" +msgstr "" + +#: admin/views/class-view-general.php:71 +msgid "Multiple User Roles" +msgstr "" + +#: admin/views/class-view-general.php:75 +msgid "Enable Permissions" +msgstr "" + +#: admin/views/class-view-general.php:79 +msgid "Login Widget" +msgstr "" + +#: admin/views/class-view-general.php:80 +msgid "Users Widget" +msgstr "" + +#: admin/views/class-view-general.php:83 +msgid "Enable Private Site" +msgstr "" + +#: admin/views/class-view-general.php:84 +msgid "REST API" +msgstr "" + +#: admin/views/class-view-general.php:85 +msgid "Disable Feed" +msgstr "" + +#: admin/views/class-view-general.php:86 +msgid "Feed Error Message" +msgstr "" + +#: admin/views/class-view-general.php:129 +msgid "Your roles and capabilities will not revert back to their previous settings after deactivating or uninstalling this plugin, so use this feature wisely." +msgstr "" + +#: admin/views/class-view-general.php:144 +msgid "Enable the role manager." +msgstr "" + +#: admin/views/class-view-general.php:162 +msgid "Denied capabilities should always overrule granted capabilities." +msgstr "" + +#: admin/views/class-view-general.php:169 +msgid "Show human-readable capabilities when possible." +msgstr "" + +#: admin/views/class-view-general.php:187 +msgid "Allow users to be assigned more than a single role." +msgstr "" + +#: admin/views/class-view-general.php:202 +msgid "Enable the content permissions feature." +msgstr "" + +#: admin/views/class-view-general.php:237 +msgid "Enable the login form widget." +msgstr "" + +#: admin/views/class-view-general.php:252 +msgid "Enable the users widget." +msgstr "" + +#: admin/views/class-view-general.php:267 +msgid "Redirect all logged-out users to the login page before allowing them to view the site." +msgstr "" + +#: admin/views/class-view-general.php:282 +msgid "Require authentication for access to the REST API." +msgstr "" + +#: admin/views/class-view-general.php:297 +msgid "Show error message for feed items." +msgstr "" + +#: admin/views/class-view-general.php:333 +msgid "Update Settings" +msgstr "" + +#: admin/views/class-view-general.php:354 +msgid "Role and Capabilities" +msgstr "" + +#: admin/views/class-view-general.php:400 +msgid "The role manager allows you to manage roles on your site by giving you the ability to create, edit, and delete any role. Note that changes to roles do not change settings for the Members plugin. You are literally changing data in your WordPress database. This plugin feature merely provides an interface for you to make these changes." +msgstr "" + +#: admin/views/class-view-general.php:404 +msgid "The multiple user roles feature allows you to assign more than one role to each user from the edit user screen." +msgstr "" + +#: admin/views/class-view-general.php:408 +msgid "Tick the checkbox for denied capabilities to always take precedence over granted capabilities when there is a conflict. This is only relevant when using multiple roles per user." +msgstr "" + +#: admin/views/class-view-general.php:412 +msgid "Tick the checkbox to show human-readable capabilities when possible. Note that custom capabilities and capabilities from third-party plugins will show the machine-readable capability name unless they are registered." +msgstr "" + +#: admin/views/class-view-general.php:426 +msgid "The content permissions features adds a meta box to the edit post screen that allows you to grant permissions for who can read the post content based on the user's role. Only users of roles with the %s capability will be able to use this component." +msgstr "" + +#: admin/views/class-view-general.php:440 +msgid "The sidebar widgets feature adds additional widgets for use in your theme's sidebars." +msgstr "" + +#: admin/views/class-view-general.php:454 +msgid "The private site feature redirects all users who are not logged into the site to the login page, creating an entirely private site. You may also replace your feed content with a custom error message." +msgstr "" + +#: inc/class-widget-login.php:44 +msgid "A widget that allows users to log into your site." +msgstr "" + +#: inc/class-widget-login.php:55 +msgid "Members: Login Form" +msgstr "" + +#: inc/class-widget-login.php:59 inc/class-widget-login.php:62 +msgid "Log In" +msgstr "" + +#: inc/class-widget-login.php:60 +msgid "Username" +msgstr "" + +#: inc/class-widget-login.php:61 +msgid "Password" +msgstr "" + +#: inc/class-widget-login.php:63 +msgid "Remember Me" +msgstr "" + +#: inc/class-widget-login.php:73 +msgid "Please log into the site." +msgstr "" + +#: inc/class-widget-login.php:74 +msgid "You are currently logged in." +msgstr "" + +#: inc/class-widget-login.php:209 inc/class-widget-users.php:208 +msgid "Title:" +msgstr "" + +#: inc/class-widget-login.php:213 +msgid "Username Label:" +msgstr "" + +#: inc/class-widget-login.php:217 +msgid "Password Label:" +msgstr "" + +#: inc/class-widget-login.php:221 +msgid "Log In Label:" +msgstr "" + +#: inc/class-widget-login.php:225 +msgid "Remember Me Label:" +msgstr "" + +#: inc/class-widget-login.php:233 +msgid "Username Value:" +msgstr "" + +#: inc/class-widget-login.php:237 +msgid "Username Field ID:" +msgstr "" + +#: inc/class-widget-login.php:241 +msgid "Remember Me Field ID:" +msgstr "" + +#: inc/class-widget-login.php:245 +msgid "Password Field ID:" +msgstr "" + +#: inc/class-widget-login.php:249 +msgid "Submit Button ID:" +msgstr "" + +#: inc/class-widget-login.php:260 +msgid "\"Remember me\" checkbox?" +msgstr "" + +#: inc/class-widget-login.php:266 +msgid "Check \"remember me\"?" +msgstr "" + +#: inc/class-widget-login.php:272 +msgid "Display avatar?" +msgstr "" + +#: inc/class-widget-login.php:276 +msgid "Logged out text:" +msgstr "" + +#: inc/class-widget-login.php:281 +msgid "Logged in text:" +msgstr "" + +#: inc/class-widget-users.php:44 +msgid "Provides the ability to list the users of the site." +msgstr "" + +#: inc/class-widget-users.php:55 +msgid "Members: Users" +msgstr "" + +#: inc/class-widget-users.php:185 +msgid "Ascending" +msgstr "" + +#: inc/class-widget-users.php:186 +msgid "Descending" +msgstr "" + +#: inc/class-widget-users.php:190 +msgid "Display Name" +msgstr "" + +#: inc/class-widget-users.php:191 +msgid "Email" +msgstr "" + +#: inc/class-widget-users.php:192 +msgid "ID" +msgstr "" + +#: inc/class-widget-users.php:193 +msgid "Nice Name" +msgstr "" + +#: inc/class-widget-users.php:194 +msgid "Post Count" +msgstr "" + +#: inc/class-widget-users.php:195 +msgid "Registered" +msgstr "" + +#: inc/class-widget-users.php:196 +msgid "URL" +msgstr "" + +#: inc/class-widget-users.php:197 +msgid "Login" +msgstr "" + +#: inc/class-widget-users.php:212 +msgid "Order By:" +msgstr "" + +#: inc/class-widget-users.php:220 +msgid "Order:" +msgstr "" + +#: inc/class-widget-users.php:237 +msgid "Limit:" +msgstr "" + +#: inc/class-widget-users.php:241 +msgid "Offset:" +msgstr "" + +#: inc/class-widget-users.php:249 +msgid "Include:" +msgstr "" + +#: inc/class-widget-users.php:253 +msgid "Exclude:" +msgstr "" + +#: inc/class-widget-users.php:257 +msgid "Search:" +msgstr "" + +#: inc/class-widget-users.php:261 +msgid "Meta Key:" +msgstr "" + +#: inc/class-widget-users.php:269 +msgid "Meta Value:" +msgstr "" + +#: inc/functions-cap-groups.php:92 +msgid "Taxonomies" +msgstr "" + +#: inc/functions-cap-groups.php:103 +msgid "Appearance" +msgstr "" + +#: inc/functions-cap-groups.php:112 +msgid "Plugins" +msgstr "" + +#: inc/functions-cap-groups.php:130 +msgid "Custom" +msgstr "" + +#: inc/functions-capabilities.php:57 +msgid "Edit Dashboard" +msgstr "" + +#: inc/functions-capabilities.php:58 +msgid "Edit Files" +msgstr "" + +#: inc/functions-capabilities.php:59 +msgid "Export" +msgstr "" + +#: inc/functions-capabilities.php:60 +msgid "Import" +msgstr "" + +#: inc/functions-capabilities.php:61 +msgid "Manage Links" +msgstr "" + +#: inc/functions-capabilities.php:62 +msgid "Manage Options" +msgstr "" + +#: inc/functions-capabilities.php:63 +msgid "Moderate Comments" +msgstr "" + +#: inc/functions-capabilities.php:64 +msgid "Read" +msgstr "" + +#: inc/functions-capabilities.php:65 +msgid "Unfiltered HTML" +msgstr "" + +#: inc/functions-capabilities.php:66 +msgid "Update Core" +msgstr "" + +#: inc/functions-capabilities.php:69 +msgid "Delete Others' Posts" +msgstr "" + +#: inc/functions-capabilities.php:70 +msgid "Delete Posts" +msgstr "" + +#: inc/functions-capabilities.php:71 +msgid "Delete Private Posts" +msgstr "" + +#: inc/functions-capabilities.php:72 +msgid "Delete Published Posts" +msgstr "" + +#: inc/functions-capabilities.php:73 +msgid "Edit Others' Posts" +msgstr "" + +#: inc/functions-capabilities.php:74 +msgid "Edit Posts" +msgstr "" + +#: inc/functions-capabilities.php:75 +msgid "Edit Private Posts" +msgstr "" + +#: inc/functions-capabilities.php:76 +msgid "Edit Published Posts" +msgstr "" + +#: inc/functions-capabilities.php:77 +msgid "Publish Posts" +msgstr "" + +#: inc/functions-capabilities.php:78 +msgid "Read Private Posts" +msgstr "" + +#: inc/functions-capabilities.php:81 +msgid "Delete Others' Pages" +msgstr "" + +#: inc/functions-capabilities.php:82 +msgid "Delete Pages" +msgstr "" + +#: inc/functions-capabilities.php:83 +msgid "Delete Private Pages" +msgstr "" + +#: inc/functions-capabilities.php:84 +msgid "Delete Published Pages" +msgstr "" + +#: inc/functions-capabilities.php:85 +msgid "Edit Others' Pages" +msgstr "" + +#: inc/functions-capabilities.php:86 +msgid "Edit Pages" +msgstr "" + +#: inc/functions-capabilities.php:87 +msgid "Edit Private Pages" +msgstr "" + +#: inc/functions-capabilities.php:88 +msgid "Edit Published Pages" +msgstr "" + +#: inc/functions-capabilities.php:89 +msgid "Publish Pages" +msgstr "" + +#: inc/functions-capabilities.php:90 +msgid "Read Private Pages" +msgstr "" + +#: inc/functions-capabilities.php:93 +msgid "Upload Files" +msgstr "" + +#: inc/functions-capabilities.php:96 +msgid "Manage Categories" +msgstr "" + +#: inc/functions-capabilities.php:99 +msgid "Delete Themes" +msgstr "" + +#: inc/functions-capabilities.php:100 +msgid "Edit Theme Options" +msgstr "" + +#: inc/functions-capabilities.php:101 +msgid "Edit Themes" +msgstr "" + +#: inc/functions-capabilities.php:102 +msgid "Install Themes" +msgstr "" + +#: inc/functions-capabilities.php:103 +msgid "Switch Themes" +msgstr "" + +#: inc/functions-capabilities.php:104 +msgid "Update Themes" +msgstr "" + +#: inc/functions-capabilities.php:107 +msgid "Activate Plugins" +msgstr "" + +#: inc/functions-capabilities.php:108 +msgid "Delete Plugins" +msgstr "" + +#: inc/functions-capabilities.php:109 +msgid "Edit Plugins" +msgstr "" + +#: inc/functions-capabilities.php:110 +msgid "Install Plugins" +msgstr "" + +#: inc/functions-capabilities.php:111 +msgid "Update Plugins" +msgstr "" + +#: inc/functions-capabilities.php:114 +msgid "Create Roles" +msgstr "" + +#: inc/functions-capabilities.php:115 +msgid "Create Users" +msgstr "" + +#: inc/functions-capabilities.php:116 +msgid "Delete Roles" +msgstr "" + +#: inc/functions-capabilities.php:117 +msgid "Delete Users" +msgstr "" + +#: inc/functions-capabilities.php:118 +msgid "Edit Roles" +msgstr "" + +#: inc/functions-capabilities.php:119 +msgid "Edit Users" +msgstr "" + +#: inc/functions-capabilities.php:120 +msgid "List Roles" +msgstr "" + +#: inc/functions-capabilities.php:121 +msgid "List Users" +msgstr "" + +#: inc/functions-capabilities.php:122 +msgid "Promote Users" +msgstr "" + +#: inc/functions-capabilities.php:123 +msgid "Remove Users" +msgstr "" + +#: inc/functions-capabilities.php:126 +msgid "Restrict Content" +msgstr "" + +#: inc/functions-capabilities.php:141 +msgid "Assign Categories" +msgstr "" + +#: inc/functions-capabilities.php:142 +msgid "Edit Categories" +msgstr "" + +#: inc/functions-capabilities.php:143 +msgid "Delete Categories" +msgstr "" + +#: inc/functions-capabilities.php:144 +msgid "Assign Post Tags" +msgstr "" + +#: inc/functions-capabilities.php:145 +msgid "Edit Post Tags" +msgstr "" + +#: inc/functions-capabilities.php:146 +msgid "Delete Post Tags" +msgstr "" + +#: inc/functions-capabilities.php:147 +msgid "Manage Post Tags" +msgstr "" + +#: inc/functions-options.php:134 +msgid "Sorry, but you do not have permission to view this content." +msgstr "" + +#: inc/functions-options.php:135 +msgid "You must be logged into the site to view this content." +msgstr "" + +#: inc/functions-private-site.php:167 +msgid "You are not currently logged in." +msgstr "" + +#: inc/functions-private-site.php:191 +msgid "You do not currently have access to the \"%s\" site. If you believe you should have access, please contact your network administrator." +msgstr "" + +#: inc/functions-private-site.php:198 +msgid "If you reached this page by accident and meant to visit one of your own sites, try one of the following links." +msgstr "" + +#: inc/functions-role-groups.php:42 +msgid "WordPress" +msgstr "" + +#: members.php:151 +msgid "Method does not exist." +msgstr "" + +#: members.php:323 +msgid "Members requires PHP version %1$s. You are running version %2$s. Please upgrade and try again." +msgstr "" +#. Plugin Name of the plugin/theme +msgid "Members" +msgstr "" + +#. Plugin URI of the plugin/theme +msgid "https://themehybrid.com/plugins/members" +msgstr "" + +#. Description of the plugin/theme +msgid "A user and role management plugin that puts you in full control of your site's permissions. This plugin allows you to edit your roles and their capabilities, clone existing roles, assign multiple roles per user, block post content, or even make your site completely private." +msgstr "" + +#. Author of the plugin/theme +msgid "Justin Tadlock" +msgstr "" + +#. Author URI of the plugin/theme +msgid "https://themehybrid.com" +msgstr "" diff --git a/languages/members.pot b/languages/members.pot deleted file mode 100644 index b360673..0000000 --- a/languages/members.pot +++ /dev/null @@ -1,830 +0,0 @@ -# Copyright (C) 2016 Members -# This file is distributed under the same license as the Members package. -msgid "" -msgstr "" -"Project-Id-Version: Members 1.1.1-dev\n" -"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/members\n" -"POT-Creation-Date: 2016-01-09 19:58:30+00:00\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" - -#: _assets/class-cap-new.php:64 _assets/class-cap-new.php:158 -msgid "Add New Capability" -msgstr "" - -#: _assets/class-cap-new.php:102 -msgid "Please input a valid capability." -msgstr "" - -#: _assets/class-cap-new.php:106 -msgid "The %s capability already exists." -msgstr "" - -#: _assets/class-cap-new.php:110 -msgid "Please select at least one role." -msgstr "" - -#: _assets/class-cap-new.php:123 -msgid "The %s capability has been created." -msgstr "" - -#: _assets/class-cap-new.php:172 admin/tmpl/cap-section.php:19 -#: admin/tmpl/cap-section.php:27 -msgid "Capability" -msgstr "" - -#: _assets/class-cap-new.php:178 -msgid "The capability should be unique and contain only alphanumeric characters and underscores." -msgstr "" - -#: _assets/class-cap-new.php:185 admin/class-manage-roles.php:84 -#: admin/class-manage-roles.php:90 admin/class-roles.php:168 -#: admin/class-user-edit.php:87 admin/functions-admin.php:151 -msgid "Roles" -msgstr "" - -#: _assets/class-cap-new.php:189 -msgid "Select at least one role. Because of the way capabilities work in WordPress, they can only exist if assigned to a role." -msgstr "" - -#: _assets/class-cap-new.php:209 -msgid "Add Capability" -msgstr "" - -#: _assets/class-capability-list-table.php:264 -#: _assets/class-capability-list-table.php:387 -#: admin/class-role-list-table.php:338 admin/class-role-list-table.php:463 -msgid "Delete" -msgstr "" - -#: _assets/class-capability-list-table.php:319 -#: admin/class-role-list-table.php:408 -msgid "All %s" -msgid_plural "All %s" -msgstr[0] "" -msgstr[1] "" - -#: _assets/class-capability-list-table.php:320 -#: admin/functions-role-groups.php:112 -msgid "Mine %s" -msgid_plural "Mine %s" -msgstr[0] "" -msgstr[1] "" - -#: _assets/class-capability-list-table.php:321 -#: admin/functions-role-groups.php:158 -msgid "WordPress %s" -msgid_plural "WordPress %s" -msgstr[0] "" -msgstr[1] "" - -#: _assets/class-capability-list-table.php:322 -msgid "Members %s" -msgid_plural "Members %s" -msgstr[0] "" -msgstr[1] "" - -#: _assets/class-capability-list-table.php:349 -msgid "View all roles" -msgstr "" - -#: _assets/class-capability-list-table.php:359 -msgid "Filter" -msgstr "" - -#: _assets/page-capabilities.php:45 _assets/page-capabilities.php:93 -#: admin/class-settings.php:133 -msgid "Capabilities" -msgstr "" - -#: _assets/page-capabilities.php:69 -msgid "%s capability deleted. Not really. This is just a message to output until the functionality is working." -msgstr "" - -#: _assets/page-capabilities.php:73 -msgid "No capability selected to delete." -msgstr "" - -#: _assets/page-capabilities.php:96 admin/class-meta-box-custom-cap.php:88 -#: admin/class-role-edit.php:260 admin/class-roles.php:171 -msgid "Add New" -msgstr "" - -#: _assets/page-capabilities.php:116 -msgid "Are you sure you want to delete this capability? This is a permanent action and cannot be undone." -msgstr "" - -#: admin/class-cap-control.php:115 admin/functions-admin.php:51 -msgid "Grant %s capability" -msgstr "" - -#: admin/class-cap-control.php:116 admin/functions-admin.php:52 -msgid "Deny %s capability" -msgstr "" - -#: admin/class-cap-tabs.php:174 -msgid "Edit Capabilities: %s" -msgstr "" - -#: admin/class-manage-roles.php:87 admin/class-role-edit.php:257 -msgid "Edit Role" -msgstr "" - -#: admin/class-meta-box-content-permissions.php:99 admin/class-settings.php:125 -#: admin/class-settings.php:398 -msgid "Content Permissions" -msgstr "" - -#: admin/class-meta-box-content-permissions.php:132 -msgid "Limit access to this post's content to users of the selected roles." -msgstr "" - -#: admin/class-meta-box-content-permissions.php:152 -msgid "If no roles are selected, everyone can view the content. The post author, any users who can edit this post, and users with the %s capability can view the content regardless of role." -msgstr "" - -#: admin/class-meta-box-content-permissions.php:156 -msgid "Custom error message:" -msgstr "" - -#: admin/class-meta-box-content-permissions.php:158 -msgid "Message shown to users that do not have permission to view the post." -msgstr "" - -#: admin/class-meta-box-custom-cap.php:71 admin/functions-help.php:94 -msgid "Custom Capability" -msgstr "" - -#: admin/class-meta-box-publish-role.php:65 admin/class-roles.php:66 -#: inc/functions-admin-bar.php:35 -msgid "Role" -msgstr "" - -#: admin/class-meta-box-publish-role.php:98 -msgid "Users:" -msgstr "" - -#: admin/class-meta-box-publish-role.php:104 -msgid "Granted:" -msgstr "" - -#: admin/class-meta-box-publish-role.php:110 -msgid "Denied:" -msgstr "" - -#: admin/class-meta-box-publish-role.php:121 -msgctxt "delete role" -msgid "Delete" -msgstr "" - -#: admin/class-meta-box-publish-role.php:128 -msgid "Update" -msgstr "" - -#: admin/class-meta-box-publish-role.php:128 -msgid "Add Role" -msgstr "" - -#: admin/class-role-edit.php:90 members.php:165 members.php:176 -msgid "Whoah, partner!" -msgstr "" - -#: admin/class-role-edit.php:97 -msgid "The requested role to edit does not exist." -msgstr "" - -#: admin/class-role-edit.php:189 -msgid "%s role updated." -msgstr "" - -#: admin/class-role-edit.php:193 -msgid "The %s role is not editable. This means that it is most likely added via another plugin for a special use or that you do not have permission to edit it." -msgstr "" - -#: admin/class-role-edit.php:197 admin/class-role-new.php:224 -msgid "The %s role has been created." -msgstr "" - -#: admin/class-role-edit.php:279 admin/class-role-new.php:325 -#: admin/class-roles.php:65 admin/functions-help.php:62 -msgid "Role Name" -msgstr "" - -#: admin/class-role-edit.php:285 admin/class-role-new.php:331 -#: inc/class-widget-users.php:227 -msgid "Role:" -msgstr "" - -#: admin/class-role-list-table.php:229 -msgid "Default Role" -msgstr "" - -#: admin/class-role-list-table.php:233 -msgid "Your Role" -msgstr "" - -#: admin/class-role-list-table.php:334 admin/class-role-new.php:333 -#: admin/functions-admin.php:49 -msgid "Edit" -msgstr "" - -#: admin/class-role-list-table.php:344 -msgid "View" -msgstr "" - -#: admin/class-role-list-table.php:349 -msgid "Clone" -msgstr "" - -#: admin/class-role-list-table.php:353 -msgid "Change Default" -msgstr "" - -#: admin/class-role-list-table.php:357 admin/class-roles.php:67 -#: admin/functions-cap-groups.php:193 inc/class-widget-users.php:57 -msgid "Users" -msgstr "" - -#: admin/class-role-new.php:107 admin/class-role-new.php:308 -msgid "Add New Role" -msgstr "" - -#: admin/class-role-new.php:233 -msgid "You must enter a valid role." -msgstr "" - -#: admin/class-role-new.php:237 -msgid "The %s role already exists." -msgstr "" - -#: admin/class-role-new.php:241 -msgid "You must enter a valid role name." -msgstr "" - -#: admin/class-role-new.php:308 -msgid "Clone Role" -msgstr "" - -#: admin/class-role-new.php:326 -msgid "%s Clone" -msgstr "" - -#: admin/class-role-new.php:326 -msgid "Enter role name" -msgstr "" - -#: admin/class-roles.php:68 -msgid "Granted" -msgstr "" - -#: admin/class-roles.php:69 -msgid "Denied" -msgstr "" - -#: admin/class-roles.php:110 -msgid "Selected roles deleted." -msgstr "" - -#: admin/class-roles.php:129 -msgid "%s role deleted." -msgstr "" - -#: admin/class-roles.php:208 admin/functions-help.php:46 -msgid "Overview" -msgstr "" - -#: admin/class-roles.php:217 -msgid "Screen Content" -msgstr "" - -#: admin/class-roles.php:226 -msgid "Available Actions" -msgstr "" - -#: admin/class-roles.php:235 -msgid "Bulk Actions" -msgstr "" - -#: admin/class-roles.php:254 -msgid "This screen provides access to all of your user roles. Roles are a method of grouping users. They are made up of capabilities (caps), which give permission to users to perform specific actions on the site." -msgstr "" - -#: admin/class-roles.php:268 -msgid "You can customize the display of this screen‘s contents in a number of ways:" -msgstr "" - -#: admin/class-roles.php:272 -msgid "You can hide/display columns based on your needs and decide how many roles to list per screen using the Screen Options tab." -msgstr "" - -#: admin/class-roles.php:273 -msgid "You can filter the list of roles by types using the text links in the upper left. The default view is to show all roles." -msgstr "" - -#: admin/class-roles.php:287 -msgid "Hovering over a row in the roles list will display action links that allow you to manage your role. You can perform the following actions:" -msgstr "" - -#: admin/class-roles.php:291 -msgid "Edit takes you to the editing screen for that role. You can also reach that screen by clicking on the role name." -msgstr "" - -#: admin/class-roles.php:292 -msgid "Delete removes your role from this list and permanently deletes it." -msgstr "" - -#: admin/class-roles.php:293 -msgid "Clone copies the role and takes you to the new role screen to further edit it." -msgstr "" - -#: admin/class-roles.php:294 -msgid "Users takes you to the users screen and lists the users that have that role." -msgstr "" - -#: admin/class-roles.php:308 -msgid "You can permanently delete multiple roles at once. Select the roles you want to act on using the checkboxes, then select the action you want to take from the Bulk Actions menu and click Apply." -msgstr "" - -#: admin/class-settings.php:70 admin/class-settings.php:362 -msgid "Members Settings" -msgstr "" - -#: admin/class-settings.php:71 -msgctxt "admin screen" -msgid "Members" -msgstr "" - -#: admin/class-settings.php:124 -msgid "Roles and Capabilities" -msgstr "" - -#: admin/class-settings.php:126 admin/class-settings.php:407 -msgid "Sidebar Widgets" -msgstr "" - -#: admin/class-settings.php:127 admin/class-settings.php:416 -msgid "Private Site" -msgstr "" - -#: admin/class-settings.php:132 -msgid "Role Manager" -msgstr "" - -#: admin/class-settings.php:134 -msgid "Multiple User Roles" -msgstr "" - -#: admin/class-settings.php:137 -msgid "Enable Permissions" -msgstr "" - -#: admin/class-settings.php:138 -msgid "Error Message" -msgstr "" - -#: admin/class-settings.php:141 -msgid "Login Widget" -msgstr "" - -#: admin/class-settings.php:142 -msgid "Users Widget" -msgstr "" - -#: admin/class-settings.php:145 -msgid "Enable Private Site" -msgstr "" - -#: admin/class-settings.php:146 -msgid "Disable Feed" -msgstr "" - -#: admin/class-settings.php:147 -msgid "Feed Error Message" -msgstr "" - -#: admin/class-settings.php:188 -msgid "Your roles and capabilities will not revert back to their previous settings after deactivating or uninstalling this plugin, so use this feature wisely." -msgstr "" - -#: admin/class-settings.php:203 -msgid "Enable the role manager." -msgstr "" - -#: admin/class-settings.php:218 -msgid "Denied capabilities should always overrule granted capabilities." -msgstr "" - -#: admin/class-settings.php:233 -msgid "Allow users to be assigned more than a single role." -msgstr "" - -#: admin/class-settings.php:248 -msgid "Enable the content permissions feature." -msgstr "" - -#: admin/class-settings.php:283 -msgid "Enable the login form widget." -msgstr "" - -#: admin/class-settings.php:298 -msgid "Enable the users widget." -msgstr "" - -#: admin/class-settings.php:313 -msgid "Redirect all logged-out users to the login page before allowing them to view the site." -msgstr "" - -#: admin/class-settings.php:328 -msgid "Show error message for feed items." -msgstr "" - -#: admin/class-settings.php:367 -msgid "Update Settings" -msgstr "" - -#: admin/class-settings.php:389 -msgid "Role and Capabilities" -msgstr "" - -#: admin/class-settings.php:422 admin/functions-help.php:23 -msgid "Documentation" -msgstr "" - -#: admin/class-settings.php:423 admin/functions-help.php:24 -msgid "Support Forums" -msgstr "" - -#: admin/class-settings.php:424 -msgid "Users, Roles, and Capabilities" -msgstr "" - -#: admin/class-settings.php:440 -msgid "The role manager allows you to manage roles on your site by giving you the ability to create, edit, and delete any role. Note that changes to roles do not change settings for the Members plugin. You are literally changing data in your WordPress database. This plugin feature merely provides an interface for you to make these changes." -msgstr "" - -#: admin/class-settings.php:444 -msgid "Tick the checkbox for denied capabilities to always take precedence over granted capabilities when there is a conflict. This is only relevant when using multiple roles per user." -msgstr "" - -#: admin/class-settings.php:448 -msgid "The multiple user roles feature allows you to assign more than one role to each user from the edit user screen." -msgstr "" - -#: admin/class-settings.php:462 -msgid "The content permissions features adds a meta box to the edit post screen that allows you to grant permissions for who can read the post content based on the user's role. Only users of roles with the %s capability will be able to use this component." -msgstr "" - -#: admin/class-settings.php:476 -msgid "The sidebar widgets feature adds additional widgets for use in your theme's sidebars." -msgstr "" - -#: admin/class-settings.php:490 -msgid "The private site feature redirects all users who are not logged into the site to the login page, creating an entirely private site. You may also replace your feed content with a custom error message." -msgstr "" - -#: admin/class-user-edit.php:92 -msgid "User Roles" -msgstr "" - -#: admin/functions-admin.php:50 -msgid "OK" -msgstr "" - -#: admin/functions-admin.php:53 -msgid "Are you sure you want to delete this role? This is a permanent action and cannot be undone." -msgstr "" - -#: admin/functions-admin.php:180 -msgid "None" -msgstr "" - -#: admin/functions-cap-groups.php:106 -msgid "All" -msgstr "" - -#: admin/functions-cap-groups.php:116 -msgid "General" -msgstr "" - -#: admin/functions-cap-groups.php:165 -msgid "Taxonomies" -msgstr "" - -#: admin/functions-cap-groups.php:175 -msgid "Appearance" -msgstr "" - -#: admin/functions-cap-groups.php:184 -msgid "Plugins" -msgstr "" - -#: admin/functions-cap-groups.php:202 -msgid "Custom" -msgstr "" - -#: admin/functions-help.php:29 -msgid "For more information:" -msgstr "" - -#: admin/functions-help.php:78 -msgid "Edit Capabilities" -msgstr "" - -#: admin/functions-help.php:109 -msgid "This screen allows you to edit an individual role and its capabilities." -msgstr "" - -#: admin/functions-help.php:123 -msgid "The role name field allows you to enter a human-readable name for your role." -msgstr "" - -#: admin/functions-help.php:127 -msgid "The machine-readable version of the role appears below the name field, which you can edit. This can only have lowercase letters, numbers, or underscores." -msgstr "" - -#: admin/functions-help.php:141 -msgid "The capabilities edit box is made up of tabs that separate capabilities into groups. You may take the following actions for each capability:" -msgstr "" - -#: admin/functions-help.php:145 -msgid "Grant allows you to grant the role a capability." -msgstr "" - -#: admin/functions-help.php:146 -msgid "Deny allows you to explicitly deny the role a capability." -msgstr "" - -#: admin/functions-help.php:147 -msgid "You may also opt to neither grant nor deny the role a capability." -msgstr "" - -#: admin/functions-help.php:161 -msgid "The custom capability box allows you to create a custom capability for the role. After hitting the Add New button, it will add the capability to the Custom tab in the Edit Capabilities box." -msgstr "" - -#: admin/functions-role-groups.php:111 -msgid "Mine" -msgstr "" - -#: admin/functions-role-groups.php:121 -msgid "Has Users" -msgstr "" - -#: admin/functions-role-groups.php:122 -msgid "Has Users %s" -msgid_plural "Has Users %s" -msgstr[0] "" -msgstr[1] "" - -#: admin/functions-role-groups.php:130 -msgid "No Users" -msgstr "" - -#: admin/functions-role-groups.php:131 -msgid "No Users %s" -msgid_plural "No Users %s" -msgstr[0] "" -msgstr[1] "" - -#: admin/functions-role-groups.php:139 -msgid "Editable" -msgstr "" - -#: admin/functions-role-groups.php:140 -msgid "Editable %s" -msgid_plural "Editable %s" -msgstr[0] "" -msgstr[1] "" - -#: admin/functions-role-groups.php:148 -msgid "Uneditable" -msgstr "" - -#: admin/functions-role-groups.php:149 -msgid "Uneditable %s" -msgid_plural "Uneditable %s" -msgstr[0] "" -msgstr[1] "" - -#: admin/functions-role-groups.php:157 -msgid "WordPress" -msgstr "" - -#: admin/tmpl/cap-section.php:20 admin/tmpl/cap-section.php:28 -msgid "Grant" -msgstr "" - -#: admin/tmpl/cap-section.php:21 admin/tmpl/cap-section.php:29 -msgid "Deny" -msgstr "" - -#: inc/class-widget-login.php:42 -msgid "A widget that allows users to log into your site." -msgstr "" - -#: inc/class-widget-login.php:53 -msgid "Members: Login Form" -msgstr "" - -#: inc/class-widget-login.php:57 inc/class-widget-login.php:60 -msgid "Log In" -msgstr "" - -#: inc/class-widget-login.php:58 -msgid "Username" -msgstr "" - -#: inc/class-widget-login.php:59 -msgid "Password" -msgstr "" - -#: inc/class-widget-login.php:61 -msgid "Remember Me" -msgstr "" - -#: inc/class-widget-login.php:71 -msgid "Please log into the site." -msgstr "" - -#: inc/class-widget-login.php:72 -msgid "You are currently logged in." -msgstr "" - -#: inc/class-widget-login.php:207 inc/class-widget-users.php:207 -msgid "Title:" -msgstr "" - -#: inc/class-widget-login.php:211 -msgid "Username Label:" -msgstr "" - -#: inc/class-widget-login.php:215 -msgid "Password Label:" -msgstr "" - -#: inc/class-widget-login.php:219 -msgid "Log In Label:" -msgstr "" - -#: inc/class-widget-login.php:223 -msgid "Remember Me Label:" -msgstr "" - -#: inc/class-widget-login.php:231 -msgid "Username Value:" -msgstr "" - -#: inc/class-widget-login.php:235 -msgid "Username Field ID:" -msgstr "" - -#: inc/class-widget-login.php:239 -msgid "Remember Me Field ID:" -msgstr "" - -#: inc/class-widget-login.php:243 -msgid "Password Field ID:" -msgstr "" - -#: inc/class-widget-login.php:247 -msgid "Submit Button ID:" -msgstr "" - -#: inc/class-widget-login.php:258 -msgid "\"Remember me\" checkbox?" -msgstr "" - -#: inc/class-widget-login.php:264 -msgid "Check \"remember me\"?" -msgstr "" - -#: inc/class-widget-login.php:270 -msgid "Display avatar?" -msgstr "" - -#: inc/class-widget-login.php:274 -msgid "Logged out text:" -msgstr "" - -#: inc/class-widget-login.php:279 -msgid "Logged in text:" -msgstr "" - -#: inc/class-widget-users.php:42 -msgid "Provides the ability to list the users of the site." -msgstr "" - -#: inc/class-widget-users.php:53 -msgid "Members: Users" -msgstr "" - -#: inc/class-widget-users.php:183 -msgid "Ascending" -msgstr "" - -#: inc/class-widget-users.php:184 -msgid "Descending" -msgstr "" - -#: inc/class-widget-users.php:188 -msgid "Display Name" -msgstr "" - -#: inc/class-widget-users.php:189 -msgid "Email" -msgstr "" - -#: inc/class-widget-users.php:190 -msgid "ID" -msgstr "" - -#: inc/class-widget-users.php:191 -msgid "Nice Name" -msgstr "" - -#: inc/class-widget-users.php:192 -msgid "Post Count" -msgstr "" - -#: inc/class-widget-users.php:193 -msgid "Registered" -msgstr "" - -#: inc/class-widget-users.php:194 -msgid "URL" -msgstr "" - -#: inc/class-widget-users.php:195 -msgid "Login" -msgstr "" - -#: inc/class-widget-users.php:211 -msgid "Order By:" -msgstr "" - -#: inc/class-widget-users.php:219 -msgid "Order:" -msgstr "" - -#: inc/class-widget-users.php:235 -msgid "Limit:" -msgstr "" - -#: inc/class-widget-users.php:239 -msgid "Offset:" -msgstr "" - -#: inc/class-widget-users.php:247 -msgid "Include:" -msgstr "" - -#: inc/class-widget-users.php:251 -msgid "Exclude:" -msgstr "" - -#: inc/class-widget-users.php:255 -msgid "Search:" -msgstr "" - -#: inc/class-widget-users.php:259 -msgid "Meta Key:" -msgstr "" - -#: inc/class-widget-users.php:267 -msgid "Meta Value:" -msgstr "" - -#: inc/functions-options.php:116 -msgid "Sorry, but you do not have permission to view this content." -msgstr "" - -#: inc/functions-options.php:117 -msgid "You must be logged into the site to view this content." -msgstr "" - -#: members.php:187 -msgid "Method does not exist." -msgstr "" -#. Plugin Name of the plugin/theme -msgid "Members" -msgstr "" - -#. Plugin URI of the plugin/theme -msgid "http://themehybrid.com/plugins/members" -msgstr "" - -#. Description of the plugin/theme -msgid "A user and role management plugin that puts you in full control of your site's permissions. This plugin allows you to edit your roles and their capabilities, clone existing roles, assign multiple roles per user, block post content, or even make your site completely private." -msgstr "" - -#. Author of the plugin/theme -msgid "Justin Tadlock" -msgstr "" - -#. Author URI of the plugin/theme -msgid "http://themehybrid.com" -msgstr "" diff --git a/members.php b/members.php index 04989e2..50fd99b 100644 --- a/members.php +++ b/members.php @@ -1,13 +1,13 @@ - * @copyright Copyright (c) 2009 - 2016, Justin Tadlock - * @link http://themehybrid.com/plugins/members + * @version 2.2.0 + * @author Justin Tadlock + * @copyright Copyright (c) 2009 - 2018, Justin Tadlock + * @link https://themehybrid.com/plugins/members * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ @@ -41,67 +41,31 @@ final class Members_Plugin { /** - * Plugin directory path. - * - * @since 1.0.0 - * @access public - * @var string - */ - public $dir_path = ''; - - /** - * Plugin directory URI. - * - * @since 1.0.0 - * @access public - * @var string - */ - public $dir_uri = ''; - - /** - * Plugin admin directory path. - * - * @since 1.0.0 - * @access public - * @var string - */ - public $admin_dir = ''; - - /** - * Plugin includes directory path. - * - * @since 1.0.0 - * @access public - * @var string - */ - public $inc_dir = ''; - - /** - * Plugin templates directory path. + * Minimum required PHP version. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ - public $templates_dir = ''; + private $php_version = '5.3.0'; /** - * Plugin CSS directory URI. + * Plugin directory path. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ - public $css_uri = ''; + public $dir = ''; /** - * Plugin JS directory URI. + * Plugin directory URI. * - * @since 1.0.0 + * @since 2.0.0 * @access public * @var string */ - public $js_uri = ''; + public $uri = ''; /** * User count of all roles. @@ -125,7 +89,7 @@ public static function get_instance() { static $instance = null; if ( is_null( $instance ) ) { - $instance = new Members_Plugin; + $instance = new self; $instance->setup(); $instance->includes(); $instance->setup_actions(); @@ -193,90 +157,90 @@ public function __call( $method = '', $args = array() ) { * Sets up globals. * * @since 1.0.0 - * @access public + * @access private * @return void */ private function setup() { // Main plugin directory path and URI. - $this->dir_path = trailingslashit( plugin_dir_path( __FILE__ ) ); - $this->dir_uri = trailingslashit( plugin_dir_url( __FILE__ ) ); - - // Plugin directory paths. - $this->inc_dir = trailingslashit( $this->dir_path . 'inc' ); - $this->admin_dir = trailingslashit( $this->dir_path . 'admin' ); - $this->templates_dir = trailingslashit( $this->dir_path . 'templates' ); - - // Plugin directory URIs. - $this->css_uri = trailingslashit( $this->dir_uri . 'css' ); - $this->js_uri = trailingslashit( $this->dir_uri . 'js' ); + $this->dir = trailingslashit( plugin_dir_path( __FILE__ ) ); + $this->uri = trailingslashit( plugin_dir_url( __FILE__ ) ); } /** * Loads files needed by the plugin. * * @since 1.0.0 - * @access public + * @access private * @return void */ private function includes() { + // Check if we meet the minimum PHP version. + if ( version_compare( PHP_VERSION, $this->php_version, '<' ) ) { + + // Add admin notice. + add_action( 'admin_notices', array( $this, 'php_admin_notice' ) ); + + // Bail. + return; + } + // Load class files. - require_once( $this->inc_dir . 'class-role.php' ); - require_once( $this->inc_dir . 'class-role-factory.php' ); + require_once( $this->dir . 'inc/class-capability.php' ); + require_once( $this->dir . 'inc/class-cap-group.php' ); + require_once( $this->dir . 'inc/class-registry.php' ); + require_once( $this->dir . 'inc/class-role-group.php' ); + require_once( $this->dir . 'inc/class-role.php' ); // Load includes files. - require_once( $this->inc_dir . 'functions.php' ); - require_once( $this->inc_dir . 'functions-admin-bar.php' ); - require_once( $this->inc_dir . 'functions-capabilities.php' ); - require_once( $this->inc_dir . 'functions-content-permissions.php' ); - require_once( $this->inc_dir . 'functions-deprecated.php' ); - require_once( $this->inc_dir . 'functions-options.php' ); - require_once( $this->inc_dir . 'functions-private-site.php' ); - require_once( $this->inc_dir . 'functions-roles.php' ); - require_once( $this->inc_dir . 'functions-shortcodes.php' ); - require_once( $this->inc_dir . 'functions-users.php' ); - require_once( $this->inc_dir . 'functions-widgets.php' ); + require_once( $this->dir . 'inc/functions.php' ); + require_once( $this->dir . 'inc/functions-admin-bar.php' ); + require_once( $this->dir . 'inc/functions-capabilities.php' ); + require_once( $this->dir . 'inc/functions-cap-groups.php' ); + require_once( $this->dir . 'inc/functions-content-permissions.php' ); + require_once( $this->dir . 'inc/functions-deprecated.php' ); + require_once( $this->dir . 'inc/functions-options.php' ); + require_once( $this->dir . 'inc/functions-private-site.php' ); + require_once( $this->dir . 'inc/functions-roles.php' ); + require_once( $this->dir . 'inc/functions-role-groups.php' ); + require_once( $this->dir . 'inc/functions-shortcodes.php' ); + require_once( $this->dir . 'inc/functions-users.php' ); + require_once( $this->dir . 'inc/functions-widgets.php' ); // Load template files. - require_once( $this->inc_dir . 'template.php' ); + require_once( $this->dir . 'inc/template.php' ); // Load admin files. if ( is_admin() ) { // General admin functions. - require_once( $this->admin_dir . 'functions-admin.php' ); - require_once( $this->admin_dir . 'functions-help.php' ); + require_once( $this->dir . 'admin/functions-admin.php' ); + require_once( $this->dir . 'admin/functions-help.php' ); // Plugin settings. - require_once( $this->admin_dir . 'class-settings.php' ); + require_once( $this->dir . 'admin/class-settings.php' ); - // Edit users. - require_once( $this->admin_dir . 'class-user-edit.php' ); + // User management. + require_once( $this->dir . 'admin/class-manage-users.php' ); + require_once( $this->dir . 'admin/class-user-edit.php' ); + require_once( $this->dir . 'admin/class-user-new.php' ); // Edit posts. - require_once( $this->admin_dir . 'class-meta-box-content-permissions.php' ); + require_once( $this->dir . 'admin/class-meta-box-content-permissions.php' ); // Role management. - require_once( $this->admin_dir . 'class-manage-roles.php' ); - require_once( $this->admin_dir . 'class-roles.php' ); - require_once( $this->admin_dir . 'class-role-edit.php' ); - require_once( $this->admin_dir . 'class-role-new.php' ); - require_once( $this->admin_dir . 'class-meta-box-publish-role.php' ); - require_once( $this->admin_dir . 'class-meta-box-custom-cap.php' ); - - // Role groups. - require_once( $this->admin_dir . 'class-role-group.php' ); - require_once( $this->admin_dir . 'class-role-group-factory.php' ); - require_once( $this->admin_dir . 'functions-role-groups.php' ); + require_once( $this->dir . 'admin/class-manage-roles.php' ); + require_once( $this->dir . 'admin/class-roles.php' ); + require_once( $this->dir . 'admin/class-role-edit.php' ); + require_once( $this->dir . 'admin/class-role-new.php' ); + require_once( $this->dir . 'admin/class-meta-box-publish-role.php' ); + require_once( $this->dir . 'admin/class-meta-box-custom-cap.php' ); // Edit capabilities tabs and groups. - require_once( $this->admin_dir . 'class-cap-tabs.php' ); - require_once( $this->admin_dir . 'class-cap-section.php' ); - require_once( $this->admin_dir . 'class-cap-control.php' ); - require_once( $this->admin_dir . 'class-cap-group.php' ); - require_once( $this->admin_dir . 'class-cap-group-factory.php' ); - require_once( $this->admin_dir . 'functions-cap-groups.php' ); + require_once( $this->dir . 'admin/class-cap-tabs.php' ); + require_once( $this->dir . 'admin/class-cap-section.php' ); + require_once( $this->dir . 'admin/class-cap-control.php' ); } } @@ -284,7 +248,7 @@ private function includes() { * Sets up main plugin actions and filters. * * @since 1.0.0 - * @access public + * @access private * @return void */ private function setup_actions() { @@ -304,7 +268,8 @@ private function setup_actions() { * @return void */ public function i18n() { - load_plugin_textdomain( 'members', false, trailingslashit( dirname( plugin_basename( __FILE__ ) ) ). 'languages' ); + + load_plugin_textdomain( 'members', false, trailingslashit( dirname( plugin_basename( __FILE__ ) ) ) . 'lang' ); } /** @@ -316,19 +281,70 @@ public function i18n() { */ public function activation() { + // Check PHP version requirements. + if ( version_compare( PHP_VERSION, $this->php_version, '<' ) ) { + + // Make sure the plugin is deactivated. + deactivate_plugins( plugin_basename( __FILE__ ) ); + + // Add an error message and die. + wp_die( $this->get_min_php_message() ); + } + // Get the administrator role. $role = get_role( 'administrator' ); // If the administrator role exists, add required capabilities for the plugin. if ( ! empty( $role ) ) { - $role->add_cap( 'list_roles' ); // View roles in backend. - $role->add_cap( 'create_roles' ); // Create new roles. - $role->add_cap( 'delete_roles' ); // Delete existing roles. - $role->add_cap( 'edit_roles' ); // Edit existing roles/caps. $role->add_cap( 'restrict_content' ); // Edit per-post content permissions. + $role->add_cap( 'list_roles' ); // View roles in backend. + + // Do not allow administrators to edit, create, or delete roles + // in a multisite setup. Super admins should assign these manually. + if ( ! is_multisite() ) { + $role->add_cap( 'create_roles' ); // Create new roles. + $role->add_cap( 'delete_roles' ); // Delete existing roles. + $role->add_cap( 'edit_roles' ); // Edit existing roles/caps. + } } } + + /** + * Returns a message noting the minimum version of PHP required. + * + * @since 2.0.1 + * @access private + * @return void + */ + private function get_min_php_message() { + + return sprintf( + __( 'Members requires PHP version %1$s. You are running version %2$s. Please upgrade and try again.', 'members' ), + $this->php_version, + PHP_VERSION + ); + } + + /** + * Outputs the admin notice that the user needs to upgrade their PHP version. It also + * auto-deactivates the plugin. + * + * @since 2.0.1 + * @access public + * @return void + */ + public function php_admin_notice() { + + // Output notice. + printf( + '

    %s

    ', + esc_html( $this->get_min_php_message() ) + ); + + // Make sure the plugin is deactivated. + deactivate_plugins( plugin_basename( __FILE__ ) ); + } } /** diff --git a/readme.md b/readme.md index e5388ce..3ebb57a 100644 --- a/readme.md +++ b/readme.md @@ -17,13 +17,13 @@ It puts you in control over permissions on your site by providing a user interfa ## Professional Support -If you need professional plugin support from me, the plugin author, you can access the support forums at [Theme Hybrid](http://themehybrid.com/board/topics), which is a professional WordPress help/support site where I handle support for all my plugins and themes for a community of 60,000+ users (and growing). +If you need professional plugin support from me, the plugin author, you can access the support forums at [Theme Hybrid](https://themehybrid.com/board/topics), which is a professional WordPress help/support site where I handle support for all my plugins and themes for a community of 75,000+ users (and growing). ## Copyright and License This project is licensed under the [GNU GPL](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html), version 2 or later. -2009 – 2016 © [Justin Tadlock](http://justintadlock.com). +2009 – 2018 © [Justin Tadlock](http://justintadlock.com). ## Documentation @@ -67,15 +67,43 @@ Every capability can have one of three "states" for a role. The role can be *gr * **Denying** a capability means that the role's users are explicitly denied permission. * A role that is neither granted nor denied a capability simply doesn't have that capability. -**Note #1:** If you were using a pre-1.0.0 version of Members, the concept of denied capabilities was not built in. In those versions, you could only grant or remove a capability. +**Note:** When assigning multiple roles to a single user that have a conflicting capability (e.g., granted `publish_posts` and denied `published_posts` cap), it's best to enable the denied capabilities override via the Members Settings screen. This will consistently make sure that denied capabilities always overrule granted capabilities. With this setting disabled, WordPress will decide based on the *last* role given to the user, which can mean for extremely inconsistent behavior depending on the roles a user has. -**Note #2:** When assigning multiple roles to a single user that have a conflicting capability (e.g., granted `publish_posts` and denied `published_posts` cap), it's best to enable the denied capabilities override via the Members Settings screen. This will consistently make sure that denied capabilities always overrule granted capabilities. With this setting disabled, WordPress will decide based on the *last* role given to the user, which can mean for extremely inconsistent behavior depending on the roles a user has. +#### How denied capabilities work + +Suppose the **Super** role is *granted* these capabilities: + +* `edit_posts` + +Then, suppose the **Duper** role is *granted* these capabilities: + +- `publish_posts` +- `edit_products` + +Now, further suppose **User A** has the **Super** role because you want them to edit posts. However, you also want **User A** to be able to edit products so you assign them the **Duper** role. Suddenly, **User A** is *granted* the following capabilities: + +- `edit_posts` +- `publish_posts` +- `edit_products` + +For whatever reason you don't ever want users with the **Super** role to be able to publish posts. Now you have a problem. One way to solve this is to create a third role with just the caps that you want and give that single role to **User A**. However, that becomes cumbersome on larger sites with many roles. + +Instead, you could explicitly *deny* the publish posts capability to the **Super** role. When you do that, **User A** is only *granted* the following capabilities: + +- `edit_posts` +- `edit_products` + +And is denied the following capabilities: + +- `publish_posts` ### Multiple user roles You can assign a user more than one role by going to that edit user screen in the admin and locating the "Roles" section. There will be a checkbox for every role. -Note that the roles dropdown on the "Users" screen in the admin will overwrite all roles with a single role. So, you'll need to edit the individual users to give them multiple roles. +You can also multiple roles to a user from the add new user screen. + +On the "Users" screen in the admin, you can bulk add or remove single roles from multiple users. ### Content permissions feature @@ -97,16 +125,29 @@ The `[members_access]` shortcode is for hiding content from particular roles and [members_access role="editor"]Hide this content from everyone but editors.[/members_access] +The plugin accepts the following parameters (mixing and matching won't work): + +* `role` - A single or comma-separated list of roles. +* `capability` - A single or comma-separated list of capabilities. +* `user_name` - A single or comma-separated list of usernames. +* `user_id` - A single or comma-separated list of user IDs. +* `user_email` - A single or comma-separated list of user email addresses. + **Parameters:** * `capability`: A capability that has been assigned to a role. * `role`: A user role from WordPress or one that you've created. +* `operator`: Accepts `!` to negate the role or capability. Note that `capability` and `role` parameters aren't used in conjunction. The code first checks for the capability (if input) then checks for the role (if input). To check for multiple capabilities or multiple roles, simply add a comma between each capability/role. For example, the following code checks for an editor or administrator: - [members_access role="administrator,editor"]Hide this content from everyone but administrators and editors.[/members_access] + [members_access role="administrator,editor"]Show this content to administrators or editors only.[/members_access] + +To check that the user does not have a role: + + [members_access role="administrator" operator="!"]Show this content to anyone who is not an administrator.[/members_access] #### [members_logged_in] @@ -116,6 +157,12 @@ The `[members_logged_in]` shortcode should be used to check if a user is current This shortcode has no parameters. +##### [members_not_logged_in] + +The `[members_not_logged_in]` shortcode should be used to show content to users who are not logged into the site. If the user is logged in, the content will be hidden. + + [members_not_logged_in]This content is only shown to logged-out visitors.[/members_not_logged_in] + #### [members_login_form] The `[members_login_form]` shortcode is used to show a login form on the page. @@ -173,3 +220,52 @@ Some plugins and themes might rely on the old user level system in WordPress. T By default, the levels aren't shown. They still exist, but are tucked away behind the scenes. While not recommended, if you need to control who has what level (levels are just capabilities), add this to your plugin or your theme's `functions.php`: add_filter( 'members_remove_old_levels', '__return_false' ); + +### Registering capabilities + +If you're a plugin developer with custom capabilities, beginning with version 2.0.0 of Members, you can register your capabilities with Members. Essentially, this allows users to see your capabilities in a nicely-formatted, human-readable form (e.g., `Publish Posts` instead of `publish_posts`). This also means that it can be translated so that it's easier to understand for users who do not read English. + + add_action( 'members_register_caps', 'th_register_caps' ); + + function th_register_caps() { + + members_register_cap( + 'your_cap_name', + array( + 'label' => __( 'Your Capability Label', 'example-textdomain' ), + 'group' => 'example' + ) + ); + } + +The `group` argument is not required, but will allow you to assign the capability to a cap group. + +### Registering cap groups + +Members groups capabilities so that users can more easily find them when editing roles. If your plugin has multiple capabilities, you should consider creating a custom cap group. + + add_action( 'members_register_cap_groups', 'th_register_cap_groups' ); + + function th_register_cap_groups() { + + members_register_cap_group( + 'your_group_name', + array( + 'label' => __( 'Your Group Label', 'example-textdomain' ), + 'caps' => array(), + 'icon' => 'dashicons-admin-generic', + 'priority' => 10 + ) + ); + } + +The arguments for the array are: + +* `label` - An internationalized text label for your group. +* `caps` - An array of initial capabilities to add to your group. +* `icon` - The name of one of core WP's [dashicons](https://developer.wordpress.org/resource/dashicons/) or a custom class (would need to be styled by your plugin in this case). +* `priority` - The priority of your group compared to other groups. `10` is the default. + +_Note that custom post types are automatically registered as groups with Members. So, if you want to do something custom with that, you simply need to unregister the group before registering your own._ + + members_unregister_cap_group( "type-{$post_type}" ); diff --git a/readme.txt b/readme.txt index 4a23997..e5b3c7f 100644 --- a/readme.txt +++ b/readme.txt @@ -1,22 +1,26 @@ === Members === Contributors: greenshady -Donate link: http://themehybrid.com/donate -Tags: admin, role, roles, member, members, profile, shortcode, user, users, widget, widgets -Requires at least: 4.3 -Stable tag: 1.1.2 +Donate link: https://themehybrid.com/plugins/members#donate +Tags: capabilities, roles, members, users +Requires at least: 4.7 +Tested up to: 5.2 +Requires PHP: 5.6 +Stable tag: 2.2.0 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html The most powerful user, role, and capability management plugin for WordPress. == Description == -Members is a plugin that extends your control over your blog. It's a user, role, and capability management plugin that was created to make WordPress a more powerful CMS. +Members is a plugin that extends your control over your blog. It's a user, role, and capability editor plugin that was created to make WordPress a more powerful CMS. It puts you in control over permissions on your site by providing a user interface (UI) for WordPress' powerful role and cap system, which is traditionally only available to developers who know how to code this by hand. ### Plugin Features -* **Role Manager:** Allows you to edit, create, and delete roles as well as capabilities for these roles. +* **Role Editor:** Allows you to edit, create, and delete roles as well as capabilities for these roles. * **Multiple User Roles:** Give one, two, or even more roles to any user. * **Explicitly Deny Capabilities:** Deny specific caps to specific user roles. * **Clone Roles:** Build a new role by cloning an existing role. @@ -26,23 +30,23 @@ It puts you in control over permissions on your site by providing a user interfa * **Private Site:** You can make your site and its feed completely private if you want. * **Plugin Integration:** Members is highly recommended by other WordPress developers. Many existing plugins integrate their custom roles and caps directly into it. -For more info, vist the [Members plugin home page](http://themehybrid.com/plugins/members). +For more info, visit the [Members plugin home page](https://themehybrid.com/plugins/members). ### Like this plugin? The Members plugin is a massive project with 1,000s of lines of code to maintain. A major update can take weeks or months of work. I don't make any money directly from this plugin while other, similar plugins charge substantial fees to even download them or get updates. Please consider helping the cause by: -* [Making a donation](http://themehybrid.com/donate). -* [Signing up at my site](http://themehybrid.com/club). +* [Making a donation](https://themehybrid.com/plugins/members#donate). +* [Signing up at my site](https://themehybrid.com/plugins/members). * [Rating the plugin](https://wordpress.org/support/view/plugin-reviews/members?rate=5#postform). ### Professional Support -If you need professional plugin support from me, the plugin author, you can access the support forums at [Theme Hybrid](http://themehybrid.com/board/topics), which is a professional WordPress help/support site where I handle support for all my plugins and themes for a community of 60,000+ users (and growing). +If you need professional plugin support from me, the plugin author, you can access the support forums at [Theme Hybrid](https://themehybrid.com/board/topics), which is a professional WordPress help/support site where I handle support for all my plugins and themes for a community of 75,000+ users (and growing). ### Plugin Development -If you're a theme author, plugin author, or just a code hobbyist, you can follow the development of this plugin on it's [GitHub repository](https://github.com/justintadlock/members). +If you're a theme author, plugin author, or just a code hobbyist, you can follow the development of this plugin on it's [GitHub repository](https://github.com/justintadlock/members). == Installation == @@ -66,17 +70,29 @@ Most things should be fairly straightforward, but I've included an in-depth guid You can also [view the readme](https://github.com/justintadlock/members/blob/master/readme.md) online. +### Minimum PHP requirements. + +Since version 2.1.0 of Members, PHP 5.6+ is a soft requirement to use the plugin. The plugin will still work on PHP 5.3+, but it is not recommended. + +When Members version 3.0.0 is released, PHP 5.6+ will be a hard requirement and won't work on older versions of PHP. + ### I can't access the "Role Manager" features. When the plugin is first activated, it runs a script that sets specific capabilities to the "Administrator" role on your site that grants you access to this feature. So, you must be logged in with the administrator account to access the role manager. If, for some reason, you do have the administrator role and the role manager is still inaccessible to you, deactivate the plugin. Then, reactivate it. +### On multisite, why can't administrators cannot manage roles? + +If you have a multisite installation, only Super Admins can create, edit, and delete roles by default. This is a security measure to make sure that you absolutely trust sub-site admins to make these types of changes to roles. If you're certain you want to allow this, add the Create Roles (`create_roles`), Edit Roles (`edit_roles`), and/or Delete Roles (`delete_roles`) capabilities to the role on each sub-site where you want to allow this. + +_Note: This change was made in version 2.0.2 and has no effect on existing installs of Members on existing sub-sites._ + ### Help! I've locked myself out of my site! Please read the documentation for the plugin before actually using it, especially a plugin that controls permissions for your site. I cannot stress this enough. This is a powerful plugin that allows you to make direct changes to roles and capabilities in the database. -You'll need to stop by my [support forums](http://themehybrid.com/board/topics) to see if we can get your site fixed if you managed to lock yourself out. I know that this can be a bit can be a bit scary, but it's not that tough to fix with a little custom code. +You'll need to stop by my [support forums](https://themehybrid.com/board/topics) to see if we can get your site fixed if you managed to lock yourself out. I know that this can be a bit can be a bit scary, but it's not that tough to fix with a little custom code. == Screenshots == @@ -86,6 +102,10 @@ You'll need to stop by my [support forums](http://themehybrid.com/board/topics) 4. Plugin settings screen 5. Select multiple roles per user (edit user screen) +== Upgrade Notice == + +If upgrading from a version prior to 2.0.0, please note that the plugin now requires PHP 5.3.0 or later. + == Changelog == -The change log is located in the `changelog.md` file in the plugin folder. You may also [view the change log](https://github.com/justintadlock/members/blob/master/changelog.md) online. \ No newline at end of file +The change log is located in the `changelog.md` file in the plugin folder. You may also [view the change log](https://github.com/justintadlock/members/blob/master/changelog.md) online. diff --git a/screenshot-1.png b/screenshot-1.png index 217dab5..17b1b35 100644 Binary files a/screenshot-1.png and b/screenshot-1.png differ diff --git a/screenshot-2.png b/screenshot-2.png index dcbc174..d47ee56 100644 Binary files a/screenshot-2.png and b/screenshot-2.png differ diff --git a/screenshot-3.png b/screenshot-3.png index cf7600d..5b7b4bc 100644 Binary files a/screenshot-3.png and b/screenshot-3.png differ diff --git a/screenshot-4.png b/screenshot-4.png index fe394b8..1cd005f 100644 Binary files a/screenshot-4.png and b/screenshot-4.png differ diff --git a/screenshot-5.png b/screenshot-5.png index 1a5e973..e1aa9bd 100644 Binary files a/screenshot-5.png and b/screenshot-5.png differ