├── admin ├── class-addon.php ├── class-cap-control.php ├── class-cap-section.php ├── class-cap-tabs.php ├── class-manage-roles.php ├── class-manage-users.php ├── class-meta-box-content-permissions.php ├── class-meta-box-custom-cap.php ├── class-meta-box-publish-role.php ├── class-role-edit.php ├── class-role-list-table.php ├── class-role-new.php ├── class-roles.php ├── class-settings.php ├── class-user-edit.php ├── class-user-new.php ├── config │ └── addons.php ├── functions-addons.php ├── functions-admin.php ├── functions-help.php ├── functions-settings.php ├── tmpl │ ├── cap-control.php │ └── cap-section.php └── views │ ├── class-view-addons.php │ ├── class-view-donate.php │ ├── class-view-general.php │ └── class-view.php ├── changelog.md ├── composer.json ├── contributing.md ├── css ├── admin.css └── admin.min.css ├── img ├── icon-addon.png ├── members-admin-access.svg ├── members-block-permissions.svg ├── members-core-create-caps.svg ├── members-privacy-caps.svg ├── members-role-hierarchy.svg ├── members-role-levels.svg └── members.svg ├── inc ├── class-cap-group.php ├── class-capability.php ├── class-registry.php ├── class-role-group.php ├── class-role.php ├── class-widget-login.php ├── class-widget-users.php ├── functions-admin-bar.php ├── functions-cap-groups.php ├── functions-capabilities.php ├── functions-content-permissions.php ├── functions-deprecated.php ├── functions-options.php ├── functions-private-site.php ├── functions-role-groups.php ├── functions-roles.php ├── functions-shortcodes.php ├── functions-users.php ├── functions-widgets.php ├── functions.php └── template.php ├── js ├── edit-post.js ├── edit-post.min.js ├── edit-role.js ├── edit-role.min.js ├── settings.js └── settings.min.js ├── lang └── members.pot ├── license.md ├── members.php ├── readme.md ├── readme.txt ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── screenshot-4.png ├── screenshot-5.png └── templates └── comments.php /admin/class-addon.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members; 14 | 15 | /** 16 | * Add-on object class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Addon { 22 | 23 | /** 24 | * Name/ID for the addon. 25 | * 26 | * @since 2.0.0 27 | * @access protected 28 | * @var string 29 | */ 30 | public $name = ''; 31 | 32 | /** 33 | * Title of the add-on. 34 | * 35 | * @since 2.0.0 36 | * @access protected 37 | * @var string 38 | */ 39 | public $title = ''; 40 | 41 | /** 42 | * Short description of the add-on. 43 | * 44 | * @since 2.0.0 45 | * @access protected 46 | * @var string 47 | */ 48 | public $excerpt = ''; 49 | 50 | /** 51 | * URL where the add-on can be found. 52 | * 53 | * @since 2.0.0 54 | * @access protected 55 | * @var string 56 | */ 57 | public $url = 'https://themehybrid.com/plugins/members'; 58 | 59 | /** 60 | * Add-on ZIP file URL. 61 | * 62 | * @since 2.0.0 63 | * @access protected 64 | * @var string 65 | */ 66 | public $download_url = ''; 67 | 68 | /** 69 | * Alternate purchase URL. 70 | * 71 | * @since 2.0.0 72 | * @access protected 73 | * @var string 74 | */ 75 | public $purchase_url = ''; 76 | 77 | /** 78 | * URL for a 128x128 (size used by WordPress.org) icon image. 79 | * 80 | * @since 2.0.0 81 | * @access protected 82 | * @var string 83 | */ 84 | public $icon_url = ''; 85 | 86 | /** 87 | * Add-on plugin's author URL. 88 | * 89 | * @since 2.0.0 90 | * @access protected 91 | * @var string 92 | */ 93 | public $author_url = ''; 94 | 95 | /** 96 | * Add-on plugin's author display name. 97 | * 98 | * @since 2.0.0 99 | * @access protected 100 | * @var string 101 | */ 102 | public $author_name = ''; 103 | 104 | /** 105 | * Rating for the add-on. This is the total rating based on a 5-star rating system. 106 | * It will be divided by the rating count, so both must be supplied. 107 | * 108 | * @since 2.0.0 109 | * @access protected 110 | * @var int 111 | */ 112 | public $rating = ''; 113 | 114 | /** 115 | * Number of ratings. 116 | * 117 | * @since 2.0.0 118 | * @access protected 119 | * @var int 120 | */ 121 | public $rating_count = 0; 122 | 123 | /** 124 | * Number of active installs. Note that this will be displayed with a `+` at 125 | * the end, such as `100,000+`. Exact counts are necessary. Just a round number. 126 | * 127 | * @since 2.0.0 128 | * @access protected 129 | * @var string 130 | */ 131 | public $install_count = 0; 132 | 133 | /** 134 | * Magic method to use in case someone tries to output the object as a string. 135 | * We'll just return the name. 136 | * 137 | * @since 2.0.0 138 | * @access public 139 | * @return string 140 | */ 141 | public function __toString() { 142 | return $this->name; 143 | } 144 | 145 | /** 146 | * Register a new object. 147 | * 148 | * @since 2.0.0 149 | * @access public 150 | * @param string $name 151 | * @param array $args { 152 | * @type string $label Internationalized text label. 153 | * @type string $icon Dashicon icon in the form of `dashicons-icon-name`. 154 | * @type array $caps Array of capabilities in the addon. 155 | * @type bool $merge_added Whether to merge this caps into the added caps array. 156 | * @type bool $diff_added Whether to remove previously-added caps from this addon. 157 | * } 158 | * @return void 159 | */ 160 | public function __construct( $name, $args = array() ) { 161 | 162 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 163 | 164 | if ( isset( $args[ $key ] ) ) 165 | $this->$key = $args[ $key ]; 166 | } 167 | 168 | $this->name = sanitize_key( $name ); 169 | 170 | if ( ! $this->icon_url ) 171 | $this->icon_url = members_plugin()->uri . 'img/icon-addon.png'; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /admin/class-cap-control.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Cap control class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Cap_Control { 22 | 23 | /** 24 | * Stores the cap tabs object. 25 | * 26 | * @see Members_Cap_Tabs 27 | * @since 2.0.0 28 | * @access public 29 | * @var object 30 | */ 31 | public $manager; 32 | 33 | /** 34 | * Name of the capability the control is for. 35 | * 36 | * @since 2.0.0 37 | * @access public 38 | * @var string 39 | */ 40 | public $cap = ''; 41 | 42 | /** 43 | * ID of the section the control is for. 44 | * 45 | * @since 2.0.0 46 | * @access public 47 | * @var string 48 | */ 49 | public $section = ''; 50 | 51 | /** 52 | * Array of data to pass as a json object to the Underscore template. 53 | * 54 | * @since 2.0.0 55 | * @access public 56 | * @var array 57 | */ 58 | public $json = array(); 59 | 60 | /** 61 | * Creates a new control object. 62 | * 63 | * @since 2.0.0 64 | * @access public 65 | * @param object $manager 66 | * @param string $cap 67 | * @param array $args 68 | * @return void 69 | */ 70 | public function __construct( $manager, $cap, $args = array() ) { 71 | 72 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 73 | 74 | if ( isset( $args[ $key ] ) ) 75 | $this->$key = $args[ $key ]; 76 | } 77 | 78 | $this->manager = $manager; 79 | $this->cap = $cap; 80 | } 81 | 82 | /** 83 | * Returns the json array. 84 | * 85 | * @since 2.0.0 86 | * @access public 87 | * @return array 88 | */ 89 | public function json() { 90 | $this->to_json(); 91 | return $this->json; 92 | } 93 | 94 | /** 95 | * Adds custom data to the json array. This data is passed to the Underscore template. 96 | * 97 | * @since 2.0.0 98 | * @access public 99 | * @return void 100 | */ 101 | public function to_json() { 102 | 103 | // Is the role editable? 104 | $is_editable = $this->manager->role ? members_is_role_editable( $this->manager->role->name ) : true; 105 | 106 | // Get the current capability. 107 | $this->json['cap'] = $this->cap; 108 | 109 | // Add the section ID. 110 | $this->json['section'] = $this->section; 111 | 112 | // If the cap is not editable, the inputs should be read-only. 113 | $this->json['readonly'] = $is_editable ? '' : ' disabled="disabled" readonly="readonly"'; 114 | 115 | // Set up the input labels. 116 | $this->json['label'] = array( 117 | 'cap' => members_show_human_caps() && members_cap_exists( $this->cap ) ? members_get_cap( $this->cap )->label : $this->cap, 118 | 'grant' => sprintf( esc_html__( 'Grant %s capability', 'members' ), "{$this->cap}" ), 119 | 'deny' => sprintf( esc_html__( 'Deny %s capability', 'members' ), "{$this->cap}" ) 120 | ); 121 | 122 | // Set up the input `name` attributes. 123 | $this->json['name'] = array( 124 | 'grant' => 'grant-caps[]', 125 | 'deny' => 'deny-caps[]' 126 | ); 127 | 128 | // Is this a granted or denied cap? 129 | $this->json['is_granted_cap'] = isset( $this->manager->has_caps[ $this->cap ] ) && $this->manager->has_caps[ $this->cap ]; 130 | $this->json['is_denied_cap'] = isset( $this->manager->has_caps[ $this->cap ] ) && false === $this->manager->has_caps[ $this->cap ]; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /admin/class-cap-section.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Cap section class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Cap_Section { 22 | 23 | /** 24 | * Stores the cap tabs object. 25 | * 26 | * @see Members_Cap_Tabs 27 | * @since 2.0.0 28 | * @access public 29 | * @var object 30 | */ 31 | public $manager; 32 | 33 | /** 34 | * ID of the section. 35 | * 36 | * @since 2.0.0 37 | * @access public 38 | * @var string 39 | */ 40 | public $section = ''; 41 | 42 | /** 43 | * Dashicons icon for the section. 44 | * 45 | * @since 2.0.0 46 | * @access public 47 | * @var string 48 | */ 49 | public $icon = 'dashicons-admin-generic'; 50 | 51 | /** 52 | * Label for the section. 53 | * 54 | * @since 2.0.0 55 | * @access public 56 | * @var string 57 | */ 58 | public $label = ''; 59 | 60 | /** 61 | * Array of data to pass as a json object to the Underscore template. 62 | * 63 | * @since 2.0.0 64 | * @access public 65 | * @var array 66 | */ 67 | public $json = array(); 68 | 69 | /** 70 | * Creates a new section object. 71 | * 72 | * @since 2.0.0 73 | * @access public 74 | * @param object $manager 75 | * @param string $section 76 | * @param array $args 77 | * @return void 78 | */ 79 | public function __construct( $manager, $section, $args = array() ) { 80 | 81 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 82 | 83 | if ( isset( $args[ $key ] ) ) 84 | $this->$key = $args[ $key ]; 85 | } 86 | 87 | $this->manager = $manager; 88 | $this->section = $section; 89 | } 90 | 91 | /** 92 | * Returns the json array. 93 | * 94 | * @since 2.0.0 95 | * @access public 96 | * @return array 97 | */ 98 | public function json() { 99 | $this->to_json(); 100 | return $this->json; 101 | } 102 | 103 | /** 104 | * Adds custom data to the json array. This data is passed to the Underscore template. 105 | * 106 | * @since 2.0.0 107 | * @access public 108 | * @return void 109 | */ 110 | public function to_json() { 111 | 112 | // Is the role editable? 113 | $is_editable = $this->manager->role ? members_is_role_editable( $this->manager->role->name ) : true; 114 | 115 | // Set up the ID and class. 116 | $this->json['id'] = $this->section; 117 | $this->json['class'] = 'members-tab-content' . ( $is_editable ? ' editable-role' : '' ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /admin/class-cap-tabs.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Handles building the edit caps tabs. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Cap_Tabs { 22 | 23 | /** 24 | * The role object that we're creating tabs for. 25 | * 26 | * @since 2.0.0 27 | * @access public 28 | * @var object 29 | */ 30 | public $role; 31 | 32 | /** 33 | * Array of caps shown by the cap tabs. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @var array 38 | */ 39 | public $added_caps = array(); 40 | 41 | /** 42 | * The caps the role has. Note that if this is a new role (new role screen), the default 43 | * new role caps will be passed in. 44 | * 45 | * @since 2.0.0 46 | * @access public 47 | * @var array 48 | */ 49 | public $has_caps = array(); 50 | 51 | /** 52 | * Array of tab sections. 53 | * 54 | * @since 2.0.0 55 | * @access public 56 | * @var array 57 | */ 58 | public $sections = array(); 59 | 60 | /** 61 | * Array of single cap controls. 62 | * 63 | * @since 2.0.0 64 | * @access public 65 | * @var array 66 | */ 67 | public $controls = array(); 68 | 69 | /** 70 | * Array of section json data. 71 | * 72 | * @since 2.0.0 73 | * @access public 74 | * @var array 75 | */ 76 | public $sections_json = array(); 77 | 78 | /** 79 | * Array of control json data. 80 | * 81 | * @since 2.0.0 82 | * @access public 83 | * @var array 84 | */ 85 | public $controls_json = array(); 86 | 87 | /** 88 | * Sets up the cap tabs. 89 | * 90 | * @since 2.0.0 91 | * @access public 92 | * @param string $role 93 | * @param array $has_caps 94 | * @return void 95 | */ 96 | public function __construct( $role = '', $has_caps = array() ) { 97 | 98 | // Check if there were explicit caps passed in. 99 | if ( $has_caps ) 100 | $this->has_caps = $has_caps; 101 | 102 | // Check if we have a role. 103 | if ( $role ) { 104 | $this->role = members_get_role( $role ); 105 | 106 | // If no explicit caps were passed in, use the role's caps. 107 | if ( ! $has_caps ) 108 | $this->has_caps = $this->role->caps; 109 | } 110 | 111 | // Add sections and controls. 112 | $this->register(); 113 | 114 | // Print custom JS in the footer. 115 | add_action( 'admin_footer', array( $this, 'localize_scripts' ), 0 ); 116 | add_action( 'admin_footer', array( $this, 'print_templates' ) ); 117 | } 118 | 119 | /** 120 | * Registers the sections (and each section's controls) that will be used for 121 | * the tab content. 122 | * 123 | * @since 2.0.0 124 | * @access public 125 | * @return void 126 | */ 127 | public function register() { 128 | 129 | // Hook before registering. 130 | do_action( 'members_pre_edit_caps_manager_register' ); 131 | 132 | $groups = members_get_cap_groups(); 133 | 134 | uasort( $groups, 'members_priority_sort' ); 135 | 136 | // Get and loop through the available capability groups. 137 | foreach ( $groups as $group ) { 138 | 139 | $caps = $group->caps; 140 | 141 | // Remove added caps. 142 | if ( $group->diff_added ) 143 | $caps = array_diff( $group->caps, $this->added_caps ); 144 | 145 | // Add group's caps to the added caps array. 146 | $this->added_caps = array_unique( array_merge( $this->added_caps, $caps ) ); 147 | 148 | // Create a new section. 149 | $this->sections[] = $section = new Cap_Section( $this, $group->name, array( 'icon' => $group->icon, 'label' => $group->label ) ); 150 | 151 | // Get the section json data. 152 | $this->sections_json[] = $section->json(); 153 | 154 | // Create new controls for each cap. 155 | foreach ( $caps as $cap ) { 156 | 157 | $this->controls[] = $control = new Cap_Control( $this, $cap, array( 'section' => $group->name ) ); 158 | 159 | // Get the control json data. 160 | $this->controls_json[] = $control->json(); 161 | } 162 | } 163 | 164 | // Create a new "All" section. 165 | $this->sections[] = $section = new Cap_Section( $this, 'all', array( 'icon' => 'dashicons-plus', 'label' => esc_html__( 'All', 'members' ) ) ); 166 | 167 | // Get the section json data. 168 | $this->sections_json[] = $section->json(); 169 | 170 | // Create new controls for each cap. 171 | foreach ( $this->added_caps as $cap ) { 172 | 173 | $this->controls[] = $control = new Cap_Control( $this, $cap, array( 'section' => 'all' ) ); 174 | 175 | // Get the control json data. 176 | $this->controls_json[] = $control->json(); 177 | } 178 | 179 | // Hook after registering. 180 | do_action( 'members_edit_caps_manager_register' ); 181 | } 182 | 183 | /** 184 | * Displays the cap tabs. 185 | * 186 | * @since 2.0.0 187 | * @access public 188 | * @return void 189 | */ 190 | public function display() { ?> 191 | 192 |
193 | 194 |

' ); ?>

195 | 196 |
197 | 198 |
199 | tab_nav(); ?> 200 |
201 |
202 | 203 |
204 | 205 |
206 | 216 | 217 | 230 | sections_json ); 242 | wp_localize_script( 'members-edit-role', 'members_controls', $this->controls_json ); 243 | } 244 | 245 | /** 246 | * Outputs the Underscore JS templates. 247 | * 248 | * @since 2.0.0 249 | * @access public 250 | * @return void 251 | */ 252 | public function print_templates() { ?> 253 | 254 | 257 | 258 | 261 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Role management class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Manage_Roles { 22 | 23 | /** 24 | * Holds the instances of this class. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var object 29 | */ 30 | private static $instance; 31 | 32 | /** 33 | * Name of the page we've created. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @var string 38 | */ 39 | public $page = ''; 40 | 41 | /** 42 | * The page object to show. 43 | * 44 | * @since 2.0.0 45 | * @access public 46 | * @var object 47 | */ 48 | public $page_obj = ''; 49 | 50 | /** 51 | * Sets up our initial actions. 52 | * 53 | * @since 2.0.0 54 | * @access public 55 | * @return void 56 | */ 57 | public function __construct() { 58 | 59 | // If the role manager is active. 60 | if ( members_role_manager_enabled() ) 61 | add_action( 'admin_menu', array( $this, 'add_admin_page' ) ); 62 | } 63 | 64 | /** 65 | * Adds the roles page to the admin. 66 | * 67 | * @since 2.0.0 68 | * @access public 69 | * @return void 70 | */ 71 | public function add_admin_page() { 72 | 73 | // The "Roles" page should be shown for anyone that has the 'list_roles', 'edit_roles', or 74 | // 'delete_roles' caps, so we're checking against all three. 75 | $edit_roles_cap = 'list_roles'; 76 | 77 | // If the current user can 'edit_roles'. 78 | if ( current_user_can( 'edit_roles' ) ) 79 | $edit_roles_cap = 'edit_roles'; 80 | 81 | // If the current user can 'delete_roles'. 82 | elseif ( current_user_can( 'delete_roles' ) ) 83 | $edit_roles_cap = 'delete_roles'; 84 | 85 | // Get the page title. 86 | $title = esc_html__( 'Roles', 'members' ); 87 | 88 | if ( isset( $_GET['action'] ) && 'edit' === $_GET['action'] && isset( $_GET['role'] ) ) 89 | $title = esc_html__( 'Edit Role', 'members' ); 90 | 91 | // Create the Manage Roles page. 92 | $this->page = add_submenu_page( 'users.php', $title, esc_html__( 'Roles', 'members' ), $edit_roles_cap, 'roles', array( $this, 'page' ) ); 93 | 94 | // Let's roll if we have a page. 95 | if ( $this->page ) { 96 | 97 | // If viewing the edit role page. 98 | if ( isset( $_REQUEST['action'] ) && 'edit' === $_REQUEST['action'] && current_user_can( 'edit_roles' ) ) 99 | $this->page_obj = new Role_Edit(); 100 | 101 | // If viewing the role list page. 102 | else 103 | $this->page_obj = new Roles(); 104 | 105 | // Load actions. 106 | add_action( "load-{$this->page}", array( $this, 'load' ) ); 107 | 108 | // Load scripts/styles. 109 | add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 110 | } 111 | } 112 | 113 | /** 114 | * Checks posted data on load and performs actions if needed. 115 | * 116 | * @since 2.0.0 117 | * @access public 118 | * @return void 119 | */ 120 | public function load() { 121 | 122 | if ( method_exists( $this->page_obj, 'load' ) ) 123 | $this->page_obj->load(); 124 | } 125 | 126 | /** 127 | * Loads necessary scripts/styles. 128 | * 129 | * @since 2.0.0 130 | * @access public 131 | * @param string $hook_suffix 132 | * @return void 133 | */ 134 | public function enqueue( $hook_suffix ) { 135 | 136 | if ( $this->page === $hook_suffix && method_exists( $this->page_obj, 'enqueue' ) ) 137 | $this->page_obj->enqueue(); 138 | } 139 | 140 | /** 141 | * Outputs the page. 142 | * 143 | * @since 2.0.0 144 | * @access public 145 | * @return void 146 | */ 147 | public function page() { 148 | 149 | if ( method_exists( $this->page_obj, 'page' ) ) 150 | $this->page_obj->page(); 151 | } 152 | 153 | /** 154 | * Returns the instance. 155 | * 156 | * @since 2.0.0 157 | * @access public 158 | * @return object 159 | */ 160 | public static function get_instance() { 161 | 162 | if ( ! self::$instance ) 163 | self::$instance = new self; 164 | 165 | return self::$instance; 166 | } 167 | } 168 | 169 | Manage_Roles::get_instance(); 170 | -------------------------------------------------------------------------------- /admin/class-meta-box-content-permissions.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Class to handle the content permissios meta box and saving the meta. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Meta_Box_Content_Permissions { 22 | 23 | /** 24 | * Holds the instances of this class. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var object 29 | */ 30 | private static $instance; 31 | 32 | /** 33 | * Whether this is a new post. Once the post is saved and we're 34 | * no longer on the `post-new.php` screen, this is going to be 35 | * `false`. 36 | * 37 | * @since 2.0.0 38 | * @access public 39 | * @var bool 40 | */ 41 | public $is_new_post = false; 42 | 43 | /** 44 | * Sets up the appropriate actions. 45 | * 46 | * @since 2.0.0 47 | * @access protected 48 | * @return void 49 | */ 50 | protected function __construct() { 51 | 52 | // If content permissions is disabled, bail. 53 | if ( ! members_content_permissions_enabled() ) 54 | return; 55 | 56 | add_action( 'load-post.php', array( $this, 'load' ) ); 57 | add_action( 'load-post-new.php', array( $this, 'load' ) ); 58 | } 59 | 60 | /** 61 | * Fires on the page load hook to add actions specifically for the post and 62 | * new post screens. 63 | * 64 | * @since 2.0.0 65 | * @access public 66 | * @return void 67 | */ 68 | public function load() { 69 | 70 | // Make sure meta box is allowed for this post type. 71 | if ( ! $this->maybe_enable() ) 72 | return; 73 | 74 | // Is this a new post? 75 | $this->is_new_post = 'load-post-new.php' === current_action(); 76 | 77 | // Enqueue scripts/styles. 78 | add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 79 | 80 | // Add custom meta boxes. 81 | add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); 82 | 83 | // Save metadata on post save. 84 | add_action( 'save_post', array( $this, 'update' ), 10, 2 ); 85 | } 86 | 87 | /** 88 | * Enqueues scripts styles. 89 | * 90 | * @since 2.0.0 91 | * @access public 92 | * @return void 93 | */ 94 | public function enqueue() { 95 | 96 | wp_enqueue_script( 'members-edit-post' ); 97 | wp_enqueue_style( 'members-admin' ); 98 | } 99 | 100 | /** 101 | * Adds the meta box. 102 | * 103 | * @since 2.0.0 104 | * @access public 105 | * @param string $post_type 106 | * @return void 107 | */ 108 | public function add_meta_boxes( $post_type ) { 109 | 110 | // If the current user can't restrict content, bail. 111 | if ( ! current_user_can( 'restrict_content' ) ) 112 | return; 113 | 114 | // Add the meta box. 115 | add_meta_box( 'members-cp', esc_html__( 'Content Permissions', 'members' ), array( $this, 'meta_box' ), $post_type, 'advanced', 'high' ); 116 | } 117 | 118 | /** 119 | * Checks if Content Permissions should appear for the given post type. 120 | * 121 | * @since 2.0.0 122 | * @access public 123 | * @return bool 124 | */ 125 | public function maybe_enable() { 126 | 127 | // Get the post type object. 128 | $type = get_post_type_object( get_current_screen()->post_type ); 129 | 130 | // Only enable for public post types and non-attachments by default. 131 | $enable = 'attachment' !== $type->name && $type->public; 132 | 133 | return apply_filters( "members_enable_{$type->name}_content_permissions", $enable ); 134 | } 135 | 136 | /** 137 | * Outputs the meta box HTML. 138 | * 139 | * @since 2.0.0 140 | * @access public 141 | * @param object $post 142 | * @global object $wp_roles 143 | * @return void 144 | */ 145 | public function meta_box( $post ) { 146 | global $wp_roles; 147 | 148 | // Get roles and sort. 149 | $_wp_roles = $wp_roles->role_names; 150 | asort( $_wp_roles ); 151 | 152 | // Get the roles saved for the post. 153 | $roles = get_post_meta( $post->ID, '_members_access_role', false ); 154 | 155 | if ( ! $roles && $this->is_new_post ) 156 | $roles = apply_filters( 'members_default_post_roles', array(), $post->ID ); 157 | 158 | // Convert old post meta to the new system if no roles were found. 159 | if ( empty( $roles ) ) 160 | $roles = members_convert_old_post_meta( $post->ID ); 161 | 162 | // Nonce field to validate on save. 163 | wp_nonce_field( 'members_cp_meta_nonce', 'members_cp_meta' ); 164 | 165 | // Hook for firing at the top of the meta box. 166 | do_action( 'members_cp_meta_box_before', $post ); ?> 167 | 168 |
169 | 170 | 184 | 185 |
186 | 187 |
188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 |
    196 | 197 | $name ) : ?> 198 |
  • 199 | 203 |
  • 204 | 205 | 206 |
207 |
208 | 209 | 210 | restrict_content' ); ?> 211 | 212 | 213 |
214 | 215 |
216 | 217 | ID, '_members_access_error', true ), 219 | 'members_access_error', 220 | array( 221 | 'drag_drop_upload' => true, 222 | 'editor_height' => 200 223 | ) 224 | ); ?> 225 | 226 |
227 | 228 |
229 | 230 |
8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Class to handle the new cap meta box on the edit/new role screen. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Meta_Box_Custom_Cap { 22 | 23 | /** 24 | * Holds the instances of this class. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var object 29 | */ 30 | private static $instance; 31 | 32 | /** 33 | * Adds our methods to the proper hooks. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @return void 38 | */ 39 | protected function __construct() { 40 | 41 | add_action( 'members_load_role_edit', array( $this, 'load' ) ); 42 | add_action( 'members_load_role_new', array( $this, 'load' ) ); 43 | } 44 | 45 | /** 46 | * Runs on the page load hook to hook in the meta boxes. 47 | * 48 | * @since 2.0.0 49 | * @access public 50 | * @return void 51 | */ 52 | public function load() { 53 | 54 | add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); 55 | } 56 | 57 | /** 58 | * Adds the meta box. 59 | * 60 | * @since 2.0.0 61 | * @access public 62 | * @param string $screen_id 63 | * @param string $role 64 | * @return void 65 | */ 66 | public function add_meta_boxes( $screen_id, $role = '' ) { 67 | 68 | // If role isn't editable, bail. 69 | if ( $role && ! members_is_role_editable( $role ) ) 70 | return; 71 | 72 | // Add the meta box. 73 | add_meta_box( 'newcapdiv', esc_html__( 'Custom Capability', 'members' ), array( $this, 'meta_box' ), $screen_id, 'side', 'core' ); 74 | } 75 | 76 | /** 77 | * Outputs the meta box HTML. 78 | * 79 | * @since 2.0.0 80 | * @access public 81 | * @return void 82 | */ 83 | public function meta_box() { ?> 84 | 85 |

86 | 87 |

88 | 89 |

90 | 91 |

92 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Class to handle the role meta box edit/new role screen. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Meta_Box_Publish_Role { 22 | 23 | /** 24 | * Holds the instances of this class. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var object 29 | */ 30 | private static $instance; 31 | 32 | /** 33 | * Adds our methods to the proper hooks. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @return void 38 | */ 39 | protected function __construct() { 40 | 41 | add_action( 'members_load_role_edit', array( $this, 'load' ) ); 42 | add_action( 'members_load_role_new', array( $this, 'load' ) ); 43 | } 44 | 45 | /** 46 | * Runs on the page load hook to hook in the meta boxes. 47 | * 48 | * @since 2.0.0 49 | * @access public 50 | * @return void 51 | */ 52 | public function load() { 53 | 54 | add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); 55 | } 56 | 57 | /** 58 | * Adds the meta box. 59 | * 60 | * @since 2.0.0 61 | * @access public 62 | * @param string $screen_id 63 | * @return void 64 | */ 65 | public function add_meta_boxes( $screen_id ) { 66 | 67 | add_meta_box( 'submitdiv', esc_html__( 'Role', 'members' ), array( $this, 'meta_box' ), $screen_id, 'side', 'high' ); 68 | } 69 | 70 | /** 71 | * Outputs the meta box HTML. 72 | * 73 | * @since 2.0.0 74 | * @access public 75 | * @param object $role 76 | * @return void 77 | */ 78 | public function meta_box( $role ) { 79 | 80 | // Set up some defaults for new roles. 81 | $is_editable = true; 82 | $user_count = 0; 83 | $grant_count = 0; 84 | $deny_count = 0; 85 | 86 | // If we're editing a role, overwrite the defaults. 87 | if ( $role ) { 88 | $is_editable = members_is_role_editable( $role->name ); 89 | $user_count = members_get_role_user_count( $role->name ); 90 | $grant_count = members_get_role_granted_cap_count( $role->name ); 91 | $deny_count = members_get_role_denied_cap_count( $role->name ); 92 | } ?> 93 | 94 |
95 | 96 |
97 | 98 |
99 | 100 | 101 | 102 | 108 | 109 | 110 | 111 | 112 | 113 |
114 | 115 |
116 | 117 | 118 | 119 |
120 | 121 |
122 | 123 | 124 | 125 |
126 | 127 |
128 | 129 |
130 | 131 |
132 | 133 | 134 | 135 | 136 |
137 | 138 |
139 | 140 | 141 | 'publish' ) ); ?> 142 | 143 | 144 |
145 | 146 |
147 | 148 |
149 | 150 |
151 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Class that displays the roles admin screen and handles requests for that page. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Roles { 22 | 23 | /** 24 | * Sets up some necessary actions/filters. 25 | * 26 | * @since 2.0.0 27 | * @access public 28 | * @return void 29 | */ 30 | public function __construct() { 31 | 32 | // Set up some page options for the current screen. 33 | add_action( 'current_screen', array( $this, 'current_screen' ) ); 34 | 35 | // Set up the role list table columns. 36 | add_filter( 'manage_users_page_roles_columns', array( $this, 'manage_roles_columns' ), 5 ); 37 | 38 | // Add help tabs. 39 | add_action( 'members_load_manage_roles', array( $this, 'add_help_tabs' ) ); 40 | } 41 | 42 | /** 43 | * Modifies the current screen object. 44 | * 45 | * @since 2.0.0 46 | * @access public 47 | * @return void 48 | */ 49 | public function current_screen( $screen ) { 50 | 51 | if ( 'users_page_roles' === $screen->id ) 52 | $screen->add_option( 'per_page', array( 'default' => 20 ) ); 53 | } 54 | 55 | /** 56 | * Sets up the roles column headers. 57 | * 58 | * @since 2.0.0 59 | * @access public 60 | * @param array $columns 61 | * @return array 62 | */ 63 | public function manage_roles_columns( $columns ) { 64 | 65 | $columns = array( 66 | 'cb' => '', 67 | 'title' => esc_html__( 'Role Name', 'members' ), 68 | 'role' => esc_html__( 'Role', 'members' ), 69 | 'users' => esc_html__( 'Users', 'members' ), 70 | 'granted_caps' => esc_html__( 'Granted', 'members' ), 71 | 'denied_caps' => esc_html__( 'Denied', 'members' ) 72 | ); 73 | 74 | return apply_filters( 'members_manage_roles_columns', $columns ); 75 | } 76 | 77 | /** 78 | * Runs on the `load-{$page}` hook. This is the handler for form submissions and requests. 79 | * 80 | * @since 2.0.0 81 | * @access public 82 | * @return void 83 | */ 84 | public function load() { 85 | 86 | // Get the current action if sent as request. 87 | $action = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : false; 88 | 89 | // Get the current action if posted. 90 | if ( ( isset( $_POST['action'] ) && 'delete' == $_POST['action'] ) || ( isset( $_POST['action2'] ) && 'delete' == $_POST['action2'] ) ) 91 | $action = 'bulk-delete'; 92 | 93 | // Bulk delete role handler. 94 | if ( 'bulk-delete' === $action ) { 95 | 96 | // If roles were selected, let's delete some roles. 97 | if ( current_user_can( 'delete_roles' ) && isset( $_POST['roles'] ) && is_array( $_POST['roles'] ) ) { 98 | 99 | // Verify the nonce. Nonce created via `WP_List_Table::display_tablenav()`. 100 | check_admin_referer( 'bulk-roles' ); 101 | 102 | // Loop through each of the selected roles. 103 | foreach ( $_POST['roles'] as $role ) { 104 | 105 | $role = members_sanitize_role( $role ); 106 | 107 | if ( members_role_exists( $role ) ) 108 | members_delete_role( $role ); 109 | } 110 | 111 | // Add roles deleted message. 112 | add_settings_error( 'members_roles', 'roles_deleted', esc_html__( 'Selected roles deleted.', 'members' ), 'updated' ); 113 | } 114 | 115 | // Delete single role handler. 116 | } else if ( 'delete' === $action ) { 117 | 118 | // Make sure the current user can delete roles. 119 | if ( current_user_can( 'delete_roles' ) ) { 120 | 121 | // Verify the referer. 122 | check_admin_referer( 'delete_role', 'members_delete_role_nonce' ); 123 | 124 | // Get the role we want to delete. 125 | $role = members_sanitize_role( $_GET['role'] ); 126 | 127 | // Check that we have a role before attempting to delete it. 128 | if ( members_role_exists( $role ) ) { 129 | 130 | // Add role deleted message. 131 | add_settings_error( 'members_roles', 'role_deleted', sprintf( esc_html__( '%s role deleted.', 'members' ), members_get_role( $role )->get( 'label' ) ), 'updated' ); 132 | 133 | // Delete the role. 134 | members_delete_role( $role ); 135 | } 136 | } 137 | } 138 | 139 | // Load page hook. 140 | do_action( 'members_load_manage_roles' ); 141 | } 142 | 143 | /** 144 | * Enqueue scripts/styles. 145 | * 146 | * @since 2.0.0 147 | * @access public 148 | * @return void 149 | */ 150 | public function enqueue() { 151 | 152 | wp_enqueue_style( 'members-admin' ); 153 | wp_enqueue_script( 'members-edit-role' ); 154 | } 155 | 156 | /** 157 | * Displays the page content. 158 | * 159 | * @since 2.0.0 160 | * @access public 161 | * @return void 162 | */ 163 | public function page() { 164 | 165 | require_once( members_plugin()->dir . 'admin/class-role-list-table.php' ); ?> 166 | 167 |
168 | 169 |

170 | 171 | 172 | 173 | 174 | 175 |

176 | 177 | 178 | 179 |
180 | 181 |
182 | 183 | 184 | prepare_items(); ?> 185 | display(); ?> 186 | 187 |
188 | 189 |
190 | 191 |
192 | add_help_tab( 208 | array( 209 | 'id' => 'overview', 210 | 'title' => esc_html__( 'Overview', 'members' ), 211 | 'callback' => array( $this, 'help_tab_overview' ) 212 | ) 213 | ); 214 | 215 | // Add screen content help tab. 216 | $screen->add_help_tab( 217 | array( 218 | 'id' => 'screen-content', 219 | 'title' => esc_html__( 'Screen Content', 'members' ), 220 | 'callback' => array( $this, 'help_tab_screen_content' ) 221 | ) 222 | ); 223 | 224 | // Add available actions help tab. 225 | $screen->add_help_tab( 226 | array( 227 | 'id' => 'row-actions', 228 | 'title' => esc_html__( 'Available Actions', 'members' ), 229 | 'callback' => array( $this, 'help_tab_row_actions' ) 230 | ) 231 | ); 232 | 233 | // Add bulk actions help tab. 234 | $screen->add_help_tab( 235 | array( 236 | 'id' => 'bulk-actions', 237 | 'title' => esc_html__( 'Bulk Actions', 'members' ), 238 | 'callback' => array( $this, 'help_tab_bulk_actions' ) 239 | ) 240 | ); 241 | 242 | // Set the help sidebar. 243 | $screen->set_help_sidebar( members_get_help_sidebar_text() ); 244 | } 245 | 246 | /** 247 | * Overview help tab callback function. 248 | * 249 | * @since 2.0.0 250 | * @access public 251 | * @return void 252 | */ 253 | public function help_tab_overview() { ?> 254 | 255 |

256 | 257 |

258 | 268 | 269 |

270 | 271 |

272 | 273 | 277 | 287 | 288 |

289 | 290 |

291 | 292 | 298 | 308 | 309 |

310 | 311 |

312 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Sets up and handles the plugin settings screen. 17 | * 18 | * @since 1.0.0 19 | * @access public 20 | */ 21 | final class Settings_Page { 22 | 23 | /** 24 | * Admin page name/ID. 25 | * 26 | * @since 2.0.0 27 | * @access public 28 | * @var string 29 | */ 30 | public $name = 'members-settings'; 31 | 32 | /** 33 | * Settings page name. 34 | * 35 | * @since 1.0.0 36 | * @access public 37 | * @var string 38 | */ 39 | public $settings_page = ''; 40 | 41 | /** 42 | * Holds an array the settings page views. 43 | * 44 | * @since 2.0.0 45 | * @access public 46 | * @var array 47 | */ 48 | public $views = array(); 49 | 50 | /** 51 | * Returns the instance. 52 | * 53 | * @since 1.0.0 54 | * @access public 55 | * @return object 56 | */ 57 | public static function get_instance() { 58 | 59 | static $instance = null; 60 | 61 | if ( is_null( $instance ) ) { 62 | $instance = new self; 63 | $instance->includes(); 64 | $instance->setup_actions(); 65 | } 66 | 67 | return $instance; 68 | } 69 | 70 | /** 71 | * Constructor method. 72 | * 73 | * @since 1.0.0 74 | * @access public 75 | * @return void 76 | */ 77 | private function __construct() {} 78 | 79 | /** 80 | * Loads settings files. 81 | * 82 | * @since 2.0.0 83 | * @access private 84 | * @return void 85 | */ 86 | private function includes() { 87 | 88 | // Include the settings functions. 89 | require_once( members_plugin()->dir . 'admin/functions-settings.php' ); 90 | 91 | // Load settings view classes. 92 | require_once( members_plugin()->dir . 'admin/views/class-view.php' ); 93 | require_once( members_plugin()->dir . 'admin/views/class-view-general.php' ); 94 | require_once( members_plugin()->dir . 'admin/views/class-view-addons.php' ); 95 | require_once( members_plugin()->dir . 'admin/views/class-view-donate.php' ); 96 | } 97 | 98 | /** 99 | * Sets up initial actions. 100 | * 101 | * @since 2.0.0 102 | * @access private 103 | * @return void 104 | */ 105 | private function setup_actions() { 106 | 107 | add_action( 'admin_menu', array( $this, 'admin_menu' ) ); 108 | } 109 | 110 | /** 111 | * Register a view. 112 | * 113 | * @since 2.0.0 114 | * @access public 115 | * @param object $view 116 | * @return void 117 | */ 118 | public function register_view( $view ) { 119 | 120 | if ( ! $this->view_exists( $view->name ) ) 121 | $this->views[ $view->name ] = $view; 122 | } 123 | 124 | /** 125 | * Unregister a view. 126 | * 127 | * @since 2.0.0 128 | * @access public 129 | * @param string $name 130 | * @return void 131 | */ 132 | public function unregister_view( $name ) { 133 | 134 | if ( $this->view_exists( $name ) ) 135 | unset( $this->view[ $name ] ); 136 | } 137 | 138 | /** 139 | * Get a view object 140 | * 141 | * @since 2.0.0 142 | * @access public 143 | * @param string $name 144 | * @return object 145 | */ 146 | public function get_view( $name ) { 147 | 148 | return $this->view_exists( $name ) ? $this->views[ $name ] : false; 149 | } 150 | 151 | /** 152 | * Check if a view exists. 153 | * 154 | * @since 2.0.0 155 | * @access public 156 | * @param string $name 157 | * @return bool 158 | */ 159 | public function view_exists( $name ) { 160 | 161 | return isset( $this->views[ $name ] ); 162 | } 163 | 164 | /** 165 | * Sets up custom admin menus. 166 | * 167 | * @since 1.0.0 168 | * @access public 169 | * @return void 170 | */ 171 | public function admin_menu() { 172 | 173 | // Create the settings page. 174 | $this->settings_page = add_options_page( 175 | esc_html_x( 'Members', 'admin screen', 'members' ), 176 | esc_html_x( 'Members', 'admin screen', 'members' ), 177 | apply_filters( 'members_settings_capability', 'manage_options' ), 178 | $this->name, 179 | array( $this, 'settings_page' ) 180 | ); 181 | 182 | if ( $this->settings_page ) { 183 | 184 | do_action( 'members_register_settings_views', $this ); 185 | 186 | uasort( $this->views, 'members_priority_sort' ); 187 | 188 | // Register setings. 189 | add_action( 'admin_init', array( $this, 'register_settings' ) ); 190 | 191 | // Page load callback. 192 | add_action( "load-{$this->settings_page}", array( $this, 'load' ) ); 193 | 194 | // Enqueue scripts/styles. 195 | add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 196 | } 197 | } 198 | 199 | /** 200 | * Runs on page load. 201 | * 202 | * @since 2.0.0 203 | * @access public 204 | * @return void 205 | */ 206 | public function load() { 207 | 208 | // Print custom styles. 209 | add_action( 'admin_head', array( $this, 'print_styles' ) ); 210 | 211 | // Add help tabs for the current view. 212 | $view = $this->get_view( members_get_current_settings_view() ); 213 | 214 | if ( $view ) { 215 | $view->load(); 216 | $view->add_help_tabs(); 217 | } 218 | } 219 | 220 | /** 221 | * Print styles to the header. 222 | * 223 | * @since 2.0.0 224 | * @access public 225 | * @return void 226 | */ 227 | public function print_styles() { ?> 228 | 229 | 232 | settings_page !== $hook_suffix ) 245 | return; 246 | 247 | $view = $this->get_view( members_get_current_settings_view() ); 248 | 249 | if ( $view ) 250 | $view->enqueue(); 251 | } 252 | 253 | /** 254 | * Registers the plugin settings. 255 | * 256 | * @since 1.0.0 257 | * @access public 258 | * @return void 259 | */ 260 | function register_settings() { 261 | 262 | foreach ( $this->views as $view ) 263 | $view->register_settings(); 264 | } 265 | 266 | /** 267 | * Renders the settings page. 268 | * 269 | * @since 1.0.0 270 | * @access public 271 | * @return void 272 | */ 273 | public function settings_page() { ?> 274 | 275 |
276 |

277 | 278 |
279 | filter_links(); ?> 280 |
281 | 282 | get_view( members_get_current_settings_view() )->template(); ?> 283 | 284 |
285 | 295 | 296 | 316 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Edit user screen class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class User_Edit { 22 | 23 | /** 24 | * Holds the instances of this class. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var object 29 | */ 30 | private static $instance; 31 | 32 | /** 33 | * Sets up needed actions/filters for the admin to initialize. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @return void 38 | */ 39 | public function __construct() { 40 | 41 | // If multiple roles per user is not enabled, bail. 42 | if ( ! members_multiple_user_roles_enabled() ) 43 | return; 44 | 45 | // Only run our customization on the 'user-edit.php' page in the admin. 46 | add_action( 'load-user-edit.php', array( $this, 'load_user_edit' ) ); 47 | } 48 | 49 | /** 50 | * Adds actions/filters on load. 51 | * 52 | * @since 2.0.0 53 | * @access public 54 | * @return void 55 | */ 56 | public function load_user_edit() { 57 | 58 | // Handle scripts and styles. 59 | add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 60 | add_action( 'admin_footer', array( $this, 'print_scripts' ), 25 ); 61 | add_action( 'admin_head', array( $this, 'print_styles' ) ); 62 | 63 | add_action( 'show_user_profile', array( $this, 'profile_fields' ) ); 64 | add_action( 'edit_user_profile', array( $this, 'profile_fields' ) ); 65 | 66 | // Must use `profile_update` to change role. Otherwise, WP will wipe it out. 67 | add_action( 'profile_update', array( $this, 'role_update' ), 10, 2 ); 68 | } 69 | 70 | /** 71 | * Adds custom profile fields. 72 | * 73 | * @since 2.0.0 74 | * @access public 75 | * @param object $user 76 | * @return void 77 | */ 78 | public function profile_fields( $user ) { 79 | global $wp_roles; 80 | 81 | if ( ! current_user_can( 'promote_users' ) || ! current_user_can( 'edit_user', $user->ID ) ) 82 | return; 83 | 84 | $user_roles = (array) $user->roles; 85 | 86 | $roles = members_get_roles(); 87 | 88 | ksort( $roles ); 89 | 90 | wp_nonce_field( 'new_user_roles', 'members_new_user_roles_nonce' ); ?> 91 | 92 |

93 | 94 | 95 | 96 | 97 | 98 | 99 | 117 | 118 | 119 |
100 |
101 |
    102 | 103 | 104 | name ) ) :?> 105 |
  • 106 | 110 |
  • 111 | 112 | 113 | 114 |
115 |
116 |
120 | roles; 151 | 152 | // Sanitize the posted roles. 153 | $new_roles = array_map( 'members_sanitize_role', $_POST['members_user_roles'] ); 154 | 155 | // Loop through the posted roles. 156 | foreach ( $new_roles as $new_role ) { 157 | 158 | // If the user doesn't already have the role, add it. 159 | if ( members_is_role_editable( $new_role ) && ! in_array( $new_role, (array) $old_user_data->roles ) ) 160 | $old_user_data->add_role( $new_role ); 161 | } 162 | 163 | // Loop through the current user roles. 164 | foreach ( $old_roles as $old_role ) { 165 | 166 | // If the role is editable and not in the new roles array, remove it. 167 | if ( members_is_role_editable( $old_role ) && ! in_array( $old_role, $new_roles ) ) 168 | $old_user_data->remove_role( $old_role ); 169 | } 170 | 171 | // If the posted roles are empty. 172 | } else { 173 | 174 | // Loop through the current user roles. 175 | foreach ( (array) $old_user_data->roles as $old_role ) { 176 | 177 | // Remove the role if it is editable. 178 | if ( members_is_role_editable( $old_role ) ) 179 | $old_user_data->remove_role( $old_role ); 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * Enqueue scripts. 186 | * 187 | * @since 2.0.0 188 | * @access public 189 | * @return void 190 | */ 191 | public function enqueue() { 192 | 193 | wp_enqueue_script( 'jquery' ); 194 | } 195 | 196 | /** 197 | * Enqueue the plugin admin CSS. 198 | * 199 | * @since 2.0.0 200 | * @access public 201 | * @return void 202 | */ 203 | public function print_scripts() { ?> 204 | 205 | 211 | 212 | 222 | 223 | 224 | 225 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Edit user screen class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class User_New { 22 | 23 | /** 24 | * Holds the instances of this class. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var object 29 | */ 30 | private static $instance; 31 | 32 | /** 33 | * Constructor method. 34 | * 35 | * @since 2.0.0 36 | * @access private 37 | * @return void 38 | */ 39 | private function __construct() {} 40 | 41 | /** 42 | * Sets up needed actions/filters for the admin to initialize. 43 | * 44 | * @since 2.0.0 45 | * @access private 46 | * @return void 47 | */ 48 | private function setup_actions() { 49 | 50 | // If multiple roles per user is not enabled, bail. 51 | // 52 | // @since 2.0.1 Added a check to not run on multisite. 53 | // @link https://github.com/justintadlock/members/issues/153 54 | if ( ! members_multiple_user_roles_enabled() || is_multisite() ) 55 | return; 56 | 57 | // Only run our customization on the 'user-edit.php' page in the admin. 58 | add_action( 'load-user-new.php', array( $this, 'load' ) ); 59 | 60 | // Sets the new user's roles. 61 | add_action( 'user_register', array( $this, 'user_register' ), 5 ); 62 | } 63 | 64 | /** 65 | * Adds actions/filters on load. 66 | * 67 | * @since 2.0.0 68 | * @access public 69 | * @return void 70 | */ 71 | public function load() { 72 | 73 | // Adds the profile fields. 74 | add_action( 'user_new_form', array( $this, 'profile_fields' ) ); 75 | 76 | // Handle scripts. 77 | add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 78 | add_action( 'admin_footer', array( $this, 'print_scripts' ), 25 ); 79 | } 80 | 81 | /** 82 | * Adds custom profile fields. 83 | * 84 | * @since 2.0.0 85 | * @access public 86 | * @return void 87 | */ 88 | public function profile_fields() { 89 | 90 | if ( ! current_user_can( 'promote_users' ) ) 91 | return; 92 | 93 | // Get the default user roles. 94 | $new_user_roles = apply_filters( 'members_default_user_roles', array( get_option( 'default_role' ) ) ); 95 | 96 | // If the form was submitted but didn't go through, get the posted roles. 97 | if ( isset( $_POST['createuser'] ) && ! empty( $_POST['members_user_roles'] ) ) 98 | $new_user_roles = array_map( 'members_sanitize_role', $_POST['members_user_roles'] ); 99 | 100 | $roles = members_get_roles(); 101 | 102 | ksort( $roles ); 103 | 104 | wp_nonce_field( 'new_user_roles', 'members_new_user_roles_nonce' ); ?> 105 | 106 | 107 | 108 | 109 | 110 | 111 | 129 | 130 | 131 |
112 |
113 |
    114 | 115 | 116 | name ) ) :?> 117 |
  • 118 | 122 |
  • 123 | 124 | 125 | 126 |
127 |
128 |
132 | roles; 160 | 161 | // Sanitize the posted roles. 162 | $new_roles = array_map( 'members_sanitize_role', $_POST['members_user_roles'] ); 163 | 164 | // Loop through the posted roles. 165 | foreach ( $new_roles as $new_role ) { 166 | 167 | // If the user doesn't already have the role, add it. 168 | if ( members_is_role_editable( $new_role ) && ! in_array( $new_role, (array) $user->roles ) ) 169 | $user->add_role( $new_role ); 170 | } 171 | 172 | // Loop through the current user roles. 173 | foreach ( $old_roles as $old_role ) { 174 | 175 | // If the role is editable and not in the new roles array, remove it. 176 | if ( members_is_role_editable( $old_role ) && ! in_array( $old_role, $new_roles ) ) 177 | $user->remove_role( $old_role ); 178 | } 179 | 180 | // If the posted roles are empty. 181 | } else { 182 | 183 | // Loop through the current user roles. 184 | foreach ( (array) $user->roles as $old_role ) { 185 | 186 | // Remove the role if it is editable. 187 | if ( members_is_role_editable( $old_role ) ) 188 | $user->remove_role( $old_role ); 189 | } 190 | } 191 | } 192 | 193 | /** 194 | * Enqueue scripts. 195 | * 196 | * @since 2.0.0 197 | * @access public 198 | * @return void 199 | */ 200 | public function enqueue() { 201 | 202 | wp_enqueue_script( 'jquery' ); 203 | } 204 | 205 | /** 206 | * Enqueue the plugin admin CSS. 207 | * 208 | * @since 2.0.0 209 | * @access public 210 | * @return void 211 | */ 212 | public function print_scripts() { ?> 213 | 214 | 221 | 222 | setup_actions(); 237 | } 238 | 239 | return self::$instance; 240 | } 241 | } 242 | 243 | User_New::get_instance(); 244 | -------------------------------------------------------------------------------- /admin/config/addons.php: -------------------------------------------------------------------------------- 1 | array( 6 | 'url' => 'https://themehybrid.com/plugins/members-privacy-caps', 7 | 'title' => 'Members - Privacy Caps', 8 | 'excerpt' => 'Creates additional capabilities for control over WordPress’ privacy and personal data features (GDPR).' 9 | ), 10 | 11 | 'members-admin-access' => array( 12 | 'url' => 'https://themehybrid.com/plugins/members-admin-access', 13 | 'title' => 'Members - Admin Access', 14 | 'excerpt' => 'Allows site administrators to control which users have access to the WordPress admin via role.', 15 | ), 16 | 17 | 'members-core-create-caps' => array( 18 | 'url' => 'https://themehybrid.com/plugins/members-core-create-caps', 19 | 'title' => 'Members - Core Create Caps', 20 | 'excerpt' => 'Adds the create_posts and create_pages caps to posts/pages to separate them from their edit_* counterparts, providing more flexible editing capabilities.' 21 | ), 22 | 23 | 'members-role-levels' => array( 24 | 'url' => 'https://themehybrid.com/plugins/members-role-levels', 25 | 'title' => 'Members - Role Levels', 26 | '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.' 27 | ), 28 | 29 | 'members-role-hierarchy' => array( 30 | 'url' => 'https://themehybrid.com/plugins/members-role-hierarchy', 31 | 'title' => 'Members - Role Hierarchy', 32 | 'excerpt' => 'Add-on plugin for Members, which creates a hierarchical roles system.' 33 | ), 34 | 35 | 'members-block-permissions' => array( 36 | 'url' => '', 37 | 'title' => 'Members - Block Permissions', 38 | 'excerpt' => '

COMING SOON!

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

' 39 | ) 40 | ); 41 | -------------------------------------------------------------------------------- /admin/functions-addons.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 10 | * @link https://themehybrid.com/plugins/members 11 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 12 | */ 13 | 14 | # Register addons. 15 | add_action( 'members_register_addons', 'members_register_default_addons', 5 ); 16 | 17 | /** 18 | * Registers any addons stored globally with WordPress. 19 | * 20 | * @since 2.0.0 21 | * @access public 22 | * @param object $wp_addons 23 | * @return void 24 | */ 25 | function members_register_default_addons() { 26 | 27 | $data = include members_plugin()->dir . 'admin/config/addons.php'; 28 | 29 | // If we have an array of data, let's roll. 30 | if ( ! empty( $data ) && is_array( $data ) ) { 31 | 32 | foreach ( $data as $addon => $options ) { 33 | members_register_addon( $addon, $options ); 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Returns the instance of the addon registry. 40 | * 41 | * @since 2.0.0 42 | * @access public 43 | * @return object 44 | */ 45 | function members_addon_registry() { 46 | 47 | return \Members\Registry::get_instance( 'addon' ); 48 | } 49 | 50 | /** 51 | * Returns all registered addons. 52 | * 53 | * @since 2.0.0 54 | * @access public 55 | * @return array 56 | */ 57 | function members_get_addons() { 58 | 59 | return members_addon_registry()->get_collection(); 60 | } 61 | 62 | /** 63 | * Registers a addon. 64 | * 65 | * @since 2.0.0 66 | * @access public 67 | * @param string $name 68 | * @param array $args 69 | * @return void 70 | */ 71 | function members_register_addon( $name, $args = array() ) { 72 | 73 | members_addon_registry()->register( $name, new \Members\Addon( $name, $args ) ); 74 | } 75 | 76 | /** 77 | * Unregisters a addon. 78 | * 79 | * @since 2.0.0 80 | * @access public 81 | * @param string $name 82 | * @return void 83 | */ 84 | function members_unregister_addon( $name ) { 85 | 86 | members_addon_registry()->unregister( $name ); 87 | } 88 | 89 | /** 90 | * Returns a addon object. 91 | * 92 | * @since 2.0.0 93 | * @access public 94 | * @param string $name 95 | * @return object 96 | */ 97 | function members_get_addon( $name ) { 98 | 99 | return members_addon_registry()->get( $name ); 100 | } 101 | 102 | /** 103 | * Checks if a addon object exists. 104 | * 105 | * @since 2.0.0 106 | * @access public 107 | * @param string $name 108 | * @return bool 109 | */ 110 | function members_addon_exists( $name ) { 111 | 112 | return members_addon_registry()->exists( $name ); 113 | } 114 | -------------------------------------------------------------------------------- /admin/functions-admin.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Register scripts/styles. 14 | add_action( 'admin_enqueue_scripts', 'members_admin_register_scripts', 0 ); 15 | add_action( 'admin_enqueue_scripts', 'members_admin_register_styles', 0 ); 16 | 17 | /** 18 | * Get an Underscore JS template. 19 | * 20 | * @since 1.0.0 21 | * @access public 22 | * @param string $name 23 | * @return bool 24 | */ 25 | function members_get_underscore_template( $name ) { 26 | require_once( members_plugin()->dir . "admin/tmpl/{$name}.php" ); 27 | } 28 | 29 | /** 30 | * Registers custom plugin scripts. 31 | * 32 | * @since 1.0.0 33 | * @access public 34 | * @return void 35 | */ 36 | function members_admin_register_scripts() { 37 | 38 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 39 | 40 | wp_register_script( 'members-settings', members_plugin()->uri . "js/settings{$min}.js", array( 'jquery' ), '', true ); 41 | wp_register_script( 'members-edit-post', members_plugin()->uri . "js/edit-post{$min}.js", array( 'jquery' ), '', true ); 42 | wp_register_script( 'members-edit-role', members_plugin()->uri . "js/edit-role{$min}.js", array( 'postbox', 'wp-util' ), '', true ); 43 | 44 | // Localize our script with some text we want to pass in. 45 | $i18n = array( 46 | 'button_role_edit' => esc_html__( 'Edit', 'members' ), 47 | 'button_role_ok' => esc_html__( 'OK', 'members' ), 48 | 'label_grant_cap' => esc_html__( 'Grant %s capability', 'members' ), 49 | 'label_deny_cap' => esc_html__( 'Deny %s capability', 'members' ), 50 | 'ays_delete_role' => esc_html__( 'Are you sure you want to delete this role? This is a permanent action and cannot be undone.', 'members' ), 51 | 'hidden_caps' => members_get_hidden_caps() 52 | ); 53 | 54 | wp_localize_script( 'members-edit-role', 'members_i18n', $i18n ); 55 | } 56 | 57 | /** 58 | * Registers custom plugin scripts. 59 | * 60 | * @since 1.0.0 61 | * @access public 62 | * @return void 63 | */ 64 | function members_admin_register_styles() { 65 | 66 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 67 | 68 | wp_register_style( 'members-admin', members_plugin()->uri . "css/admin{$min}.css" ); 69 | } 70 | 71 | /** 72 | * Function for safely deleting a role and transferring the deleted role's users to the default 73 | * role. Note that this function can be extremely intensive. Whenever a role is deleted, it's 74 | * best for the site admin to assign the user's of the role to a different role beforehand. 75 | * 76 | * @since 0.2.0 77 | * @access public 78 | * @param string $role 79 | * @return void 80 | */ 81 | function members_delete_role( $role ) { 82 | 83 | // Get the default role. 84 | $default_role = get_option( 'default_role' ); 85 | 86 | // Don't delete the default role. Site admins should change the default before attempting to delete the role. 87 | if ( $role == $default_role ) 88 | return; 89 | 90 | // Get all users with the role to be deleted. 91 | $users = get_users( array( 'role' => $role ) ); 92 | 93 | // Check if there are any users with the role we're deleting. 94 | if ( is_array( $users ) ) { 95 | 96 | // If users are found, loop through them. 97 | foreach ( $users as $user ) { 98 | 99 | // If the user has the role and no other roles, set their role to the default. 100 | if ( $user->has_cap( $role ) && 1 >= count( $user->roles ) ) 101 | $user->set_role( $default_role ); 102 | 103 | // Else, remove the role. 104 | else if ( $user->has_cap( $role ) ) 105 | $user->remove_role( $role ); 106 | } 107 | } 108 | 109 | // Remove the role. 110 | remove_role( $role ); 111 | 112 | // Remove the role from the role factory. 113 | members_unregister_role( $role ); 114 | } 115 | 116 | /** 117 | * Returns an array of all the user meta keys in the $wpdb->usermeta table. 118 | * 119 | * @since 0.2.0 120 | * @access public 121 | * @global object $wpdb 122 | * @return array 123 | */ 124 | function members_get_user_meta_keys() { 125 | global $wpdb; 126 | 127 | return $wpdb->get_col( "SELECT meta_key FROM $wpdb->usermeta GROUP BY meta_key ORDER BY meta_key" ); 128 | } 129 | -------------------------------------------------------------------------------- /admin/functions-help.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | /** 14 | * Help sidebar for all of the help tabs. 15 | * 16 | * @since 1.0.0 17 | * @access public 18 | * @return string 19 | */ 20 | function members_get_help_sidebar_text() { 21 | 22 | // Get docs and help links. 23 | $docs_link = sprintf( '
  • %s
  • ', esc_html__( 'Documentation', 'members' ) ); 24 | $help_link = sprintf( '
  • %s
  • ', esc_html__( 'Support Forums', 'members' ) ); 25 | 26 | // Return the text. 27 | return sprintf( 28 | '

    %s

    ', 29 | esc_html__( 'For more information:', 'members' ), 30 | $docs_link, 31 | $help_link 32 | ); 33 | } 34 | 35 | /** 36 | * Edit role overview help tab args. 37 | * 38 | * @since 1.0.0 39 | * @access public 40 | * @return array 41 | */ 42 | function members_get_edit_role_help_overview_args() { 43 | 44 | return array( 45 | 'id' => 'overview', 46 | 'title' => esc_html__( 'Overview', 'members' ), 47 | 'callback' => 'members_edit_role_help_overview_cb' 48 | ); 49 | } 50 | 51 | /** 52 | * Edit role name help tab args. 53 | * 54 | * @since 1.0.0 55 | * @access public 56 | * @return array 57 | */ 58 | function members_get_edit_role_help_role_name_args() { 59 | 60 | return array( 61 | 'id' => 'role-name', 62 | 'title' => esc_html__( 'Role Name', 'members' ), 63 | 'callback' => 'members_edit_role_help_role_name_cb' 64 | ); 65 | } 66 | 67 | /** 68 | * Edit role edit caps help tab args. 69 | * 70 | * @since 1.0.0 71 | * @access public 72 | * @return array 73 | */ 74 | function members_get_edit_role_help_edit_caps_args() { 75 | 76 | return array( 77 | 'id' => 'edit-capabilities', 78 | 'title' => esc_html__( 'Edit Capabilities', 'members' ), 79 | 'callback' => 'members_edit_role_help_edit_caps_cb' 80 | ); 81 | } 82 | 83 | /** 84 | * Edit role custom cap help tab args. 85 | * 86 | * @since 1.0.0 87 | * @access public 88 | * @return array 89 | */ 90 | function members_get_edit_role_help_custom_cap_args() { 91 | 92 | return array( 93 | 'id' => 'custom-capability', 94 | 'title' => esc_html__( 'Custom Capability', 'members' ), 95 | 'callback' => 'members_edit_role_help_custom_cap_cb' 96 | ); 97 | } 98 | 99 | /** 100 | * Edit role overview help tab callback function. 101 | * 102 | * @since 1.0.0 103 | * @access public 104 | * @return void 105 | */ 106 | function members_edit_role_help_overview_cb() { ?> 107 | 108 |

    109 | 110 |

    111 | 112 |

    113 | ' . esc_html__( 'Roles and Capabilities', 'members' ) . '' 116 | ); ?> 117 |

    118 | 128 | 129 |

    130 | 131 |

    132 | 133 |

    134 | 135 |

    136 | 146 | 147 |

    148 | 149 |

    150 | 151 | 156 | 166 | 167 |

    168 | 169 |

    170 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Register settings views. 14 | add_action( 'members_register_settings_views', 'members_register_default_settings_views', 5 ); 15 | 16 | /** 17 | * Registers the plugin's built-in settings views. 18 | * 19 | * @since 2.0.0 20 | * @access public 21 | * @param object $manager 22 | * @return void 23 | */ 24 | function members_register_default_settings_views( $manager ) { 25 | 26 | // Bail if not on the settings screen. 27 | if ( 'members-settings' !== $manager->name ) 28 | return; 29 | 30 | // Register general settings view (default view). 31 | $manager->register_view( 32 | new \Members\Admin\View_General( 33 | 'general', 34 | array( 35 | 'label' => esc_html__( 'General', 'members' ), 36 | 'priority' => 0 37 | ) 38 | ) 39 | ); 40 | 41 | // Register add-ons view. 42 | $manager->register_view( 43 | new \Members\Admin\View_Addons( 44 | 'add-ons', 45 | array( 46 | 'label' => esc_html__( 'Add-Ons', 'members' ), 47 | 'priority' => 95 48 | ) 49 | ) 50 | ); 51 | 52 | // Register add-ons view. 53 | $manager->register_view( 54 | new \Members\Admin\View_Donate( 55 | 'donate', 56 | array( 57 | 'label' => esc_html__( 'Help Fund Version 3.0', 'members' ), 58 | 'priority' => 100 59 | ) 60 | ) 61 | ); 62 | } 63 | 64 | /** 65 | * Conditional function to check if on the plugin's settings page. 66 | * 67 | * @since 2.0.0 68 | * @access public 69 | * @return bool 70 | */ 71 | function members_is_settings_page() { 72 | 73 | $screen = get_current_screen(); 74 | 75 | return is_object( $screen ) && 'settings_page_members-settings' === $screen->id; 76 | } 77 | 78 | /** 79 | * Returns the URL to the settings page. 80 | * 81 | * @since 2.0.0 82 | * @access public 83 | * @return string 84 | */ 85 | function members_get_settings_page_url() { 86 | 87 | return add_query_arg( array( 'page' => 'members-settings' ), admin_url( 'options-general.php' ) ); 88 | } 89 | 90 | /** 91 | * Returns the URL to a settings view page. 92 | * 93 | * @since 2.0.0 94 | * @access public 95 | * @param string $view 96 | * @return string 97 | */ 98 | function members_get_settings_view_url( $view ) { 99 | 100 | return add_query_arg( array( 'view' => sanitize_key( $view ) ), members_get_settings_page_url() ); 101 | } 102 | 103 | /** 104 | * Returns the current settings view name. 105 | * 106 | * @since 2.0.0 107 | * @access public 108 | * @return string 109 | */ 110 | function members_get_current_settings_view() { 111 | 112 | if ( ! members_is_settings_page() ) 113 | return ''; 114 | 115 | return isset( $_GET['view'] ) ? sanitize_key( $_GET['view'] ) : 'general'; 116 | } 117 | 118 | /** 119 | * Conditional function to check if on a specific settings view page. 120 | * 121 | * @since 2.0.0 122 | * @access public 123 | * @param string $view 124 | * @return bool 125 | */ 126 | function members_is_settings_view( $view = '' ) { 127 | 128 | return members_is_settings_page() && $view === members_get_current_settings_view(); 129 | } 130 | -------------------------------------------------------------------------------- /admin/tmpl/cap-control.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | ?> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{{ data.label.grant }}} 21 | checked="checked"<# } #> /> 22 | 23 | 24 | 25 | {{{ data.label.deny }}} 26 | checked="checked"<# } #> /> 27 | 28 | 29 | -------------------------------------------------------------------------------- /admin/tmpl/cap-section.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | ?> 13 |
    14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
    35 |
    36 | -------------------------------------------------------------------------------- /admin/views/class-view-addons.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Sets up and handles the add-ons settings view. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | class View_Addons extends View { 22 | 23 | /** 24 | * Enqueues scripts/styles. 25 | * 26 | * @since 2.2.0 27 | * @access public 28 | * @return void 29 | */ 30 | public function enqueue() { 31 | wp_enqueue_style( 'members-admin' ); 32 | } 33 | 34 | /** 35 | * Renders the settings page. 36 | * 37 | * @since 2.0.0 38 | * @access public 39 | * @return void 40 | */ 41 | public function template() { 42 | 43 | require_once( members_plugin()->dir . 'admin/class-addon.php' ); 44 | require_once( members_plugin()->dir . 'admin/functions-addons.php' ); 45 | 46 | do_action( 'members_register_addons' ); 47 | 48 | $addons = members_get_addons(); ?> 49 | 50 |
    51 | 52 |
    53 | 54 |
    55 | 56 |
    57 | 62 | 63 |
    64 |

    65 | 66 |

    67 |

    68 | 69 |

    70 |

    71 | 72 |

    73 |
    74 |
    75 | 76 |
    77 | 78 |
    79 | 80 |
    81 | 82 |
    83 | 84 | 85 | 86 | 87 | 88 | addon_card( $addon ); ?> 89 | 90 | 91 | 92 | 93 | 94 |
    95 |

    96 | 97 |

    98 |
    99 | 100 | 101 | 102 |
    103 | 113 | 114 |
    115 | 116 |
    117 | 118 | 143 | 144 |
    145 | excerpt ) ); ?> 146 |
    147 | 148 |
    149 | 150 |
    151 | 152 | add_help_tab( 168 | array( 169 | 'id' => 'overview', 170 | 'title' => esc_html__( 'Overview', 'members' ), 171 | 'callback' => array( $this, 'help_tab_overview' ) 172 | ) 173 | ); 174 | 175 | // Roles/Caps help tab. 176 | $screen->add_help_tab( 177 | array( 178 | 'id' => 'download', 179 | 'title' => esc_html__( 'Download', 'members' ), 180 | 'callback' => array( $this, 'help_tab_download' ) 181 | ) 182 | ); 183 | 184 | // Roles/Caps help tab. 185 | $screen->add_help_tab( 186 | array( 187 | 'id' => 'purchase', 188 | 'title' => esc_html__( 'Purchase', 'members' ), 189 | 'callback' => array( $this, 'help_tab_purchase' ) 190 | ) 191 | ); 192 | 193 | // Set the help sidebar. 194 | $screen->set_help_sidebar( members_get_help_sidebar_text() ); 195 | } 196 | 197 | /** 198 | * Displays the overview help tab. 199 | * 200 | * @since 2.0.0 201 | * @access public 202 | * @return void 203 | */ 204 | public function help_tab_overview() { ?> 205 | 206 |

    207 | 208 |

    209 | 219 | 220 |

    221 | 222 |

    223 | 233 | 234 |

    235 | 236 |

    237 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Sets up and handles the donate settings view. 17 | * 18 | * @since 2.2.0 19 | * @access public 20 | */ 21 | class View_Donate extends View { 22 | 23 | /** 24 | * Enqueues scripts/styles. 25 | * 26 | * @since 2.2.0 27 | * @access public 28 | * @return void 29 | */ 30 | public function enqueue() { 31 | wp_enqueue_style( 'members-admin' ); 32 | } 33 | 34 | /** 35 | * Renders the settings page. 36 | * 37 | * @since 2.2.0 38 | * @access public 39 | * @return void 40 | */ 41 | public function template() { ?> 42 | 43 |
    44 | 45 |
    46 | 47 |
    48 | 49 |

    50 | 51 |

    52 | 53 |

    54 | 55 |

    56 | 57 |

    58 | 59 |

    60 | 61 |

    62 | 63 |

    64 |

    65 | 66 |

    67 | 68 |
    69 | 70 |
    71 | 72 |
    73 | 74 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members\Admin; 14 | 15 | /** 16 | * Settings view base class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | abstract class View { 22 | 23 | /** 24 | * Name/ID for the group. 25 | * 26 | * @since 2.0.0 27 | * @access protected 28 | * @var string 29 | */ 30 | public $name = ''; 31 | 32 | /** 33 | * Internationalized text label for the group. 34 | * 35 | * @since 2.0.0 36 | * @access protected 37 | * @var string 38 | */ 39 | public $label = ''; 40 | 41 | /** 42 | * Priority (order) the control should be output. 43 | * 44 | * @since 2.0.0 45 | * @access public 46 | * @var int 47 | */ 48 | public $priority = 10; 49 | 50 | /** 51 | * A user role capability required to show the control. 52 | * 53 | * @since 2.0.0 54 | * @access public 55 | * @var string|array 56 | */ 57 | public $capability = 'manage_options'; 58 | 59 | /** 60 | * Magic method to use in case someone tries to output the object as a string. 61 | * We'll just return the name. 62 | * 63 | * @since 2.0.0 64 | * @access public 65 | * @return string 66 | */ 67 | public function __toString() { 68 | return $this->name; 69 | } 70 | 71 | /** 72 | * Register a new object. 73 | * 74 | * @since 2.0.0 75 | * @access public 76 | * @param string $name 77 | * @param array $args { 78 | * @type string $label Internationalized text label. 79 | * @type string $icon Dashicon icon in the form of `dashicons-icon-name`. 80 | * @type string $callback Callback function for outputting the content for the view. 81 | * } 82 | * @return void 83 | */ 84 | public function __construct( $name, $args = array() ) { 85 | 86 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 87 | 88 | if ( isset( $args[ $key ] ) ) 89 | $this->$key = $args[ $key ]; 90 | } 91 | 92 | $this->name = sanitize_key( $name ); 93 | } 94 | 95 | /** 96 | * Runs on the `load-{$page}` hook 97 | * 98 | * @since 2.0.0 99 | * @access public 100 | * @return void 101 | */ 102 | public function load() {} 103 | 104 | /** 105 | * Enqueue scripts/styles for the control. 106 | * 107 | * @since 2.0.0 108 | * @access public 109 | * @return void 110 | */ 111 | public function enqueue() {} 112 | 113 | /** 114 | * Register settings for the view. 115 | * 116 | * @since 2.0.0 117 | * @access public 118 | * @return void 119 | */ 120 | public function register_settings() {} 121 | 122 | /** 123 | * Add help tabs for the view. 124 | * 125 | * @since 2.0.0 126 | * @access public 127 | * @return void 128 | */ 129 | public function add_help_tabs() {} 130 | 131 | /** 132 | * Output the content for the view. 133 | * 134 | * @since 2.0.0 135 | * @access public 136 | * @return void 137 | */ 138 | public function template() {} 139 | 140 | /** 141 | * Checks if the control should be allowed at all. 142 | * 143 | * @since 2.0.0 144 | * @access public 145 | * @return bool 146 | */ 147 | public function check_capabilities() { 148 | 149 | if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) 150 | return false; 151 | 152 | return true; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "justintadlock/members", 3 | "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.", 4 | "keywords" : ["wordpress"], 5 | "homepage" : "https://themehybrid.com/plugins/members", 6 | "license" : "GPL-2.0+", 7 | "type" : "wordpress-plugin", 8 | "authors" : [ 9 | { 10 | "name" : "Justin Tadlock", 11 | "email" : "justintadlock@gmail.com", 12 | "homepage" : "http://justintadlock.com" 13 | } 14 | ], 15 | "require" : { 16 | "composer/installers" : "^1.0", 17 | "php" : ">=5.3.0" 18 | }, 19 | "support" : { 20 | "issues": "https://github.com/justintadlock/members/issues", 21 | "forum" : "https://themehybrid.com/board/topics" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | The code for the project is handled via its [GitHub Repository](https://github.com/justintadlock/members). You can open tickets, create patches, and send pull requests there. 4 | 5 | ## Pull requests 6 | 7 | Problem first. Solution second. 8 | 9 | Pull requests should have a ticket open for discussion first. I rarely accept pull requests that aren't for a specific issue for various reasons. It's far better to post an issue and let me or the community provide feedback prior to creating a pull request. 10 | 11 | Please don't make pull requests against the `master` branch. This is the latest, stable code. You can make a pull request against one of the point branches or the `dev` (future release) branch. 12 | 13 | ## Coding standards 14 | 15 | In general, the project follows all WordPress [coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards). There are instances where it doesn't, opting for personal choices of my own, but in terms of contributing, following the WordPress standards is best practice. 16 | 17 | ## Script and style files 18 | 19 | The project consists of several script and style files. When making patches or pull requests with changes to these files, only do so to the primary file. Don't create patches for the minified (`.min`) versions of the files. Those will be minified after a patch is merged into the code base. 20 | 21 | ## Language 22 | 23 | All text strings follow U.S. English by default. While such guides are generally unneeded, in cases where style considerations are necessary, these will typically follow conventions laid out in *Elements of Style* or the *AP Stylebook*. 24 | 25 | ## Licensing 26 | 27 | Any code contributed to the project via patches, pull requests, or other means will be licensed under the [GPL version 2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) or later. By contributing code to the project, you provide consent to use such code under this license. The exception to this rule is when bringing in third-party code with an alternate open source license. 28 | 29 | ## Versioning 30 | 31 | The project uses [semantic versioning](http://semver.org). Version numbers will look like `3.2.1` where `3` is the "major" release, `2` is the minor release, and `1` is the patch release. -------------------------------------------------------------------------------- /css/admin.css: -------------------------------------------------------------------------------- 1 | /* ====== Members Settings Screen. ====== */ 2 | 3 | .settings_page_members-settings .welcome-panel { 4 | margin-top: 0; 5 | padding: 40px 20px; 6 | } 7 | 8 | .settings_page_members-settings .welcome-panel .about-description { 9 | margin: 20px 0; 10 | } 11 | 12 | .settings_page_members-settings .welcome-panel-content { 13 | margin: 0; 14 | } 15 | 16 | @media screen and (min-width: 870px) { 17 | 18 | .settings_page_members-settings .members-short-p { 19 | max-width: 612px; 20 | margin-left: auto; 21 | margin-right: auto; 22 | } 23 | 24 | .settings_page_members-settings .welcome-panel-content { 25 | text-align: center; 26 | } 27 | } 28 | 29 | .settings_page_members-settings .welcome-panel .button.button-hero { 30 | margin-left: auto; 31 | margin-right: auto; 32 | } 33 | 34 | .settings_page_members-settings .members-svg-wrap { 35 | display: inline-block; 36 | margin: 0 auto 20px; 37 | } 38 | 39 | @media screen and (max-width: 870px) { 40 | .settings_page_members-settings .welcome-panel { 41 | padding: 20px; 42 | } 43 | 44 | .settings_page_members-settings .members-svg-wrap { 45 | float: left; 46 | margin-right: 28px; 47 | } 48 | 49 | .settings_page_members-settings.rtl .members-svg-wrap { 50 | float: right; 51 | margin-right: 0; 52 | margin-left: 28px; 53 | } 54 | } 55 | 56 | .settings_page_members-settings .members-svg-link { 57 | display: table-cell; 58 | text-align: center; 59 | width: 128px; 60 | height: 128px; 61 | background: #363b3f; 62 | color: #fff; 63 | padding: 24px 16px 16px; 64 | border-radius: 50%; 65 | box-sizing: border-box; 66 | border: 4px solid #ffffff; 67 | box-shadow: 0 0 0 4px #363b3f; 68 | } 69 | 70 | .settings_page_members-settings .members-svg-link svg { 71 | max-width: 84px; 72 | max-height: 64px; 73 | width: auto; 74 | height: auto; 75 | } 76 | 77 | /* ====== Manage Roles Screen. ====== */ 78 | 79 | @media only screen and ( min-width: 783px ) { 80 | 81 | .users_page_roles .column-users, 82 | .users_page_roles .column-granted_caps, 83 | .users_page_roles .column-denied_caps { 84 | width: 100px; 85 | text-align: center; 86 | } 87 | } 88 | 89 | /* ====== Edit/New Role Screen. ====== */ 90 | 91 | /* === Role and Role Name === */ 92 | 93 | .members-title-div #titlewrap input { 94 | padding: 0 8px; 95 | font-size: 1.7em; 96 | line-height: normal; 97 | height: 1.7em; 98 | width: 100%; 99 | outline: none; 100 | margin: 0 0 3px; 101 | background-color: #fff; 102 | } 103 | 104 | .members-title-div input[name="role"] { 105 | font-size: 13px; 106 | height: 22px; 107 | margin: 0; 108 | width: 16em; 109 | } 110 | 111 | /* === Edit Capabilities Tabs === */ 112 | 113 | /* Wrapper box */ 114 | 115 | #tabcapsdiv { margin-top: 1em; } 116 | 117 | #tabcapsdiv > .hndle { 118 | padding: 10px; 119 | border-bottom: 1px solid #eee; 120 | } 121 | 122 | #tabcapsdiv .inside, 123 | #members-cp .inside { 124 | margin: 0; 125 | padding: 0; 126 | } 127 | 128 | /* Tabs wrapper. */ 129 | 130 | .members-cap-tabs, 131 | .members-tabs { 132 | overflow: hidden; 133 | background: #fff; 134 | background: linear-gradient( 90deg, #fafafa 0%, #fafafa 20%, #fff 20%, #fff 100% ); 135 | } 136 | 137 | @media only screen and ( max-width: 782px ) { 138 | 139 | .members-cap-tabs, 140 | .members-tabs { 141 | background: linear-gradient( 90deg, #fafafa 0%, #fafafa 48px, #fff 48px, #fff 100% ); 142 | } 143 | } 144 | 145 | /* Tab nav. */ 146 | 147 | .members-cap-tabs .members-tab-nav, 148 | .members-tabs .members-tab-nav { 149 | position: relative; 150 | float: left; 151 | list-style: none; 152 | width: 20%; 153 | line-height: 1em; 154 | margin: 0 0 -1px 0; 155 | padding: 0; 156 | background-color: #fafafa; 157 | border-right: 1px solid #eee; 158 | box-sizing: border-box; 159 | } 160 | 161 | .members-cap-tabs .members-tab-nav li, 162 | .members-tabs .members-tab-nav li { 163 | display: block; 164 | position: relative; 165 | margin: 0; 166 | padding: 0; 167 | line-height: 20px; 168 | } 169 | 170 | .members-cap-tabs .members-tab-nav li a, 171 | .members-tabs .members-tab-nav li a { 172 | display: block; 173 | margin: 0; 174 | padding: 10px; 175 | line-height: 20px !important; 176 | text-decoration: none; 177 | border-bottom: 1px solid #eee; 178 | box-shadow: none; 179 | } 180 | 181 | .members-cap-tabs .members-tab-nav li a .dashicons, 182 | .members-tabs .members-tab-nav li a .dashicons { 183 | line-height: 20px; 184 | margin-right: 3px; 185 | } 186 | 187 | .members-cap-tabs .members-tab-nav li[aria-selected="true"] a, 188 | .members-tabs .members-tab-nav li[aria-selected="true"] a { 189 | position: relative; 190 | font-weight: bold; 191 | color: #555; 192 | background-color: #e0e0e0; 193 | } 194 | 195 | @media only screen and ( max-width: 782px ) { 196 | 197 | .members-cap-tabs .members-tab-nav, 198 | .members-tabs .members-tab-nav { width: 48px; } 199 | 200 | .members-cap-tabs .members-tab-nav li a .dashicons, 201 | .members-tabs .members-tab-nav li a .dashicons { 202 | width: 24px; 203 | height: 24px; 204 | font-size: 24px; 205 | line-height: 24px; 206 | } 207 | 208 | .members-tab-nav li .dashicons::before, 209 | .members-tab-nav li .dashicons::before { 210 | width: 24px; 211 | height: 24px; 212 | } 213 | 214 | .members-tab-nav li .label { 215 | overflow: hidden; 216 | position: absolute; 217 | top: -1000em; 218 | left: -1000em; 219 | width: 1px; 220 | height: 1px; 221 | } 222 | } 223 | 224 | /* Tab content wrapper */ 225 | 226 | .members-cap-tabs .members-tab-wrap, 227 | .members-tabs .members-tab-wrap { 228 | float: left; 229 | width: 80%; 230 | margin-left: -1px; 231 | } 232 | 233 | @media only screen and ( max-width: 782px ) { 234 | 235 | .members-cap-tabs .members-tab-wrap, 236 | .members-tabs .members-tab-wrap { 237 | width: calc( 100% - 48px ); 238 | } 239 | } 240 | 241 | #members-cp .members-tab-content { 242 | padding: 10px; 243 | border-left: 1px solid #e5e5e5; 244 | } 245 | 246 | /* === Manager when in the side meta box. === */ 247 | 248 | @media only screen and ( min-width: 850px ) { 249 | 250 | #side-sortables .members-tabs { background: #fff; } 251 | 252 | #side-sortables .members-tabs .members-tab-wrap { width: 100%; } 253 | 254 | #side-sortables .members-tabs .members-tab-nav { 255 | display: table; 256 | width: 100%; 257 | } 258 | 259 | #side-sortables .members-tabs .members-tab-nav li { 260 | display: table-cell; 261 | text-align: center; 262 | border-right: 1px solid #eee; 263 | } 264 | 265 | #side-sortables .members-tabs .members-tab-nav li:last-of-type { border-right: none; } 266 | 267 | #side-sortables .members-tabs .members-tab-nav li a { 268 | padding: 10px 0; 269 | } 270 | 271 | #side-sortables .members-tabs .members-tab-nav .dashicons { 272 | width: 24px; 273 | height: 24px; 274 | font-size: 24px; 275 | line-height: 24px; 276 | } 277 | 278 | #side-sortables .members-tabs .members-tab-nav .dashicons::before { 279 | width: 24px; 280 | height: 24px; 281 | } 282 | 283 | #side-sortables .members-tabs .members-tab-nav .label { 284 | overflow: hidden; 285 | position: absolute; 286 | top: -1000em; 287 | left: -1000em; 288 | width: 1px; 289 | height: 1px; 290 | } 291 | } 292 | 293 | 294 | .members-tabs .members-tabs-label { 295 | display : block !important; 296 | font-weight : bold; 297 | display : inline-block; 298 | margin-bottom : 4px; 299 | } 300 | 301 | .members-tabs .butterbean-control-checkbox .members-tabs-label { 302 | display: inline !important; 303 | } 304 | 305 | .members-tabs .members-tabs-description { 306 | display : block; 307 | font-style : italic; 308 | margin-top : 4px; 309 | } 310 | 311 | .members-tabs .members-tabs-label + .members-tabs-description { 312 | margin-top : 0; 313 | margin-bottom : 4px; 314 | } 315 | 316 | /* Tab tables. */ 317 | 318 | #tabcapsdiv table { 319 | border-right: none; 320 | border-top: none; 321 | border-bottom: none; 322 | } 323 | 324 | /* Tabs table columns. */ 325 | 326 | #tabcapsdiv table td, 327 | #tabcapsdiv table th { 328 | padding: 10px; 329 | padding-bottom: 10px; 330 | border-bottom: 1px solid #eee; 331 | font-size: 13px; 332 | line-height: 20px; 333 | } 334 | 335 | #tabcapsdiv table td { padding: 9px; } 336 | 337 | #tabcapsdiv tbody tr:last-of-type td { border-bottom: none; } 338 | 339 | #tabcapsdiv tfoot th { border-color: #eee; } 340 | 341 | @media only screen and ( max-width: 782px ) { 342 | 343 | #tabcapsdiv table td, 344 | #tabcapsdiv table th { 345 | line-height: 24px; 346 | } 347 | } 348 | 349 | /* Grant/Deny columns. */ 350 | 351 | .members-roles-select .column-grant, 352 | .members-roles-select .column-deny { 353 | width: 70px !important; 354 | text-align: center; 355 | display: table-cell !important; 356 | clear: none !important; 357 | } 358 | 359 | /* Cap grant/deny button. */ 360 | 361 | .members-cap-tabs button { 362 | display: inline; 363 | margin: -4px; 364 | line-height: inherit; 365 | padding: 4px 8px; 366 | border: 1px solid transparent; 367 | background: transparent; 368 | border-radius: 0; 369 | outline: none; 370 | 371 | -webkit-transition: all 0.25s ease-out; 372 | -moz-transition: all 0.25s ease-out; 373 | -o-transition: all 0.25s ease-out; 374 | transition: all 0.25s ease-out; 375 | } 376 | 377 | .members-cap-tabs button:hover, 378 | .members-cap-tabs button:focus { 379 | border-color: #eee; 380 | background: #fafafa; 381 | cursor: pointer; 382 | } 383 | 384 | .members-cap-tabs button:active { 385 | color: #0073aa; 386 | border-color: #0073aa; 387 | } 388 | 389 | .members-cap-tabs button + .dashicons { 390 | display: none; 391 | margin-top: 1px; 392 | margin-bottom: -1px; 393 | line-height: inherit; 394 | } 395 | 396 | .members-cap-tabs button:hover + .dashicons, 397 | .members-cap-tabs button:focus + .dashicons { 398 | display: inline-block; 399 | } 400 | 401 | 402 | /* New cap highlight */ 403 | 404 | .members-tab-content .members-highlight { 405 | background-color: rgba( 0, 115, 170, 0.05 ); 406 | } 407 | 408 | .members-tab-content tbody { 409 | -webkit-transition: all 2s ease-in-out; 410 | -moz-transition: all 2s ease-in-out; 411 | -o-transition: all 2s ease-in-out; 412 | transition: all 2s ease-in-out; 413 | } 414 | 415 | /* ====== Edit Post ====== */ 416 | 417 | /* Role checkboxes in the Content Permissions meta box. */ 418 | 419 | .members-cp-role-list-wrap { 420 | overflow: auto; 421 | min-height: 42px; 422 | max-height: 200px; 423 | padding: 0 0.9em; 424 | border: solid 1px rgb(223, 223, 223); 425 | background-color: rgb(253, 253, 253); 426 | } 427 | -------------------------------------------------------------------------------- /css/admin.min.css: -------------------------------------------------------------------------------- 1 | .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)} 2 | -------------------------------------------------------------------------------- /img/icon-addon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justintadlock/members/07a1eeebdb1efee61534885f4e087ac167d46736/img/icon-addon.png -------------------------------------------------------------------------------- /img/members-admin-access.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/members-block-permissions.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/members-core-create-caps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/members-privacy-caps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/members-role-hierarchy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/members-role-levels.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/members.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inc/class-cap-group.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members; 14 | 15 | /** 16 | * Capability group object class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Cap_Group { 22 | 23 | /** 24 | * Name/ID for the group. 25 | * 26 | * @since 2.0.0 27 | * @access public 28 | * @var string 29 | */ 30 | public $name = ''; 31 | 32 | /** 33 | * Internationalized text label for the group. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @var string 38 | */ 39 | public $label = ''; 40 | 41 | /** 42 | * Icon for the group. This can be a dashicons class or a custom class. 43 | * 44 | * @since 2.0.0 45 | * @access public 46 | * @var string 47 | */ 48 | public $icon = 'dashicons-admin-generic'; 49 | 50 | /** 51 | * Capabilities for the group. 52 | * 53 | * @since 2.0.0 54 | * @access public 55 | * @var array 56 | */ 57 | public $caps = array(); 58 | 59 | /** 60 | * Sort order priority. 61 | * 62 | * @since 2.0.0 63 | * @access public 64 | * @var int 65 | */ 66 | public $priority = 10; 67 | 68 | /** 69 | * Whether to remove previously-added caps from this group's caps. 70 | * 71 | * @since 2.0.0 72 | * @access public 73 | * @var bool 74 | */ 75 | public $diff_added = false; 76 | 77 | /** 78 | * Magic method to use in case someone tries to output the object as a string. 79 | * We'll just return the name. 80 | * 81 | * @since 2.0.0 82 | * @access public 83 | * @return string 84 | */ 85 | public function __toString() { 86 | return $this->name; 87 | } 88 | 89 | /** 90 | * Register a new object. 91 | * 92 | * @since 2.0.0 93 | * @access public 94 | * @param string $name 95 | * @param array $args { 96 | * @type string $label Internationalized text label. 97 | * @type string $icon Dashicon icon in the form of `dashicons-icon-name`. 98 | * @type array $caps Array of capabilities in the group. 99 | * @type bool $merge_added Whether to merge this caps into the added caps array. 100 | * @type bool $diff_added Whether to remove previously-added caps from this group. 101 | * } 102 | * @return void 103 | */ 104 | public function __construct( $name, $args = array() ) { 105 | 106 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 107 | 108 | if ( isset( $args[ $key ] ) ) 109 | $this->$key = $args[ $key ]; 110 | } 111 | 112 | $this->name = sanitize_key( $name ); 113 | 114 | $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => $this->name ) ) ); 115 | 116 | $this->caps = array_unique( array_merge( $this->caps, $registered_caps ) ); 117 | 118 | $this->caps = members_remove_hidden_caps( $this->caps ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /inc/class-capability.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members; 14 | 15 | /** 16 | * Capability class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | class Capability { 22 | 23 | /** 24 | * The capability name. 25 | * 26 | * @since 2.0.0 27 | * @access public 28 | * @var string 29 | */ 30 | public $name = ''; 31 | 32 | /** 33 | * The capability label. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @var string 38 | */ 39 | public $label = ''; 40 | 41 | /** 42 | * The group the capability belongs to. 43 | * 44 | * @see Members_Cap_Group 45 | * @since 2.0.0 46 | * @access public 47 | * @var string 48 | */ 49 | public $group = ''; 50 | 51 | /** 52 | * Return the role string in attempts to use the object as a string. 53 | * 54 | * @since 2.0.0 55 | * @access public 56 | * @return string 57 | */ 58 | public function __toString() { 59 | return $this->name; 60 | } 61 | 62 | /** 63 | * Creates a new role object. 64 | * 65 | * @since 2.0.0 66 | * @access public 67 | * @global object $wp_roles 68 | * @param string $role 69 | * @return void 70 | */ 71 | public function __construct( $name, $args = array() ) { 72 | 73 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 74 | 75 | if ( isset( $args[ $key ] ) ) 76 | $this->$key = $args[ $key ]; 77 | } 78 | 79 | $this->name = sanitize_key( $name ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /inc/class-registry.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members; 14 | 15 | /** 16 | * Base registry class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | class Registry { 22 | 23 | /** 24 | * Registry instances. 25 | * 26 | * @since 2.0.0 27 | * @access private 28 | * @var array 29 | */ 30 | private static $instances = array(); 31 | 32 | /** 33 | * Array of items in the collection. 34 | * 35 | * @since 2.0.0 36 | * @access protected 37 | * @var array 38 | */ 39 | protected $collection = array(); 40 | 41 | /** 42 | * Constructor method. 43 | * 44 | * @since 2.0.0 45 | * @access protected 46 | * @return void 47 | */ 48 | protected function __construct() {} 49 | 50 | /** 51 | * Lock down `__clone()`. 52 | * 53 | * @since 2.0.0 54 | * @access private 55 | * @return void 56 | */ 57 | private function __clone() {} 58 | 59 | /** 60 | * Lock down `__wakeup()`. 61 | * 62 | * @since 2.0.0 63 | * @access private 64 | * @return void 65 | */ 66 | private function __wakeup() {} 67 | 68 | /** 69 | * Register an item. 70 | * 71 | * @since 2.0.0 72 | * @access public 73 | * @param string $name 74 | * @param mixed $value 75 | * @return void 76 | */ 77 | public function register( $name, $value ) { 78 | 79 | if ( ! $this->exists( $name ) ) 80 | $this->collection[ $name ] = $value; 81 | } 82 | 83 | /** 84 | * Unregisters an item. 85 | * 86 | * @since 2.0.0 87 | * @access public 88 | * @param string $name 89 | * @return void 90 | */ 91 | public function unregister( $name ) { 92 | 93 | if ( $this->exists( $name ) ) 94 | unset( $this->collection[ $name ] ); 95 | } 96 | 97 | /** 98 | * Checks if an item exists. 99 | * 100 | * @since 2.0.0 101 | * @access public 102 | * @param string $name 103 | * @return bool 104 | */ 105 | public function exists( $name ) { 106 | 107 | return isset( $this->collection[ $name ] ); 108 | } 109 | 110 | /** 111 | * Returns an item. 112 | * 113 | * @since 2.0.0 114 | * @access public 115 | * @param string $name 116 | * @return mixed 117 | */ 118 | public function get( $name ) { 119 | 120 | return $this->exists( $name ) ? $this->collection[ $name ] : false; 121 | } 122 | 123 | /** 124 | * Returns the entire collection. 125 | * 126 | * @since 2.0.0 127 | * @access public 128 | * @return array 129 | */ 130 | public function get_collection() { 131 | 132 | return $this->collection; 133 | } 134 | 135 | /** 136 | * Returns the instance. 137 | * 138 | * @since 2.0.0 139 | * @access public 140 | * @return object 141 | */ 142 | final public static function get_instance( $name = '' ) { 143 | 144 | if ( ! isset( self::$instances[ $name ] ) ) 145 | self::$instances[ $name ] = new static(); 146 | 147 | return self::$instances[ $name ]; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /inc/class-role-group.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | namespace Members; 14 | 15 | /** 16 | * Role group object class. 17 | * 18 | * @since 2.0.0 19 | * @access public 20 | */ 21 | final class Role_Group { 22 | 23 | /** 24 | * Name/ID for the group. 25 | * 26 | * @since 2.0.0 27 | * @access public 28 | * @var string 29 | */ 30 | public $name = ''; 31 | 32 | /** 33 | * Internationalized text label for the group. 34 | * 35 | * @since 2.0.0 36 | * @access public 37 | * @var string 38 | */ 39 | public $label = ''; 40 | 41 | /** 42 | * Internationalized text label for the group + the count in the form of 43 | * `_n_noop( 'Singular Name %s', 'Plural Name %s', $textdomain )` 44 | * 45 | * @since 2.0.0 46 | * @access public 47 | * @var string 48 | */ 49 | public $label_count = ''; 50 | 51 | /** 52 | * Array of roles that belong to the group. 53 | * 54 | * @since 2.0.0 55 | * @access public 56 | * @var array 57 | */ 58 | public $roles = array(); 59 | 60 | /** 61 | * Whether to create a view for the group on the Manage Roles screen. 62 | * 63 | * @since 2.0.0 64 | * @access public 65 | * @var bool 66 | */ 67 | public $show_in_view_list = true; 68 | 69 | /** 70 | * Magic method to use in case someone tries to output the object as a string. 71 | * We'll just return the name. 72 | * 73 | * @since 2.0.0 74 | * @access public 75 | * @return string 76 | */ 77 | public function __toString() { 78 | return $this->name; 79 | } 80 | 81 | /** 82 | * Register a new object. 83 | * 84 | * @since 2.0.0 85 | * @access public 86 | * @param string $name 87 | * @param array $args 88 | * @return void 89 | */ 90 | public function __construct( $name, $args = array() ) { 91 | 92 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 93 | 94 | if ( isset( $args[ $key ] ) ) 95 | $this->$key = $args[ $key ]; 96 | } 97 | 98 | $this->name = sanitize_key( $name ); 99 | 100 | $registered_roles = array_keys( wp_list_filter( members_get_roles(), array( 'group' => $this->name ) ) ); 101 | 102 | $this->roles = array_unique( array_merge( $this->roles, $registered_roles ) ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /inc/class-role.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 10 | * @link https://themehybrid.com/plugins/members 11 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 12 | */ 13 | 14 | namespace Members; 15 | 16 | /** 17 | * Role class. 18 | * 19 | * @since 2.0.0 20 | * @access public 21 | */ 22 | class Role { 23 | 24 | /** 25 | * The role name. 26 | * 27 | * @since 2.0.0 28 | * @access public 29 | * @var string 30 | */ 31 | public $name = ''; 32 | 33 | /** 34 | * The role label. 35 | * 36 | * @since 2.0.0 37 | * @access public 38 | * @var string 39 | */ 40 | public $label = ''; 41 | 42 | /** 43 | * The group the role belongs to. 44 | * 45 | * @see Members\Role_Group 46 | * @since 2.0.0 47 | * @access public 48 | * @var string 49 | */ 50 | public $group = ''; 51 | 52 | /** 53 | * Whether the role has caps (granted). 54 | * 55 | * @since 2.0.0 56 | * @access public 57 | * @var bool 58 | */ 59 | public $has_caps = false; 60 | 61 | /** 62 | * Capability count for the role. 63 | * 64 | * @since 2.0.0 65 | * @access public 66 | * @var int 67 | */ 68 | public $granted_cap_count = 0; 69 | 70 | /** 71 | * Capability count for the role. 72 | * 73 | * @since 2.0.0 74 | * @access public 75 | * @var int 76 | */ 77 | public $denied_cap_count = 0; 78 | 79 | /** 80 | * Array of capabilities that the role has in the form of `array( $cap => $bool )`. 81 | * 82 | * @since 2.0.0 83 | * @access public 84 | * @var array 85 | */ 86 | public $caps = array(); 87 | 88 | /** 89 | * Array of granted capabilities that the role has. 90 | * 91 | * @since 2.0.0 92 | * @access public 93 | * @var array 94 | */ 95 | public $granted_caps = array(); 96 | 97 | /** 98 | * Array of denied capabilities that the role has. 99 | * 100 | * @since 2.0.0 101 | * @access public 102 | * @var array 103 | */ 104 | public $denied_caps = array(); 105 | 106 | /** 107 | * Return the role string in attempts to use the object as a string. 108 | * 109 | * @since 2.0.0 110 | * @access public 111 | * @return string 112 | */ 113 | public function __toString() { 114 | return $this->name; 115 | } 116 | 117 | /** 118 | * Creates a new role object. 119 | * 120 | * @since 2.0.0 121 | * @access public 122 | * @param string $role 123 | * @param array $args 124 | * @return void 125 | */ 126 | public function __construct( $name, $args = array() ) { 127 | 128 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 129 | 130 | if ( isset( $args[ $key ] ) ) 131 | $this->$key = $args[ $key ]; 132 | } 133 | 134 | $this->name = members_sanitize_role( $name ); 135 | 136 | if ( $this->caps ) { 137 | 138 | // Validate cap values as booleans in case they are stored as strings. 139 | $this->caps = array_map( 'members_validate_boolean', $this->caps ); 140 | 141 | // Get granted and denied caps. 142 | $this->granted_caps = array_keys( $this->caps, true ); 143 | $this->denied_caps = array_keys( $this->caps, false ); 144 | 145 | // Remove user levels from granted/denied caps. 146 | $this->granted_caps = members_remove_old_levels( $this->granted_caps ); 147 | $this->denied_caps = members_remove_old_levels( $this->denied_caps ); 148 | 149 | // Remove hidden caps from granted/denied caps. 150 | $this->granted_caps = members_remove_hidden_caps( $this->granted_caps ); 151 | $this->denied_caps = members_remove_hidden_caps( $this->denied_caps ); 152 | 153 | // Set the cap count. 154 | $this->granted_cap_count = count( $this->granted_caps ); 155 | $this->denied_cap_count = count( $this->denied_caps ); 156 | 157 | // Check if we have caps. 158 | $this->has_caps = 0 < $this->granted_cap_count; 159 | } 160 | } 161 | 162 | /** 163 | * Magic method for getting media object properties. Let's keep from failing if a theme 164 | * author attempts to access a property that doesn't exist. 165 | * 166 | * @since 2.0.2 167 | * @access public 168 | * @param string $property 169 | * @return mixed 170 | */ 171 | public function get( $property ) { 172 | 173 | if ( 'label' === $property ) 174 | return members_translate_role( $this->name ); 175 | 176 | return isset( $this->$property ) ? $this->$property : false; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /inc/functions-admin-bar.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Hook the members admin bar to 'wp_before_admin_bar_render'. 14 | add_action( 'wp_before_admin_bar_render', 'members_admin_bar' ); 15 | 16 | /** 17 | * Adds new menu items to the WordPress admin bar. 18 | * 19 | * @since 0.2.0 20 | * @access public 21 | * @global object $wp_admin_bar 22 | * @return void 23 | */ 24 | function members_admin_bar() { 25 | global $wp_admin_bar; 26 | 27 | // Check if the current user can 'create_roles'. 28 | if ( current_user_can( 'create_roles' ) ) { 29 | 30 | // Add a 'Role' menu item as a sub-menu item of the new content menu. 31 | $wp_admin_bar->add_menu( 32 | array( 33 | 'id' => 'members-new-role', 34 | 'parent' => 'new-content', 35 | 'title' => esc_attr__( 'Role', 'members' ), 36 | 'href' => esc_url( members_get_new_role_url() ) 37 | ) 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /inc/functions-cap-groups.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Registers default groups. 14 | add_action( 'init', 'members_register_cap_groups', 95 ); 15 | add_action( 'members_register_cap_groups', 'members_register_default_cap_groups', 5 ); 16 | 17 | /** 18 | * Fires the cap group registration action hook. 19 | * 20 | * @since 1.0.0 21 | * @access public 22 | * @return void 23 | */ 24 | function members_register_cap_groups() { 25 | 26 | // Hook for registering cap groups. Plugins should always register on this hook. 27 | do_action( 'members_register_cap_groups' ); 28 | } 29 | 30 | /** 31 | * Registers the default cap groups. 32 | * 33 | * @since 2.0.0 34 | * @access public 35 | * @return void 36 | */ 37 | function members_register_default_cap_groups() { 38 | 39 | // Registers the general group. 40 | members_register_cap_group( 'general', 41 | array( 42 | 'label' => esc_html__( 'General', 'members' ), 43 | 'icon' => 'dashicons-wordpress', 44 | 'priority' => 5 45 | ) 46 | ); 47 | 48 | // Loop through every custom post type. 49 | foreach ( get_post_types( array(), 'objects' ) as $type ) { 50 | 51 | // Skip revisions and nave menu items. 52 | if ( in_array( $type->name, array( 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset' ) ) ) 53 | continue; 54 | 55 | // Get the caps for the post type. 56 | $has_caps = members_get_post_type_group_caps( $type->name ); 57 | 58 | // Skip if the post type doesn't have caps. 59 | if ( empty( $has_caps ) ) 60 | continue; 61 | 62 | // Set the default post type icon. 63 | $icon = $type->hierarchical ? 'dashicons-admin-page' : 'dashicons-admin-post'; 64 | 65 | // Get the post type icon. 66 | if ( is_string( $type->menu_icon ) && preg_match( '/dashicons-/i', $type->menu_icon ) ) 67 | $icon = $type->menu_icon; 68 | 69 | else if ( 'attachment' === $type->name ) 70 | $icon = 'dashicons-admin-media'; 71 | 72 | else if ( 'download' === $type->name ) 73 | $icon = 'dashicons-download'; // EDD 74 | 75 | else if ( 'product' === $type->name ) 76 | $icon = 'dashicons-cart'; 77 | 78 | // Register the post type cap group. 79 | members_register_cap_group( "type-{$type->name}", 80 | array( 81 | 'label' => $type->labels->name, 82 | 'caps' => $has_caps, 83 | 'icon' => $icon, 84 | 'priority' => 10 85 | ) 86 | ); 87 | } 88 | 89 | // Register the taxonomy group. 90 | members_register_cap_group( 'taxonomy', 91 | array( 92 | 'label' => esc_html__( 'Taxonomies', 'members' ), 93 | 'caps' => members_get_taxonomy_group_caps(), 94 | 'icon' => 'dashicons-tag', 95 | 'diff_added' => true, 96 | 'priority' => 15 97 | ) 98 | ); 99 | 100 | // Register the theme group. 101 | members_register_cap_group( 'theme', 102 | array( 103 | 'label' => esc_html__( 'Appearance', 'members' ), 104 | 'icon' => 'dashicons-admin-appearance', 105 | 'priority' => 20 106 | ) 107 | ); 108 | 109 | // Register the plugin group. 110 | members_register_cap_group( 'plugin', 111 | array( 112 | 'label' => esc_html__( 'Plugins', 'members' ), 113 | 'icon' => 'dashicons-admin-plugins', 114 | 'priority' => 25 115 | ) 116 | ); 117 | 118 | // Register the user group. 119 | members_register_cap_group( 'user', 120 | array( 121 | 'label' => esc_html__( 'Users', 'members' ), 122 | 'icon' => 'dashicons-admin-users', 123 | 'priority' => 30 124 | ) 125 | ); 126 | 127 | // Register the custom group. 128 | members_register_cap_group( 'custom', 129 | array( 130 | 'label' => esc_html__( 'Custom', 'members' ), 131 | 'caps' => members_get_capabilities(), 132 | 'icon' => 'dashicons-admin-generic', 133 | 'diff_added' => true, 134 | 'priority' => 995 135 | ) 136 | ); 137 | } 138 | 139 | /** 140 | * Returns the instance of cap group registry. 141 | * 142 | * @since 2.0.0 143 | * @access public 144 | * @return object 145 | */ 146 | function members_cap_group_registry() { 147 | 148 | return \Members\Registry::get_instance( 'cap_group' ); 149 | } 150 | 151 | /** 152 | * Function for registering a cap group. 153 | * 154 | * @since 1.0.0 155 | * @access public 156 | * @param string $name 157 | * @param array $args 158 | * @return void 159 | */ 160 | function members_register_cap_group( $name, $args = array() ) { 161 | 162 | members_cap_group_registry()->register( $name, new \Members\Cap_Group( $name, $args ) ); 163 | } 164 | 165 | /** 166 | * Unregisters a group. 167 | * 168 | * @since 1.0.0 169 | * @access public 170 | * @param string $name 171 | * @return void 172 | */ 173 | function members_unregister_cap_group( $name ) { 174 | 175 | members_cap_group_registry()->unregister( $name ); 176 | } 177 | 178 | /** 179 | * Checks if a group exists. 180 | * 181 | * @since 1.0.0 182 | * @access public 183 | * @param string $name 184 | * @return bool 185 | */ 186 | function members_cap_group_exists( $name ) { 187 | 188 | return members_cap_group_registry()->exists( $name ); 189 | } 190 | 191 | /** 192 | * Returns an array of registered group objects. 193 | * 194 | * @since 1.0.0 195 | * @access public 196 | * @return array 197 | */ 198 | function members_get_cap_groups() { 199 | 200 | return members_cap_group_registry()->get_collection(); 201 | } 202 | 203 | /** 204 | * Returns a group object if it exists. Otherwise, `FALSE`. 205 | * 206 | * @since 1.0.0 207 | * @access public 208 | * @param string $name 209 | * @return object|bool 210 | */ 211 | function members_get_cap_group( $name ) { 212 | 213 | return members_cap_group_registry()->get( $name ); 214 | } 215 | 216 | /** 217 | * Returns the caps for a specific post type capability group. 218 | * 219 | * @since 1.0.0 220 | * @access public 221 | * @return array 222 | */ 223 | function members_get_post_type_group_caps( $post_type = 'post' ) { 224 | 225 | // Get the post type caps. 226 | $caps = (array) get_post_type_object( $post_type )->cap; 227 | 228 | // remove meta caps. 229 | unset( $caps['edit_post'] ); 230 | unset( $caps['read_post'] ); 231 | unset( $caps['delete_post'] ); 232 | 233 | // Get the cap names only. 234 | $caps = array_values( $caps ); 235 | 236 | // If this is not a core post/page post type. 237 | if ( ! in_array( $post_type, array( 'post', 'page' ) ) ) { 238 | 239 | // Get the post and page caps. 240 | $post_caps = array_values( (array) get_post_type_object( 'post' )->cap ); 241 | $page_caps = array_values( (array) get_post_type_object( 'page' )->cap ); 242 | 243 | // Remove post/page caps from the current post type caps. 244 | $caps = array_diff( $caps, $post_caps, $page_caps ); 245 | } 246 | 247 | // If attachment post type, add the `unfiltered_upload` cap. 248 | if ( 'attachment' === $post_type ) 249 | $caps[] = 'unfiltered_upload'; 250 | 251 | $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => "type-{$post_type}" ) ) ); 252 | 253 | if ( $registered_caps ) 254 | array_merge( $caps, $registered_caps ); 255 | 256 | // Make sure there are no duplicates and return. 257 | return array_unique( $caps ); 258 | } 259 | 260 | /** 261 | * Returns the caps for the taxonomy capability group. 262 | * 263 | * @since 1.0.0 264 | * @access public 265 | * @return array 266 | */ 267 | function members_get_taxonomy_group_caps() { 268 | 269 | $do_not_add = array( 270 | 'assign_categories', 271 | 'edit_categories', 272 | 'delete_categories', 273 | 'assign_post_tags', 274 | 'edit_post_tags', 275 | 'delete_post_tags', 276 | 'manage_post_tags' 277 | ); 278 | 279 | $taxi = get_taxonomies( array(), 'objects' ); 280 | 281 | $caps = array(); 282 | 283 | foreach ( $taxi as $tax ) 284 | $caps = array_merge( $caps, array_values( (array) $tax->cap ) ); 285 | 286 | $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'taxonomy' ) ) ); 287 | 288 | if ( $registered_caps ) 289 | array_merge( $caps, $registered_caps ); 290 | 291 | return array_diff( array_unique( $caps ), $do_not_add ); 292 | } 293 | 294 | /** 295 | * Returns the caps for the custom capability group. 296 | * 297 | * @since 1.0.0 298 | * @access public 299 | * @return array 300 | */ 301 | function members_get_custom_group_caps() { 302 | 303 | $caps = members_get_capabilities(); 304 | 305 | $registered_caps = array_keys( wp_list_filter( members_get_caps(), array( 'group' => 'custom' ) ) ); 306 | 307 | if ( $registered_caps ) 308 | array_merge( $caps, $registered_caps ); 309 | 310 | return array_unique( $caps ); 311 | } 312 | -------------------------------------------------------------------------------- /inc/functions-content-permissions.php: -------------------------------------------------------------------------------- 1 | role_names as $role => $name ) { 111 | 112 | // If the WP role is one of the current roles but not a new role, remove it. 113 | if ( ! in_array( $role, $roles ) && in_array( $role, $current_roles ) ) 114 | members_remove_post_role( $post_id, $role ); 115 | } 116 | } 117 | 118 | /** 119 | * Deletes all of a post's access roles. 120 | * 121 | * @since 1.0.0 122 | * @access public 123 | * @param int $post_id 124 | * @return bool 125 | */ 126 | function members_delete_post_roles( $post_id ) { 127 | 128 | return delete_post_meta( $post_id, '_members_access_role' ); 129 | } 130 | 131 | /** 132 | * Adds required filters for the content permissions feature if it is active. 133 | * 134 | * @since 0.2.0 135 | * @access public 136 | * @global object $wp_embed 137 | * @return void 138 | */ 139 | function members_enable_content_permissions() { 140 | global $wp_embed; 141 | 142 | // Only add filters if the content permissions feature is enabled and we're not in the admin. 143 | if ( members_content_permissions_enabled() && !is_admin() ) { 144 | 145 | // Filter the content and exerpts. 146 | add_filter( 'the_content', 'members_content_permissions_protect', 95 ); 147 | add_filter( 'get_the_excerpt', 'members_content_permissions_protect', 95 ); 148 | add_filter( 'the_excerpt', 'members_content_permissions_protect', 95 ); 149 | add_filter( 'the_content_feed', 'members_content_permissions_protect', 95 ); 150 | add_filter( 'get_comment_text', 'members_content_permissions_protect', 95 ); 151 | 152 | // Filter the comments template to make sure comments aren't shown to users without access. 153 | add_filter( 'comments_template', 'members_content_permissions_comments', 95 ); 154 | 155 | // Use WP formatting filters on the post error message. 156 | add_filter( 'members_post_error_message', array( $wp_embed, 'run_shortcode' ), 5 ); 157 | add_filter( 'members_post_error_message', array( $wp_embed, 'autoembed' ), 5 ); 158 | add_filter( 'members_post_error_message', 'wptexturize', 10 ); 159 | add_filter( 'members_post_error_message', 'convert_smilies', 15 ); 160 | add_filter( 'members_post_error_message', 'convert_chars', 20 ); 161 | add_filter( 'members_post_error_message', 'wpautop', 25 ); 162 | add_filter( 'members_post_error_message', 'do_shortcode', 30 ); 163 | add_filter( 'members_post_error_message', 'shortcode_unautop', 35 ); 164 | } 165 | } 166 | 167 | /** 168 | * Denies/Allows access to view post content depending on whether a user has permission to 169 | * view the content. 170 | * 171 | * @since 0.1.0 172 | * @access public 173 | * @param string $content 174 | * @return string 175 | */ 176 | function members_content_permissions_protect( $content ) { 177 | 178 | $post_id = get_the_ID(); 179 | 180 | return members_can_current_user_view_post( $post_id ) ? $content : members_get_post_error_message( $post_id ); 181 | } 182 | 183 | /** 184 | * Disables the comments template if a user doesn't have permission to view the post the 185 | * comments are associated with. 186 | * 187 | * @since 0.1.0 188 | * @param string $template 189 | * @return string 190 | */ 191 | function members_content_permissions_comments( $template ) { 192 | 193 | // Check if the current user has permission to view the comments' post. 194 | if ( ! members_can_current_user_view_post( get_the_ID() ) ) { 195 | 196 | // Look for a 'comments-no-access.php' template in the parent and child theme. 197 | $has_template = locate_template( array( 'comments-no-access.php' ) ); 198 | 199 | // If the template was found, use it. Otherwise, fall back to the Members comments.php template. 200 | $template = $has_template ? $has_template : members_plugin()->dir . 'templates/comments.php'; 201 | 202 | // Allow devs to overwrite the comments template. 203 | $template = apply_filters( 'members_comments_template', $template ); 204 | } 205 | 206 | // Return the comments template filename. 207 | return $template; 208 | } 209 | 210 | /** 211 | * Gets the error message to display for users who do not have access to view the given post. 212 | * The function first checks to see if a custom error message has been written for the 213 | * specific post. If not, it loads the error message set on the plugins settings page. 214 | * 215 | * @since 0.2.0 216 | * @access public 217 | * @param int $post_id 218 | * @return string 219 | */ 220 | function members_get_post_error_message( $post_id ) { 221 | 222 | // Get the error message for the specific post. 223 | $message = members_get_post_access_message( $post_id ); 224 | 225 | // Use default error message if we don't have one for the post. 226 | if ( ! $message ) 227 | $message = members_get_setting( 'content_permissions_error' ); 228 | 229 | // Return the error message. 230 | return apply_filters( 'members_post_error_message', sprintf( '
    %s
    ', $message ) ); 231 | } 232 | 233 | /** 234 | * Returns the post access message. 235 | * 236 | * @since 1.0.0 237 | * @access public 238 | * @param int $post_id 239 | * @return string 240 | */ 241 | function members_get_post_access_message( $post_id ) { 242 | 243 | return get_post_meta( $post_id, '_members_access_error', true ); 244 | } 245 | 246 | /** 247 | * Sets the post access message. 248 | * 249 | * @since 1.0.0 250 | * @access public 251 | * @param int $post_id 252 | * @param string $message 253 | * @return bool 254 | */ 255 | function members_set_post_access_message( $post_id, $message ) { 256 | 257 | return update_post_meta( $post_id, '_members_access_error', $message ); 258 | } 259 | 260 | /** 261 | * Deletes the post access message. 262 | * 263 | * @since 1.0.0 264 | * @access public 265 | * @param int $post_id 266 | * @return bool 267 | */ 268 | function members_delete_post_access_message( $post_id ) { 269 | 270 | return delete_post_meta( $post_id, '_members_access_error' ); 271 | } 272 | 273 | /** 274 | * Converts the meta values of the old '_role' post meta key to the newer '_members_access_role' meta 275 | * key. The reason for this change is to avoid any potential conflicts with other plugins/themes. We're 276 | * now using a meta key that is extremely specific to the Members plugin. 277 | * 278 | * @since 0.2.0 279 | * @access public 280 | * @param int $post_id 281 | * @return array|bool 282 | */ 283 | function members_convert_old_post_meta( $post_id ) { 284 | 285 | // Check if there are any meta values for the '_role' meta key. 286 | $old_roles = get_post_meta( $post_id, '_role', false ); 287 | 288 | // If roles were found, let's convert them. 289 | if ( !empty( $old_roles ) ) { 290 | 291 | // Delete the old '_role' post meta. 292 | delete_post_meta( $post_id, '_role' ); 293 | 294 | // Check if there are any roles for the '_members_access_role' meta key. 295 | $new_roles = get_post_meta( $post_id, '_members_access_role', false ); 296 | 297 | // If new roles were found, don't do any conversion. 298 | if ( empty( $new_roles ) ) { 299 | 300 | // Loop through the old meta values for '_role' and add them to the new '_members_access_role' meta key. 301 | foreach ( $old_roles as $role ) 302 | add_post_meta( $post_id, '_members_access_role', $role, false ); 303 | 304 | // Return the array of roles. 305 | return $old_roles; 306 | } 307 | } 308 | 309 | // Return false if we get to this point. 310 | return false; 311 | } 312 | -------------------------------------------------------------------------------- /inc/functions-options.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | /** 14 | * Conditional check to see if the role manager is enabled. 15 | * 16 | * @since 1.0.0 17 | * @access public 18 | * @return bool 19 | */ 20 | function members_role_manager_enabled() { 21 | 22 | return apply_filters( 'members_role_manager_enabled', members_get_setting( 'role_manager' ) ); 23 | } 24 | 25 | /** 26 | * Conditional check to see if denied capabilities should overrule granted capabilities when 27 | * a user has multiple roles with conflicting cap definitions. 28 | * 29 | * @since 1.0.0 30 | * @access public 31 | * @return bool 32 | */ 33 | function members_explicitly_deny_caps() { 34 | 35 | return apply_filters( 'members_explicitly_deny_caps', members_get_setting( 'explicit_denied_caps' ) ); 36 | } 37 | 38 | /** 39 | * Whether to show human-readable caps. 40 | * 41 | * @since 2.0.0 42 | * @access public 43 | * @return bool 44 | */ 45 | function members_show_human_caps() { 46 | 47 | return apply_filters( 'members_show_human_caps', members_get_setting( 'show_human_caps' ) ); 48 | } 49 | 50 | /** 51 | * Conditional check to see if the role manager is enabled. 52 | * 53 | * @since 1.0.0 54 | * @access public 55 | * @return bool 56 | */ 57 | function members_multiple_user_roles_enabled() { 58 | 59 | return apply_filters( 'members_multiple_roles_enabled', members_get_setting( 'multi_roles' ) ); 60 | } 61 | 62 | /** 63 | * Conditional check to see if content permissions are enabled. 64 | * 65 | * @since 1.0.0 66 | * @access public 67 | * @return bool 68 | */ 69 | function members_content_permissions_enabled() { 70 | 71 | return apply_filters( 'members_content_permissions_enabled', members_get_setting( 'content_permissions' ) ); 72 | } 73 | 74 | /** 75 | * Conditional check to see if login widget is enabled. 76 | * 77 | * @since 1.0.0 78 | * @access public 79 | * @return bool 80 | */ 81 | function members_login_widget_enabled() { 82 | 83 | return apply_filters( 'members_login_widget_enabled', members_get_setting( 'login_form_widget' ) ); 84 | } 85 | 86 | /** 87 | * Conditional check to see if users widget is enabled. 88 | * 89 | * @since 1.0.0 90 | * @access public 91 | * @return bool 92 | */ 93 | function members_users_widget_enabled() { 94 | 95 | return apply_filters( 'members_users_widget_enabled', members_get_setting( 'users_widget' ) ); 96 | } 97 | 98 | /** 99 | * Gets a setting from from the plugin settings in the database. 100 | * 101 | * @since 0.2.0 102 | * @access public 103 | * @return mixed 104 | */ 105 | function members_get_setting( $option = '' ) { 106 | 107 | $defaults = members_get_default_settings(); 108 | 109 | $settings = wp_parse_args( get_option( 'members_settings', $defaults ), $defaults ); 110 | 111 | return isset( $settings[ $option ] ) ? $settings[ $option ] : false; 112 | } 113 | 114 | /** 115 | * Returns an array of the default plugin settings. 116 | * 117 | * @since 0.2.0 118 | * @access public 119 | * @return array 120 | */ 121 | function members_get_default_settings() { 122 | 123 | return array( 124 | 125 | // @since 0.1.0 126 | 'role_manager' => 1, 127 | 'content_permissions' => 1, 128 | 'private_blog' => 0, 129 | 130 | // @since 0.2.0 131 | 'private_feed' => 0, 132 | 'login_form_widget' => 0, 133 | 'users_widget' => 0, 134 | 'content_permissions_error' => esc_html__( 'Sorry, but you do not have permission to view this content.', 'members' ), 135 | 'private_feed_error' => esc_html__( 'You must be logged into the site to view this content.', 'members' ), 136 | 137 | // @since 1.0.0 138 | 'explicit_denied_caps' => true, 139 | 'multi_roles' => true, 140 | 141 | // @since 2.0.0 142 | 'show_human_caps' => true, 143 | 'private_rest_api' => false, 144 | ); 145 | } 146 | -------------------------------------------------------------------------------- /inc/functions-private-site.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 11 | * @link https://themehybrid.com/plugins/members 12 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 13 | */ 14 | 15 | # Redirects users to the login page. 16 | add_action( 'template_redirect', 'members_please_log_in', 0 ); 17 | 18 | # Disable content in feeds if the feed should be private. 19 | add_filter( 'the_content_feed', 'members_private_feed', 95 ); 20 | add_filter( 'the_excerpt_rss', 'members_private_feed', 95 ); 21 | add_filter( 'comment_text_rss', 'members_private_feed', 95 ); 22 | 23 | # Filters for the feed error message. 24 | add_filter( 'members_feed_error_message', array( $GLOBALS['wp_embed'], 'run_shortcode' ), 5 ); 25 | add_filter( 'members_feed_error_message', array( $GLOBALS['wp_embed'], 'autoembed' ), 5 ); 26 | add_filter( 'members_feed_error_message', 'wptexturize', 10 ); 27 | add_filter( 'members_feed_error_message', 'convert_smilies', 15 ); 28 | add_filter( 'members_feed_error_message', 'convert_chars', 20 ); 29 | add_filter( 'members_feed_error_message', 'wpautop', 25 ); 30 | add_filter( 'members_feed_error_message', 'do_shortcode', 30 ); 31 | add_filter( 'members_feed_error_message', 'shortcode_unautop', 35 ); 32 | 33 | # Authenticate when accessing the REST API. 34 | add_filter( 'rest_authentication_errors', 'members_private_rest_api', 95 ); 35 | 36 | /** 37 | * Conditional tag to see if we have a private blog. 38 | * 39 | * @since 1.0.0 40 | * @access public 41 | * @return bool 42 | */ 43 | function members_is_private_blog() { 44 | 45 | return apply_filters( 'members_is_private_blog', members_get_setting( 'private_blog' ) ); 46 | } 47 | 48 | /** 49 | * Conditional tag to see if we have a private feed. 50 | * 51 | * @since 1.0.0 52 | * @access public 53 | * @return bool 54 | */ 55 | function members_is_private_feed() { 56 | 57 | return apply_filters( 'members_is_private_feed', members_get_setting( 'private_feed' ) ); 58 | } 59 | 60 | /** 61 | * Conditional tag to see if we have a private REST API 62 | * 63 | * @since 2.0.0 64 | * @access public 65 | * @return bool 66 | */ 67 | function members_is_private_rest_api() { 68 | 69 | return apply_filters( 'members_is_private_rest_api', members_get_setting( 'private_rest_api' ) ); 70 | } 71 | 72 | /** 73 | * Redirects users that are not logged in to the 'wp-login.php' page. 74 | * 75 | * @since 0.1.0 76 | * @access public 77 | * @return void 78 | */ 79 | function members_please_log_in() { 80 | 81 | // If private blog is not enabled, bail. 82 | if ( ! members_is_private_blog() ) 83 | return; 84 | 85 | // If this is a multisite instance and the user is logged into the network. 86 | if ( is_multisite() && is_user_logged_in() && ! is_user_member_of_blog() && ! is_super_admin() ) { 87 | members_ms_private_blog_die(); 88 | } 89 | 90 | // Check if the private blog feature is active and if the user is not logged in. 91 | if ( ! is_user_logged_in() && members_is_private_page() ) { 92 | 93 | auth_redirect(); 94 | exit; 95 | } 96 | } 97 | 98 | /** 99 | * Function for determining whether a page should be public even though we're in private 100 | * site mode. Plugin devs can filter this to make specific pages public. 101 | * 102 | * @since 2.0.0 103 | * @access public 104 | * @return bool 105 | */ 106 | function members_is_private_page() { 107 | 108 | $is_private = true; 109 | 110 | if ( function_exists( 'bp_is_current_component' ) && ( bp_is_current_component( 'register' ) || bp_is_current_component( 'activate' ) ) ) 111 | $is_private = false; 112 | 113 | // WooCommerce support. 114 | if ( class_exists( 'WooCommerce' ) ) { 115 | $page_id = get_option( 'woocommerce_myaccount_page_id' ); 116 | 117 | if ( $page_id && is_page( $page_id ) ) 118 | $is_private = false; 119 | } 120 | 121 | return apply_filters( 'members_is_private_page', $is_private ); 122 | } 123 | 124 | /** 125 | * Blocks feed items if the user has selected the private feed feature. 126 | * 127 | * @since 0.2.0 128 | * @access public 129 | * @param string $content 130 | * @return string 131 | */ 132 | function members_private_feed( $content ) { 133 | 134 | return members_is_private_feed() ? members_get_private_feed_message() : $content; 135 | } 136 | 137 | /** 138 | * Returns the private feed error message. 139 | * 140 | * @since 1.0.0 141 | * @access public 142 | * @return string 143 | */ 144 | function members_get_private_feed_message() { 145 | 146 | return apply_filters( 'members_feed_error_message', members_get_setting( 'private_feed_error' ) ); 147 | } 148 | 149 | /** 150 | * Returns an error if the REST API is accessed by an unauthenticated user. 151 | * 152 | * @link https://developer.wordpress.org/rest-api/using-the-rest-api/frequently-asked-questions/#require-authentication-for-all-requests 153 | * @since 2.0.0 154 | * @access public 155 | * @param object $result 156 | * @return object 157 | */ 158 | function members_private_rest_api( $result ) { 159 | 160 | if ( empty( $result ) && members_is_private_rest_api() && ! is_user_logged_in() ) { 161 | 162 | return new WP_Error( 163 | 'rest_not_logged_in', 164 | esc_html( 165 | apply_filters( 166 | 'members_rest_api_error_message', 167 | __( 'You are not currently logged in.', 'members' ) 168 | ) 169 | ), 170 | array( 'status' => 401 ) 171 | ); 172 | } 173 | 174 | return $result; 175 | } 176 | 177 | /** 178 | * Outputs an error message if a user attempts to access a site that they do not have 179 | * access to on multisite. 180 | * 181 | * @since 2.0.0 182 | * @access public 183 | * @return void 184 | */ 185 | function members_ms_private_blog_die() { 186 | 187 | $blogs = get_blogs_of_user( get_current_user_id() ); 188 | 189 | $blogname = get_bloginfo( 'name' ); 190 | 191 | $message = __( 'You do not currently have access to the "%s" site. If you believe you should have access, please contact your network administrator.', 'members' ); 192 | 193 | if ( empty( $blogs ) ) 194 | wp_die( sprintf( $message, $blogname ), 403 ); 195 | 196 | $output = '

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

    '; 197 | 198 | $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' ) ); 199 | 200 | $output .= ''; 206 | 207 | wp_die( $output, 403 ); 208 | } 209 | -------------------------------------------------------------------------------- /inc/functions-role-groups.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Registers default groups. 14 | add_action( 'init', 'members_register_role_groups', 95 ); 15 | add_action( 'members_register_role_groups', 'members_register_default_role_groups', 5 ); 16 | 17 | /** 18 | * Fires the role group registration action hook. 19 | * 20 | * @since 1.0.0 21 | * @access public 22 | * @return void 23 | */ 24 | function members_register_role_groups() { 25 | 26 | do_action( 'members_register_role_groups' ); 27 | } 28 | 29 | 30 | /** 31 | * Registers the default role groups. 32 | * 33 | * @since 2.0.0 34 | * @access public 35 | * @return void 36 | */ 37 | function members_register_default_role_groups() { 38 | 39 | // Register the WordPress group. 40 | members_register_role_group( 'wordpress', 41 | array( 42 | 'label' => esc_html__( 'WordPress', 'members' ), 43 | 'label_count' => _n_noop( 'WordPress %s', 'WordPress %s', 'members' ), 44 | 'roles' => members_get_wordpress_roles(), 45 | ) 46 | ); 47 | } 48 | 49 | /** 50 | * Returns the instance of the role group registry. 51 | * 52 | * @since 2.0.0 53 | * @access public 54 | * @return object 55 | */ 56 | function members_role_group_registry() { 57 | 58 | return \Members\Registry::get_instance( 'role_group' ); 59 | } 60 | 61 | /** 62 | * Function for registering a role group. 63 | * 64 | * @since 1.0.0 65 | * @access public 66 | * @param string $name 67 | * @param array $args 68 | * @return void 69 | */ 70 | function members_register_role_group( $name, $args = array() ) { 71 | 72 | members_role_group_registry()->register( $name, new \Members\Role_Group( $name, $args ) ); 73 | } 74 | 75 | /** 76 | * Unregisters a group. 77 | * 78 | * @since 1.0.0 79 | * @access public 80 | * @param string $name 81 | * @return void 82 | */ 83 | function members_unregister_role_group( $name ) { 84 | 85 | members_role_group_registry()->unregister( $name ); 86 | } 87 | 88 | /** 89 | * Checks if a group exists. 90 | * 91 | * @since 1.0.0 92 | * @access public 93 | * @param string $name 94 | * @return bool 95 | */ 96 | function members_role_group_exists( $name ) { 97 | 98 | return members_role_group_registry()->exists( $name ); 99 | } 100 | 101 | /** 102 | * Returns an array of registered group objects. 103 | * 104 | * @since 1.0.0 105 | * @access public 106 | * @return array 107 | */ 108 | function members_get_role_groups() { 109 | 110 | return members_role_group_registry()->get_collection(); 111 | } 112 | 113 | /** 114 | * Returns a group object if it exists. Otherwise, `FALSE`. 115 | * 116 | * @since 1.0.0 117 | * @access public 118 | * @param string $name 119 | * @return object|bool 120 | */ 121 | function members_get_role_group( $name ) { 122 | 123 | return members_role_group_registry()->get( $name ); 124 | } 125 | -------------------------------------------------------------------------------- /inc/functions-shortcodes.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Add shortcodes. 14 | add_action( 'init', 'members_register_shortcodes' ); 15 | 16 | /** 17 | * Registers shortcodes. 18 | * 19 | * @since 0.2.0 20 | * @access public 21 | * @return void 22 | */ 23 | function members_register_shortcodes() { 24 | 25 | // Add the `[members_login_form]` shortcode. 26 | add_shortcode( 'members_login_form', 'members_login_form_shortcode' ); 27 | add_shortcode( 'login-form', 'members_login_form_shortcode' ); // @deprecated 1.0.0 28 | 29 | // Add the `[members_access]` shortcode. 30 | add_shortcode( 'members_access', 'members_access_check_shortcode' ); 31 | add_shortcode( 'access', 'members_access_check_shortcode' ); // @deprecated 1.0.0 32 | 33 | // Add the `[members_feed]` shortcode. 34 | add_shortcode( 'members_feed', 'members_feed_shortcode' ); 35 | add_shortcode( 'feed', 'members_feed_shortcode' ); // @deprecated 1.0.0 36 | 37 | // Add the `[members_logged_in]` shortcode. 38 | add_shortcode( 'members_logged_in', 'members_is_user_logged_in_shortcode' ); 39 | add_shortcode( 'is_user_logged_in', 'members_is_user_logged_in_shortcode' ); // @deprecated 1.0.0 40 | 41 | // Add the `[members_not_logged_in]` shortcode. 42 | add_shortcode( 'members_not_logged_in', 'members_not_logged_in_shortcode' ); 43 | 44 | // @deprecated 0.2.0. 45 | add_shortcode( 'get_avatar', 'members_get_avatar_shortcode' ); 46 | add_shortcode( 'avatar', 'members_get_avatar_shortcode' ); 47 | } 48 | 49 | /** 50 | * Displays content if the user viewing it is currently logged in. This also blocks content 51 | * from showing in feeds. 52 | * 53 | * @since 0.1.0 54 | * @access public 55 | * @param array $attr 56 | * @param string $content 57 | * @return string 58 | */ 59 | function members_is_user_logged_in_shortcode( $attr, $content = null ) { 60 | 61 | return is_feed() || ! is_user_logged_in() || is_null( $content ) ? '' : do_shortcode( $content ); 62 | } 63 | 64 | /** 65 | * Displays content if the user viewing it is not currently logged in. 66 | * 67 | * @since 2.0.0 68 | * @access public 69 | * @param array $attr 70 | * @param string $content 71 | * @return string 72 | */ 73 | function members_not_logged_in_shortcode( $attr, $content = null ) { 74 | 75 | return is_user_logged_in() || is_null( $content ) ? '' : do_shortcode( $content ); 76 | } 77 | 78 | /** 79 | * Content that should only be shown in feed readers. Can be useful for displaying 80 | * feed-specific items. 81 | * 82 | * @since 0.1.0 83 | * @access public 84 | * @param array $attr 85 | * @param string $content 86 | * @return string 87 | */ 88 | function members_feed_shortcode( $attr, $content = null ) { 89 | 90 | return ! is_feed() || is_null( $content ) ? '' : do_shortcode( $content ); 91 | } 92 | 93 | /** 94 | * Provide/restrict access to specific roles or capabilities. This content should not be shown 95 | * in feeds. Note that capabilities are checked first. If a capability matches, any roles 96 | * added will *not* be checked. Users should choose between using either capabilities or roles 97 | * for the check rather than both. The best option is to always use a capability. 98 | * 99 | * @since 0.1.0 100 | * @access public 101 | * @param array $attr 102 | * @param string $content 103 | * @return string 104 | */ 105 | function members_access_check_shortcode( $attr, $content = null ) { 106 | 107 | // If there's no content or if viewing a feed, return an empty string. 108 | if ( is_null( $content ) || is_feed() ) 109 | return ''; 110 | 111 | $user_can = false; 112 | 113 | // Set up the default attributes. 114 | $defaults = array( 115 | 'capability' => '', // Single capability or comma-separated multiple capabilities. 116 | 'role' => '', // Single role or comma-separated multiple roles. 117 | 'user_id' => '', // Single user ID or comma-separated multiple IDs. 118 | 'user_name' => '', // Single user name or comma-separated multiple names. 119 | 'user_email' => '', // Single user email or comma-separated multiple emails. 120 | 'operator' => 'or' // Only the `!` operator is supported for now. Everything else falls back to `or`. 121 | ); 122 | 123 | // Merge the input attributes and the defaults. 124 | $attr = shortcode_atts( $defaults, $attr, 'members_access' ); 125 | 126 | // Get the operator. 127 | $operator = strtolower( $attr['operator'] ); 128 | 129 | // If the current user has the capability, show the content. 130 | if ( $attr['capability'] ) { 131 | 132 | // Get the capabilities. 133 | $caps = explode( ',', $attr['capability'] ); 134 | 135 | if ( '!' === $operator ) 136 | return members_current_user_can_any( $caps ) ? '' : do_shortcode( $content ); 137 | 138 | return members_current_user_can_any( $caps ) ? do_shortcode( $content ) : ''; 139 | } 140 | 141 | // If the current user has the role, show the content. 142 | if ( $attr['role'] ) { 143 | 144 | // Get the roles. 145 | $roles = explode( ',', $attr['role'] ); 146 | 147 | if ( '!' === $operator ) 148 | return members_current_user_has_role( $roles ) ? '' : do_shortcode( $content ); 149 | 150 | return members_current_user_has_role( $roles ) ? do_shortcode( $content ) : ''; 151 | } 152 | 153 | $user_id = 0; 154 | $user_name = $user_email = ''; 155 | 156 | if ( is_user_logged_in() ) { 157 | 158 | $user = wp_get_current_user(); 159 | $user_id = get_current_user_id(); 160 | $user_name = $user->user_login; 161 | $user_email = $user->user_email; 162 | } 163 | 164 | // If the current user has one of the user ids. 165 | if ( $attr['user_id'] ) { 166 | 167 | // Get the user IDs. 168 | $ids = array_map( 'trim', explode( ',', $attr['user_id'] ) ); 169 | 170 | if ( '!' === $operator ) { 171 | return in_array( $user_id, $ids ) ? '' : do_shortcode( $content ); 172 | } 173 | 174 | return in_array( $user_id, $ids ) ? do_shortcode( $content ) : ''; 175 | } 176 | 177 | // If the current user has one of the user names. 178 | if ( $attr['user_name'] ) { 179 | 180 | // Get the user names. 181 | $names = array_map( 'trim', explode( ',', $attr['user_name'] ) ); 182 | 183 | if ( '!' === $operator ) { 184 | return in_array( $user_name, $names ) ? '' : do_shortcode( $content ); 185 | } 186 | 187 | return in_array( $user_name, $names ) ? do_shortcode( $content ) : ''; 188 | } 189 | 190 | // If the current user has one of the user emails. 191 | if ( $attr['user_email'] ) { 192 | 193 | // Get the user emails. 194 | $emails = array_map( 'trim', explode( ',', $attr['user_email'] ) ); 195 | 196 | if ( '!' === $operator ) { 197 | return in_array( $user_email, $emails ) ? '' : do_shortcode( $content ); 198 | } 199 | 200 | return in_array( $user_email, $emails ) ? do_shortcode( $content ) : ''; 201 | } 202 | 203 | // Return an empty string if we've made it to this point. 204 | return ''; 205 | } 206 | 207 | /** 208 | * Displays a login form. 209 | * 210 | * @since 0.1.0 211 | * @access public 212 | * @return string 213 | */ 214 | function members_login_form_shortcode() { 215 | 216 | return wp_login_form( array( 'echo' => false ) ); 217 | } 218 | -------------------------------------------------------------------------------- /inc/functions-users.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | // Filter `user_has_cap` if denied caps should take precedence. 14 | if ( members_explicitly_deny_caps() ) { 15 | add_filter( 'user_has_cap', 'members_user_has_cap_filter', 10, 4 ); 16 | } 17 | 18 | /** 19 | * Filter on `user_has_cap` to explicitly deny caps if there are conflicting caps when a 20 | * user has multiple roles. WordPress doesn't consistently handle two or more roles that 21 | * have the same capability but a conflict between being granted or denied. Core WP 22 | * merges the role caps so that the last role the user has will take precedence. This 23 | * has the potential for granting permission for things that a user shouldn't have 24 | * permission to do. 25 | * 26 | * @since 1.0.0 27 | * @access public 28 | * @param array $allcaps 29 | * @param array $caps 30 | * @param array $args 31 | * @param object $user 32 | * @return array 33 | */ 34 | function members_user_has_cap_filter( $allcaps, $caps, $args, $user ) { 35 | 36 | // If the user doesn't have more than one role, bail. 37 | if ( 1 >= count( (array) $user->roles ) ) 38 | return $allcaps; 39 | 40 | // Get the denied caps. 41 | $denied_caps = array_keys( (array) $allcaps, false ); 42 | 43 | // Loop through the user's roles and find any denied caps. 44 | foreach ( (array) $user->roles as $role ) { 45 | 46 | // Get the role object. 47 | $role_obj = get_role( $role ); 48 | 49 | // If we have an object, merge it's denied caps. 50 | if ( ! is_null( $role_obj ) ) { 51 | $denied_caps = array_merge( 52 | (array) $denied_caps, 53 | array_keys( (array) $role_obj->capabilities, false ) 54 | ); 55 | } 56 | } 57 | 58 | // If there are any denied caps, make sure they take precedence. 59 | if ( $denied_caps ) { 60 | 61 | foreach ( $denied_caps as $denied_cap ) { 62 | $allcaps[ $denied_cap ] = false; 63 | } 64 | } 65 | 66 | // Return all the user caps. 67 | return $allcaps; 68 | } 69 | 70 | /** 71 | * Conditional tag to check whether a user has a specific role. 72 | * 73 | * @since 1.0.0 74 | * @access public 75 | * @param int $user_id 76 | * @param string|array $roles 77 | * @return bool 78 | */ 79 | function members_user_has_role( $user_id, $roles ) { 80 | 81 | $user = new WP_User( $user_id ); 82 | 83 | foreach ( (array) $roles as $role ) { 84 | 85 | if ( in_array( $role, (array) $user->roles ) ) 86 | return true; 87 | } 88 | 89 | return false; 90 | } 91 | 92 | /** 93 | * Conditional tag to check whether the currently logged-in user has a specific role. 94 | * 95 | * @since 1.0.0 96 | * @access public 97 | * @param string|array $roles 98 | * @return bool 99 | */ 100 | function members_current_user_has_role( $roles ) { 101 | 102 | return is_user_logged_in() ? members_user_has_role( get_current_user_id(), $roles ) : false; 103 | } 104 | 105 | /** 106 | * Wrapper for `current_user_can()` that checks if the user can perform any action. 107 | * Accepts an array of caps instead of a single cap. 108 | * 109 | * @since 2.0.0 110 | * @access public 111 | * @param array $caps 112 | * @return bool 113 | */ 114 | function members_current_user_can_any( $caps = array() ) { 115 | 116 | foreach ( $caps as $cap ) { 117 | 118 | if ( current_user_can( $cap ) ) 119 | return true; 120 | } 121 | 122 | return false; 123 | } 124 | 125 | /** 126 | * Wrapper for `current_user_can()` that checks if the user can perform all actions. 127 | * Accepts an array of caps instead of a single cap. 128 | * 129 | * @since 2.0.0 130 | * @access public 131 | * @param array $caps 132 | * @return bool 133 | */ 134 | function members_current_user_can_all( $caps = array() ) { 135 | 136 | foreach ( $caps as $cap ) { 137 | 138 | if ( ! current_user_can( $cap ) ) 139 | return false; 140 | } 141 | 142 | return true; 143 | } 144 | 145 | /** 146 | * Returns an array of the role names a user has. 147 | * 148 | * @since 1.0.0 149 | * @access public 150 | * @param int $user_id 151 | * @return array 152 | */ 153 | function members_get_user_role_names( $user_id ) { 154 | 155 | $user = new WP_User( $user_id ); 156 | 157 | $names = array(); 158 | 159 | foreach ( $user->roles as $role ) 160 | $names[ $role ] = members_get_role( $role )->get( 'label' ); 161 | 162 | return $names; 163 | } 164 | -------------------------------------------------------------------------------- /inc/functions-widgets.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | # Hook widget registration to the 'widgets_init' hook. 14 | add_action( 'widgets_init', 'members_register_widgets' ); 15 | 16 | /** 17 | * Registers widgets for the plugin. 18 | * 19 | * @since 0.2.0 20 | * @access public 21 | * @return void 22 | */ 23 | function members_register_widgets() { 24 | 25 | // If the login form widget is enabled. 26 | if ( members_login_widget_enabled() ) { 27 | 28 | require_once( members_plugin()->dir . 'inc/class-widget-login.php' ); 29 | 30 | register_widget( '\Members\Widget_Login' ); 31 | } 32 | 33 | // If the users widget is enabled. 34 | if ( members_users_widget_enabled() ) { 35 | 36 | require_once( members_plugin()->dir . 'inc/class-widget-users.php' ); 37 | 38 | register_widget( '\Members\Widget_Users' ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /inc/functions.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | /** 14 | * Validates a value as a boolean. This way, strings such as "true" or "false" will be converted 15 | * to their correct boolean values. 16 | * 17 | * @since 1.0.0 18 | * @access public 19 | * @param mixed $val 20 | * @return bool 21 | */ 22 | function members_validate_boolean( $val ) { 23 | 24 | return filter_var( $val, FILTER_VALIDATE_BOOLEAN ); 25 | } 26 | 27 | 28 | /** 29 | * Helper function for sorting objects by priority. 30 | * 31 | * @since 2.0.0 32 | * @access protected 33 | * @param object $a 34 | * @param object $b 35 | * @return int 36 | */ 37 | function members_priority_sort( $a, $b ) { 38 | 39 | return $a->priority - $b->priority; 40 | } 41 | -------------------------------------------------------------------------------- /inc/template.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Copyright (c) 2009 - 2018, Justin Tadlock 9 | * @link https://themehybrid.com/plugins/members 10 | * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 11 | */ 12 | 13 | /** 14 | * Conditional tag to check if a user can view a specific post. A user cannot view a post if their 15 | * user role has not been selected in the 'Content Permissions' meta box on the edit post screen in 16 | * the admin. Non-logged in site visitors cannot view posts if roles were selected. If no roles 17 | * were selected, all users and site visitors can view the content. 18 | * 19 | * There are exceptions to this rule though. The post author, any user with the `restrict_content` 20 | * capability, and users that have the ability to edit the post can always view the post, even if 21 | * their role was not granted permission to view it. 22 | * 23 | * @since 0.2.0 24 | * @access public 25 | * @param int $user_id 26 | * @param int $post_id 27 | * @return bool 28 | */ 29 | function members_can_user_view_post( $user_id, $post_id = '' ) { 30 | 31 | // If no post ID is given, assume we're in The Loop and get the current post's ID. 32 | if ( ! $post_id ) { 33 | $post_id = get_the_ID(); 34 | } 35 | 36 | // Get post object. 37 | $post = get_post( $post_id ); 38 | 39 | // Assume the user can view the post at this point. */ 40 | $can_view = true; 41 | 42 | // The plugin is only going to handle permissions if the 'content permissions' feature 43 | // is active. If not active, the user can always view the post. However, developers 44 | // can roll their own handling of this and filter `members_can_user_view_post`. 45 | if ( $post instanceof \WP_Post && members_content_permissions_enabled() ) { 46 | 47 | // Get the roles selected by the user. 48 | $roles = members_get_post_roles( $post_id ); 49 | 50 | // Check if there are any old roles with the '_role' meta key. 51 | if ( empty( $roles ) ) 52 | $roles = members_convert_old_post_meta( $post_id ); 53 | 54 | // If we have an array of roles, let's get to work. 55 | if ( ! empty( $roles ) && is_array( $roles ) ) { 56 | 57 | // Since specific roles were given, let's assume the user can't view 58 | // the post at this point. The rest of this functionality should try 59 | // to disprove this. 60 | $can_view = false; 61 | 62 | // Get the post type object. 63 | $post_type = get_post_type_object( $post->post_type ); 64 | 65 | // If viewing a feed or if the user's not logged in, assume it's blocked at this point. 66 | if ( is_feed() || ! is_user_logged_in() ) { 67 | $can_view = false; 68 | } 69 | 70 | // If the post author, the current user can edit the post, or the current user can 'restrict_content', return true. 71 | elseif ( $post->post_author == $user_id || user_can( $user_id, 'restrict_content' ) || user_can( $user_id, $post_type->cap->edit_post, $post_id ) ) { 72 | $can_view = true; 73 | } 74 | 75 | // Else, let's check the user's role against the selected roles. 76 | else { 77 | 78 | // Loop through each role and set $can_view to true if the user has one of the roles. 79 | foreach ( $roles as $role ) { 80 | 81 | if ( members_user_has_role( $user_id, $role ) ) { 82 | $can_view = true; 83 | break; 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | // Set the check for the parent post based on whether we have permissions for this post. 91 | $check_parent = empty( $roles ) && $can_view; 92 | 93 | // Set to `FALSE` to avoid hierarchical checking. 94 | if ( apply_filters( 'members_check_parent_post_permission', $check_parent, $post_id, $user_id ) ) { 95 | 96 | if ( $post instanceof \WP_Post ) { 97 | 98 | $parent_id = $post->post_parent; 99 | 100 | // If the post has a parent, check if the user has permission to view it. 101 | if ( 0 < $parent_id ) { 102 | $can_view = members_can_user_view_post( $user_id, $parent_id ); 103 | } 104 | } 105 | } 106 | 107 | // Allow developers to overwrite the final return value. 108 | return apply_filters( 'members_can_user_view_post', $can_view, $user_id, $post_id ); 109 | } 110 | 111 | /** 112 | * Wrapper function for the members_can_user_view_post() function. This function checks if the 113 | * currently logged-in user can view the content of a specific post. 114 | * 115 | * @since 0.2.0 116 | * @access public 117 | * @param int $post_id 118 | * @return bool 119 | */ 120 | function members_can_current_user_view_post( $post_id = '' ) { 121 | 122 | return members_can_user_view_post( get_current_user_id(), $post_id ); 123 | } 124 | 125 | /** 126 | * Function for listing users like the WordPress function currently uses for authors. 127 | * 128 | * @link http://core.trac.wordpress.org/ticket/15145 129 | * @since 0.1.0 130 | * @access public 131 | * @param array $args 132 | * @return string 133 | */ 134 | function members_list_users( $args = array() ) { 135 | 136 | $output = ''; 137 | $users = get_users( $args ); 138 | 139 | if ( ! empty( $users ) ) { 140 | 141 | foreach ( $users as $user ) { 142 | 143 | $url = get_author_posts_url( $user->ID, $user->user_nicename ); 144 | 145 | $class = sanitize_html_class( "user-{$user->ID}" ); 146 | 147 | if ( is_author( $user->ID ) ) 148 | $class .= ' current-user'; 149 | 150 | $output .= sprintf( '
  • %s
  • ', esc_attr( $class ), esc_url( $url ), esc_html( $user->display_name ) ); 151 | } 152 | 153 | $output = sprintf( '', $output ); 154 | } 155 | 156 | $output = apply_filters( 'members_list_users', $output ); 157 | 158 | if ( empty( $args['echo'] ) ) 159 | return $output; 160 | 161 | echo $output; 162 | } 163 | -------------------------------------------------------------------------------- /js/edit-post.js: -------------------------------------------------------------------------------- 1 | ( function() { 2 | 3 | /* ====== Tabs ====== */ 4 | 5 | // Hides the tab content. 6 | jQuery( '.members-tabs .members-tab-content' ).hide(); 7 | 8 | // Shows the first tab's content. 9 | jQuery( '.members-tabs .members-tab-content:first-child' ).show(); 10 | 11 | // Makes the 'aria-selected' attribute true for the first tab nav item. 12 | jQuery( '.members-tab-nav :first-child' ).attr( 'aria-selected', 'true' ); 13 | 14 | // When a tab nav item is clicked. 15 | jQuery( '.members-tab-nav li a' ).click( 16 | function( j ) { 17 | 18 | // Prevent the default browser action when a link is clicked. 19 | j.preventDefault(); 20 | 21 | // Get the `href` attribute of the item. 22 | var href = jQuery( this ).attr( 'href' ); 23 | 24 | // Hide all tab content. 25 | jQuery( this ).parents( '.members-tabs' ).find( '.members-tab-content' ).hide(); 26 | 27 | // Find the tab content that matches the tab nav item and show it. 28 | jQuery( this ).parents( '.members-tabs' ).find( href ).show(); 29 | 30 | // Set the `aria-selected` attribute to false for all tab nav items. 31 | jQuery( this ).parents( '.members-tabs' ).find( '.members-tab-title' ).attr( 'aria-selected', 'false' ); 32 | 33 | // Set the `aria-selected` attribute to true for this tab nav item. 34 | jQuery( this ).parent().attr( 'aria-selected', 'true' ); 35 | } 36 | ); // click() 37 | 38 | }() ); 39 | -------------------------------------------------------------------------------- /js/edit-post.min.js: -------------------------------------------------------------------------------- 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")})}(); -------------------------------------------------------------------------------- /js/edit-role.min.js: -------------------------------------------------------------------------------- 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();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")}})}); -------------------------------------------------------------------------------- /js/settings.js: -------------------------------------------------------------------------------- 1 | jQuery( document ).ready( function() { 2 | 3 | /* ====== Plugin Settings ====== */ 4 | 5 | // Hide content permissions message if disabled. 6 | if ( false === jQuery( '[name="members_settings[content_permissions]"]' ).prop( 'checked' ) ) { 7 | 8 | jQuery( '[name="members_settings[content_permissions]"]' ).parents( 'tr' ).next( 'tr' ).hide(); 9 | } 10 | 11 | // Hide private feed message if private feed disabled. 12 | if ( false === jQuery( '[name="members_settings[private_feed]"]' ).prop( 'checked' ) ) { 13 | 14 | jQuery( '[name="members_settings[private_feed]"]' ).parents( 'tr' ).next( 'tr' ).hide(); 15 | } 16 | 17 | // Show above hidden items if feature becomes enabled. 18 | jQuery( '[name="members_settings[content_permissions]"], [name="members_settings[private_feed]"]' ).on( 'change', 19 | function() { 20 | 21 | if ( jQuery( this ).prop( 'checked' ) ) { 22 | 23 | jQuery( this ).parents( 'tr' ).next( 'tr' ).show( 'slow' ); 24 | } else { 25 | 26 | jQuery( this ).parents( 'tr' ).next( 'tr' ).hide( 'slow' ); 27 | } 28 | } 29 | ); 30 | } ); 31 | -------------------------------------------------------------------------------- /js/settings.min.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function(){!1===jQuery('[name="members_settings[content_permissions]"]').prop("checked")&&jQuery('[name="members_settings[content_permissions]"]').parents("tr").next("tr").hide(),!1===jQuery('[name="members_settings[private_feed]"]').prop("checked")&&jQuery('[name="members_settings[private_feed]"]').parents("tr").next("tr").hide(),jQuery('[name="members_settings[content_permissions]"], [name="members_settings[private_feed]"]').on("change",function(){jQuery(this).prop("checked")?jQuery(this).parents("tr").next("tr").show("slow"):jQuery(this).parents("tr").next("tr").hide("slow")})}); -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Members === 2 | 3 | Contributors: greenshady 4 | Donate link: https://themehybrid.com/plugins/members#donate 5 | Tags: capabilities, roles, members, users 6 | Requires at least: 4.7 7 | Tested up to: 5.2 8 | Requires PHP: 5.6 9 | Stable tag: 2.2.0 10 | License: GPLv2 or later 11 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 12 | 13 | The most powerful user, role, and capability management plugin for WordPress. 14 | 15 | == Description == 16 | 17 | 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. 18 | 19 | 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. 20 | 21 | ### Plugin Features 22 | 23 | * **Role Editor:** Allows you to edit, create, and delete roles as well as capabilities for these roles. 24 | * **Multiple User Roles:** Give one, two, or even more roles to any user. 25 | * **Explicitly Deny Capabilities:** Deny specific caps to specific user roles. 26 | * **Clone Roles:** Build a new role by cloning an existing role. 27 | * **Content Permissions:** Gives you control over which users (by role) have access to post content. 28 | * **Shortcodes:** Shortcodes to control who has access to content. 29 | * **Widgets:** A login form widget and users widget to show in your theme's sidebars. 30 | * **Private Site:** You can make your site and its feed completely private if you want. 31 | * **Plugin Integration:** Members is highly recommended by other WordPress developers. Many existing plugins integrate their custom roles and caps directly into it. 32 | 33 | For more info, visit the [Members plugin home page](https://themehybrid.com/plugins/members). 34 | 35 | ### Like this plugin? 36 | 37 | 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: 38 | 39 | * [Making a donation](https://themehybrid.com/plugins/members#donate). 40 | * [Signing up at my site](https://themehybrid.com/plugins/members). 41 | * [Rating the plugin](https://wordpress.org/support/view/plugin-reviews/members?rate=5#postform). 42 | 43 | ### Professional Support 44 | 45 | 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). 46 | 47 | ### Plugin Development 48 | 49 | 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). 50 | 51 | == Installation == 52 | 53 | 1. Upload `members` to the `/wp-content/plugins/` directory. 54 | 2. Activate the plugin through the 'Plugins' menu in WordPress. 55 | 3. Go to "Settings > Members" to select which settings you'd like to use. 56 | 57 | More detailed instructions are included in the plugin's `readme.html` file. 58 | 59 | == Frequently Asked Questions == 60 | 61 | ### Why was this plugin created? 62 | 63 | I wasn't satisfied with the current user, role, and permissions plugins available. Yes, some of them are good, but nothing fit what I had in mind perfectly. Some offered few features. Some worked completely outside of the WordPress APIs. Others lacked the GPL license. 64 | 65 | So, I just built something I actually enjoyed using. 66 | 67 | ### How do I use it? 68 | 69 | Most things should be fairly straightforward, but I've included an in-depth guide in the plugin download. It's a file called `readme.md` in the plugin folder. 70 | 71 | You can also [view the readme](https://github.com/justintadlock/members/blob/master/readme.md) online. 72 | 73 | ### Minimum PHP requirements. 74 | 75 | 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. 76 | 77 | 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. 78 | 79 | ### I can't access the "Role Manager" features. 80 | 81 | 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. 82 | 83 | 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. 84 | 85 | ### On multisite, why can't administrators cannot manage roles? 86 | 87 | 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. 88 | 89 | _Note: This change was made in version 2.0.2 and has no effect on existing installs of Members on existing sub-sites._ 90 | 91 | ### Help! I've locked myself out of my site! 92 | 93 | 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. 94 | 95 | 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. 96 | 97 | == Screenshots == 98 | 99 | 1. Role management screen 100 | 2. Edit role screen 101 | 3. Content permissions meta box (edit post/page screen) 102 | 4. Plugin settings screen 103 | 5. Select multiple roles per user (edit user screen) 104 | 105 | == Upgrade Notice == 106 | 107 | If upgrading from a version prior to 2.0.0, please note that the plugin now requires PHP 5.3.0 or later. 108 | 109 | == Changelog == 110 | 111 | 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. 112 | -------------------------------------------------------------------------------- /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justintadlock/members/07a1eeebdb1efee61534885f4e087ac167d46736/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justintadlock/members/07a1eeebdb1efee61534885f4e087ac167d46736/screenshot-2.png -------------------------------------------------------------------------------- /screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justintadlock/members/07a1eeebdb1efee61534885f4e087ac167d46736/screenshot-3.png -------------------------------------------------------------------------------- /screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justintadlock/members/07a1eeebdb1efee61534885f4e087ac167d46736/screenshot-4.png -------------------------------------------------------------------------------- /screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justintadlock/members/07a1eeebdb1efee61534885f4e087ac167d46736/screenshot-5.png -------------------------------------------------------------------------------- /templates/comments.php: -------------------------------------------------------------------------------- 1 |