├── COPYRIGHT.txt ├── LICENSE.txt ├── changelog.txt ├── css ├── groups-menu.css ├── groups-uie.css ├── groups.css ├── groups_admin.css ├── groups_admin_post.css ├── groups_admin_user.css └── selectize │ ├── selectize.bootstrap2.css │ ├── selectize.bootstrap3.css │ ├── selectize.css │ ├── selectize.default.css │ └── selectize.legacy.css ├── groups.php ├── images ├── add-ons │ ├── affiliates-enterprise.png │ ├── affiliates-pro.png │ ├── groups-drip-content.png │ ├── groups-file-access.png │ ├── groups-forums.png │ ├── groups-gravity-forms.png │ ├── groups-import-export.png │ ├── groups-newsletters.png │ ├── groups-restrict-categories.png │ ├── itthinx-mail-queue.png │ ├── widgets-control-pro.png │ └── woocommerce │ │ ├── group-coupons.png │ │ ├── groups-woocommerce.png │ │ ├── restrict-payment-methods.png │ │ ├── sales-analysis.png │ │ ├── volume-discount-coupons.png │ │ ├── woo.png │ │ ├── woocommerce-bookings.png │ │ ├── woocommerce-product-search.png │ │ ├── woocommerce-subscriptions.png │ │ └── woopayments.png ├── add.png ├── edit.png ├── groups-256x256.png ├── groups-dark.svg ├── groups-grey-8x8.png ├── groups-light.svg ├── groups.png ├── groups.svg ├── help.png ├── refresh.png ├── remove.png └── required.png ├── js ├── groups_admin.js └── selectize │ ├── selectize.js │ └── selectize.min.js ├── languages ├── groups-de.mo ├── groups-de.po ├── groups-de_formal.mo ├── groups-de_formal.po ├── groups-es.mo ├── groups-es.po ├── groups-fr.mo ├── groups-fr.po ├── groups-id_ID.mo ├── groups-id_ID.po ├── groups-lt_LT.mo ├── groups-lt_LT.po ├── groups-nl_NL.mo ├── groups-nl_NL.po ├── groups-pt_BR.mo ├── groups-pt_BR.po ├── groups-sv_SE.mo ├── groups-sv_SE.po └── groups.pot ├── legacy ├── access │ ├── class-groups-access-meta-boxes-legacy.php │ └── class-groups-post-access-legacy.php └── admin │ ├── class-groups-admin-post-columns-legacy.php │ ├── class-groups-admin-posts-legacy.php │ └── groups-admin-options-legacy.php ├── lib ├── access │ ├── class-groups-access-meta-boxes.php │ ├── class-groups-access-shortcodes.php │ ├── class-groups-comment-access.php │ └── class-groups-post-access.php ├── admin │ ├── class-groups-admin-notice.php │ ├── class-groups-admin-post-columns.php │ ├── class-groups-admin-posts.php │ ├── class-groups-admin-user-profile.php │ ├── class-groups-admin-users.php │ ├── class-groups-admin-welcome.php │ ├── class-groups-admin.php │ ├── groups-admin-add-ons.php │ ├── groups-admin-capabilities-add.php │ ├── groups-admin-capabilities-edit.php │ ├── groups-admin-capabilities-remove.php │ ├── groups-admin-capabilities.php │ ├── groups-admin-groups-add.php │ ├── groups-admin-groups-edit.php │ ├── groups-admin-groups-remove.php │ ├── groups-admin-groups.php │ ├── groups-admin-options.php │ └── groups-admin-tree-view.php ├── auto │ └── class-groups-registered.php ├── blocks │ ├── .gitignore │ ├── build │ │ ├── index.asset.php │ │ ├── index.css │ │ └── index.js │ ├── package.json │ └── src │ │ ├── blocks.js │ │ ├── blocks │ │ ├── groups-member │ │ │ ├── block.js │ │ │ └── editor.scss │ │ ├── groups-non-member │ │ │ ├── block.js │ │ │ └── editor.scss │ │ └── store.js │ │ ├── class-groups-blocks.php │ │ ├── common.scss │ │ └── index.js ├── core │ ├── class-groups-cache-object.php │ ├── class-groups-cache.php │ ├── class-groups-capability.php │ ├── class-groups-controller.php │ ├── class-groups-group-capability.php │ ├── class-groups-group.php │ ├── class-groups-help.php │ ├── class-groups-options.php │ ├── class-groups-pagination.php │ ├── class-groups-user-capability.php │ ├── class-groups-user-group.php │ ├── class-groups-user.php │ ├── class-groups-utility.php │ ├── constants.php │ ├── interface-i-capable.php │ └── wp-init.php ├── extra │ └── class-groups-extra.php ├── views │ ├── class-groups-shortcodes.php │ └── class-groups-uie.php └── wp │ └── class-groups-wordpress.php └── readme.txt /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | 2 | Groups 3 | 4 | Copyright 2011-2024 "kento" (Karim Rahimpur) www.itthinx.com 5 | 6 | The files COPYRIGHT.txt and LICENSE.txt as well as ALL NOTICES IN THE 7 | HEADERS OF ALL FILES MUST BE KEPT INTACT. 8 | 9 | GPL-licensed 10 | 11 | Unless otherwise stated, all code in this plugin is licensed under 12 | the GPL License: 13 | 14 | All code is part of this plugin, hereinafter referred to as "program". 15 | 16 | This program is free software: you can redistribute it and/or modify 17 | it under the terms of the GNU General Public License as published by 18 | the Free Software Foundation, either version 3 of the License, or 19 | (at your option) any later version. 20 | 21 | This program is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | GNU General Public License for more details. 25 | 26 | You should have received a copy of the GNU General Public License 27 | along with this program. If not, see . 28 | 29 | Visit https://www.itthinx.com/ for help, general info and fun. 30 | 31 | Plugin page: https://www.itthinx.com/plugins/groups/ 32 | -------------------------------------------------------------------------------- /css/groups-menu.css: -------------------------------------------------------------------------------- 1 | /** 2 | * groups-menu.css 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Karim Rahimpur 17 | * @package groups 18 | * @since groups 2.16.0 19 | */ 20 | li#toplevel_page_groups-admin .wp-menu-image, 21 | li#toplevel_page_groups-network-admin .wp-menu-image { 22 | background-image: url( ../images/groups.svg ); 23 | background-position: center center; 24 | background-repeat: no-repeat; 25 | } 26 | body.admin-color-blue li#toplevel_page_groups-admin .wp-menu-image, 27 | body.admin-color-blue li#toplevel_page_groups-network-admin .wp-menu-image { 28 | background-image: url( ../images/groups-dark.svg ); 29 | } 30 | body.admin-color-blue li.wp-has-current-submenu#toplevel_page_groups-admin .wp-menu-image, 31 | body.admin-color-blue li.wp-has-current-submenu#toplevel_page_groups-network-admin .wp-menu-image { 32 | background-image: url( ../images/groups.svg ); 33 | } 34 | body.admin-color-ocean li#toplevel_page_groups-admin .wp-menu-image, 35 | body.admin-color-ocean li#toplevel_page_groups-network-admin .wp-menu-image { 36 | background-image: url( ../images/groups-light.svg ); 37 | } 38 | body.admin-color-ocean li.wp-has-current-submenu#toplevel_page_groups-admin .wp-menu-image, 39 | body.admin-color-ocean li.wp-has-current-submenu#toplevel_page_groups-network-admin .wp-menu-image { 40 | background-image: url( ../images/groups.svg ); 41 | } 42 | body.admin-color-sunrise li#toplevel_page_groups-admin .wp-menu-image, 43 | body.admin-color-sunrise li#toplevel_page_groups-network-admin .wp-menu-image { 44 | background-image: url( ../images/groups-light.svg ); 45 | } 46 | body.admin-color-sunrise li.wp-has-current-submenu#toplevel_page_groups-admin .wp-menu-image, 47 | body.admin-color-sunrise li.wp-has-current-submenu#toplevel_page_groups-network-admin .wp-menu-image { 48 | background-image: url( ../images/groups.svg ); 49 | } 50 | body.admin-color-light li.wp-has-current-submenu#toplevel_page_groups-admin .wp-menu-image, 51 | body.admin-color-light li.wp-has-current-submenu#toplevel_page_groups-network-admin .wp-menu-image { 52 | background-image: url( ../images/groups-light.svg ); 53 | } 54 | body.admin-color-midnight li.wp-has-current-submenu#toplevel_page_groups-admin .wp-menu-image, 55 | body.admin-color-midnight li.wp-has-current-submenu#toplevel_page_groups-network-admin .wp-menu-image { 56 | background-image: url( ../images/groups-dark.svg ); 57 | } 58 | -------------------------------------------------------------------------------- /css/groups-uie.css: -------------------------------------------------------------------------------- 1 | /** 2 | * groups-uie.css 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Karim Rahimpur 17 | * @package groups 18 | * @since groups 2.6.1 19 | */ 20 | 21 | .selectize-dropdown.groups, 22 | .selectize-dropdown.groups-read, 23 | .selectize-dropdown.capability { 24 | width: auto !important; 25 | } 26 | .selectize-dropdown.groups .selectize-dropdown-content .option, 27 | .selectize-dropdown.capability .selectize-dropdown-content .option { 28 | text-align: initial; 29 | } 30 | .select-read-groups-container .selectize-control.groups-read div.selectize-input { 31 | box-sizing: border-box; 32 | } 33 | -------------------------------------------------------------------------------- /css/groups.css: -------------------------------------------------------------------------------- 1 | /** 2 | * groups.css 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Karim Rahimpur 17 | * @package groups 18 | * @since groups 1.0.0 19 | */ 20 | -------------------------------------------------------------------------------- /css/groups_admin_post.css: -------------------------------------------------------------------------------- 1 | /** 2 | * groups_admin_post.css 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Karim Rahimpur 17 | * @package groups 18 | * @since groups 2.18.0 19 | */ 20 | 21 | .groups-groups-container .selectize-input input[type="select-multiple"] { 22 | font-size: inherit; 23 | vertical-align: middle; 24 | height: 28px; 25 | line-height: 28px; 26 | } 27 | -------------------------------------------------------------------------------- /css/groups_admin_user.css: -------------------------------------------------------------------------------- 1 | /** 2 | * groups_admin_user.css 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Karim Rahimpur 17 | * @package groups 18 | * @since groups 2.18.0 19 | */ 20 | 21 | /* .subsubsub rule added because with views_users() the list can get long icon distinguishes from role links */ 22 | .subsubsub { white-space: normal; } 23 | a.group { 24 | background: url(../images/groups-grey-8x8.png) transparent no-repeat left center; 25 | padding-left: 10px; 26 | } 27 | 28 | /* groups-actions */ 29 | .groups-bulk-container { display: inline-block; line-height: 24px; padding-bottom: 2px; vertical-align: top; margin-left: 0.31em; margin-right: 0.31em; } 30 | .groups-bulk-container .groups-select-container { display: inline-block; vertical-align: top; } 31 | .groups-bulk-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; } 32 | .groups-bulk-container .selectize-control { min-width: 128px; } 33 | .groups-bulk-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; } 34 | .groups-bulk-container .selectize-input { font-size: inherit; line-height: 18px; padding: 0px 2px 0px 2px; vertical-align: middle; } 35 | .groups-bulk-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; height: 24px; } 36 | .groups-bulk-container input.button { margin-top: 1px; vertical-align: top; } 37 | .tablenav .actions { overflow: visible; } /* this is important so that the selectize options aren't hidden */ 38 | 39 | /* groups-filter */ 40 | .groups-filter-container { display: inline-block; line-height: 24px; vertical-align: middle; } 41 | .groups-filter-container .groups-select-container { display: inline-block; vertical-align: top; } 42 | .groups-filter-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; } 43 | .groups-filter-container .selectize-control { min-width: 128px; } 44 | .groups-filter-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; } 45 | .groups-filter-container .selectize-input { font-size: inherit; line-height: 18px; padding: 0px 2px 0px 2px; vertical-align: middle; } 46 | .groups-filter-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; height: 24px; } 47 | .groups-filter-container .selectize-input .item a { line-height: inherit; } /* neutralize .subsubsub a rule */ 48 | .groups-filter-container input.button { margin-top: 1px; vertical-align: top; } 49 | 50 | .groups-select-container .selectize-input input[type="select-multiple"] { 51 | font-size: inherit; 52 | vertical-align: middle; 53 | height: 28px; 54 | line-height: 28px; 55 | } 56 | -------------------------------------------------------------------------------- /groups.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /images/groups-grey-8x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/images/groups-grey-8x8.png -------------------------------------------------------------------------------- /images/groups-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /images/groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/images/groups.png -------------------------------------------------------------------------------- /images/groups.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /images/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/images/help.png -------------------------------------------------------------------------------- /images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/images/refresh.png -------------------------------------------------------------------------------- /images/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/images/remove.png -------------------------------------------------------------------------------- /images/required.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/images/required.png -------------------------------------------------------------------------------- /js/groups_admin.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/js/groups_admin.js -------------------------------------------------------------------------------- /languages/groups-de.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-de.mo -------------------------------------------------------------------------------- /languages/groups-de_formal.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-de_formal.mo -------------------------------------------------------------------------------- /languages/groups-es.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-es.mo -------------------------------------------------------------------------------- /languages/groups-fr.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-fr.mo -------------------------------------------------------------------------------- /languages/groups-id_ID.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-id_ID.mo -------------------------------------------------------------------------------- /languages/groups-lt_LT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-lt_LT.mo -------------------------------------------------------------------------------- /languages/groups-nl_NL.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-nl_NL.mo -------------------------------------------------------------------------------- /languages/groups-pt_BR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-pt_BR.mo -------------------------------------------------------------------------------- /languages/groups-sv_SE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itthinx/groups/f49a2b6085b7caf15faa57b70efefd56786156d7/languages/groups-sv_SE.mo -------------------------------------------------------------------------------- /legacy/admin/class-groups-admin-post-columns-legacy.php: -------------------------------------------------------------------------------- 1 | true ) ); 48 | $post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() ); 49 | foreach ( $post_types as $post_type ) { 50 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { 51 | if ( ( $post_type == 'attachment' ) ) { 52 | // filters to display the media's access restriction capabilities 53 | add_filter( 'manage_media_columns', array( __CLASS__, 'columns' ) ); 54 | // args: string $column_name, int $media_id 55 | add_action( 'manage_media_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 ); 56 | } else { 57 | // filters to display the posts' access restriction capabilities 58 | add_filter( 'manage_' . $post_type . '_posts_columns', array( __CLASS__, 'columns' ) ); 59 | // args: string $column_name, int $post_id 60 | add_action( 'manage_' . $post_type . '_posts_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 ); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Adds a new column to the post type's table showing the access 69 | * restriction capabilities. 70 | * 71 | * @param array $column_headers 72 | * @return array column headers 73 | */ 74 | public static function columns( $column_headers ) { 75 | $column_headers[self::CAPABILITIES] = sprintf( 76 | /* translators: explanation */ 77 | __( 'Access Restrictions', 'groups' ), 78 | esc_attr( __( 'One or more capabilities required to read the entry.', 'groups' ) ) 79 | ); 80 | return $column_headers; 81 | } 82 | 83 | /** 84 | * Renders custom column content. 85 | * 86 | * @param string $column_name 87 | * @param int $post_id 88 | */ 89 | public static function custom_column( $column_name, $post_id ) { 90 | $output = ''; 91 | switch ( $column_name ) { 92 | case self::CAPABILITIES : 93 | $read_caps = get_post_meta( $post_id, Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY ); 94 | $valid_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) ); 95 | if ( count( $valid_read_caps ) > 0 ) { 96 | sort( $valid_read_caps ); 97 | $output = ''; 108 | } else { 109 | $output .= ''; 110 | } 111 | break; 112 | } 113 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 114 | } 115 | } 116 | Groups_Admin_Post_Columns_Legacy::init(); 117 | -------------------------------------------------------------------------------- /legacy/admin/groups-admin-options-legacy.php: -------------------------------------------------------------------------------- 1 | capability, $valid_read_caps ) ) { 48 | $valid_read_caps[] = $valid_cap->capability; 49 | } 50 | } 51 | } 52 | } 53 | Groups_Options::update_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, $valid_read_caps ); 54 | } 55 | } 56 | 57 | // 58 | // render legacy settings 59 | // 60 | echo '

' . esc_html__( 'Capabilities', 'groups' ) . '

'; 61 | 62 | echo '

' . 63 | esc_html__( 'Include these capabilities to enforce read access on posts. The selected capabilities will be offered to restrict access to posts.', 'groups' ) . 64 | '

'; 65 | 66 | $capability_table = _groups_get_tablename( 'capability' ); 67 | $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 68 | $applicable_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) ); 69 | echo '
'; 70 | printf( ''; 79 | echo '
'; // .select-capability-container 80 | 81 | echo Groups_UIE::render_select( '.select.capability' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 82 | 83 | } 84 | 85 | add_action( 'groups_admin_options_legacy', 'groups_admin_options_legacy' ); 86 | -------------------------------------------------------------------------------- /lib/access/class-groups-access-shortcodes.php: -------------------------------------------------------------------------------- 1 | '' ), $atts ); 59 | $show_content = false; 60 | if ( $content !== null ) { 61 | $groups_user = new Groups_User( get_current_user_id() ); 62 | $groups = explode( ',', $options['group'] ); 63 | foreach ( $groups as $group ) { 64 | $group = addslashes( trim( $group ) ); 65 | $current_group = Groups_Group::read( $group ); 66 | if ( !$current_group ) { 67 | $current_group = Groups_Group::read_by_name( $group ); 68 | } 69 | if ( $current_group ) { 70 | if ( $groups_user->is_member( $current_group->group_id ) ) { 71 | $show_content = true; 72 | break; 73 | } 74 | } 75 | } 76 | if ( $show_content ) { 77 | remove_shortcode( 'groups_member' ); 78 | $content = do_shortcode( $content ); 79 | add_shortcode( 'groups_member', array( __CLASS__, 'groups_member' ) ); 80 | $output = $content; 81 | } 82 | } 83 | return $output; 84 | } 85 | 86 | /** 87 | * Takes one attribute "group" which is a comma-separated list of group 88 | * names or ids (can be mixed). 89 | * 90 | * The content is shown if the current user does NOT belong to the group(s). 91 | * 92 | * @param array $atts attributes 93 | * @param string $content content to render 94 | * 95 | * @return string 96 | */ 97 | public static function groups_non_member( $atts, $content = null ) { 98 | $output = ''; 99 | $options = shortcode_atts( array( 'group' => '' ), $atts ); 100 | $show_content = true; 101 | if ( $content !== null ) { 102 | $groups_user = new Groups_User( get_current_user_id() ); 103 | $groups = explode( ',', $options['group'] ); 104 | foreach ( $groups as $group ) { 105 | $group = addslashes( trim( $group ) ); 106 | $current_group = Groups_Group::read( $group ); 107 | if ( !$current_group ) { 108 | $current_group = Groups_Group::read_by_name( $group ); 109 | } 110 | if ( $current_group ) { 111 | if ( $groups_user->is_member( $current_group->group_id ) ) { 112 | $show_content = false; 113 | break; 114 | } 115 | } 116 | } 117 | if ( $show_content ) { 118 | remove_shortcode( 'groups_non_member' ); 119 | $content = do_shortcode( $content ); 120 | add_shortcode( 'groups_non_member', array( __CLASS__, 'groups_non_member' ) ); 121 | $output = $content; 122 | } 123 | } 124 | return $output; 125 | } 126 | 127 | /** 128 | * Takes one attribute "capability" that must be a valid capability label 129 | * or a list of capabilities separated by comma. 130 | * 131 | * The content is shown if the current user has one of the capabilities. 132 | * 133 | * @param array $atts attributes 134 | * @param string $content content to render 135 | * 136 | * @return string 137 | */ 138 | public static function groups_can( $atts, $content = null ) { 139 | $output = ''; 140 | $options = shortcode_atts( array( 'capability' => '' ), $atts ); 141 | if ( $content !== null ) { 142 | $groups_user = new Groups_User( get_current_user_id() ); 143 | $capability = $options['capability']; 144 | $capabilities = array_map( 'trim', explode( ',', $capability ) ); 145 | $show_content = false; 146 | foreach( $capabilities as $capability ) { 147 | if ( $groups_user->can( $capability ) ) { 148 | $show_content = true; 149 | break; 150 | } 151 | } 152 | if ( $show_content ) { 153 | remove_shortcode( 'groups_can' ); 154 | $content = do_shortcode( $content ); 155 | add_shortcode( 'groups_can', array( __CLASS__, 'groups_can' ) ); 156 | $output = $content; 157 | } 158 | } 159 | return $output; 160 | } 161 | 162 | /** 163 | * Takes one attribute "capability" that must be a valid capability label, 164 | * or a comma-separated list of those. 165 | * 166 | * The content is shown if the current user has none of the capabilities. 167 | * 168 | * @param array $atts attributes 169 | * @param string $content content to render 170 | * 171 | * @return string 172 | */ 173 | public static function groups_can_not( $atts, $content = null ) { 174 | $output = ''; 175 | $options = shortcode_atts( array( 'capability' => '' ), $atts ); 176 | if ( $content !== null ) { 177 | $groups_user = new Groups_User( get_current_user_id() ); 178 | $capability = $options['capability']; 179 | $capabilities = array_map( 'trim', explode( ',', $capability ) ); 180 | $show_content = true; 181 | foreach( $capabilities as $capability ) { 182 | if ( $groups_user->can( $capability ) ) { 183 | $show_content = false; 184 | break; 185 | } 186 | } 187 | if ( $show_content ) { 188 | remove_shortcode( 'groups_can_not' ); 189 | $content = do_shortcode( $content ); 190 | add_shortcode( 'groups_can_not', array( __CLASS__, 'groups_can_not' ) ); 191 | $output = $content; 192 | } 193 | } 194 | return $output; 195 | } 196 | } 197 | Groups_Access_Shortcodes::init(); 198 | -------------------------------------------------------------------------------- /lib/admin/class-groups-admin-notice.php: -------------------------------------------------------------------------------- 1 | = self::SHOW_LAPSE ) { 91 | $remind_later_notice = get_user_meta( $user_id, self::REMIND_LATER_NOTICE, true ); 92 | if ( empty( $remind_later_notice ) || ( time() > $remind_later_notice ) ) { 93 | add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) ); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * Initializes if necessary and returns the init time. 103 | * 104 | * @return int 105 | */ 106 | public static function get_init_time() { 107 | $init_time = get_site_option( self::INIT_TIME, null ); 108 | if ( $init_time === null ) { 109 | $init_time = time(); 110 | add_site_option( self::INIT_TIME, $init_time ); 111 | } 112 | return $init_time; 113 | } 114 | 115 | /** 116 | * Adds the admin notice. 117 | */ 118 | public static function admin_notices() { 119 | 120 | $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 121 | $hide_url = wp_nonce_url( add_query_arg( self::HIDE_REVIEW_NOTICE, true, $current_url ), 'hide', 'groups_notice' ); 122 | $remind_url = wp_nonce_url( add_query_arg( self::REMIND_LATER_NOTICE, true, $current_url ), 'later', 'groups_notice' ); 123 | 124 | $output = ''; 125 | 126 | $output .= ''; 133 | 134 | $output .= '
'; 135 | $output .= '

'; 136 | $output .= wp_kses_post( __( 'Many thanks for using Groups!', 'groups' ) ); 137 | $output .= ' '; 138 | $output .= esc_html__( 'Could you please spare a minute and give it a review over at WordPress.org?', 'groups' ); 139 | $output .= ' '; 140 | $output .= sprintf( 141 | '%s', 142 | esc_attr__( 'I have already done that or do not want to submit a review.', 'groups' ), 143 | esc_url( $hide_url ), 144 | esc_html__( 'Dismiss', 'groups' ) 145 | ); 146 | $output .= '

'; 147 | $output .= '

'; 148 | $output .= sprintf( 149 | '%s', 150 | esc_attr__( 'I want to submit a review right now!', 'groups' ), 151 | esc_url( 'https://wordpress.org/support/view/plugin-reviews/groups?filter=5#postform' ), 152 | esc_html__( 'Yes, here we go!', 'groups' ) 153 | ); 154 | $output .= ' '; 155 | $output .= sprintf( 156 | '%s', 157 | esc_attr__( 'I want to submit a review later, remind me!', 'groups' ), 158 | esc_url( $remind_url ), 159 | esc_html__( 'Remind me later', 'groups' ) 160 | ); 161 | 162 | $output .= '

'; 163 | $output .= '

'; 164 | $output .= sprintf( 165 | /* translators: 1: link, 2: link */ 166 | __( 'Follow %1$s and visit %2$s to stay tuned for free and premium tools.', 'groups' ), 167 | '@itthinx', 168 | 'itthinx.com' 169 | ); 170 | $output .= '

'; 171 | $output .= '
'; 172 | 173 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 174 | } 175 | } 176 | Groups_Admin_Notice::init(); 177 | -------------------------------------------------------------------------------- /lib/admin/class-groups-admin-post-columns.php: -------------------------------------------------------------------------------- 1 | true ) ); 74 | $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); 75 | foreach ( $post_types as $post_type ) { 76 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { 77 | if ( ( $post_type == 'attachment' ) ) { 78 | // filters to display the media's access restriction groups 79 | add_filter( 'manage_media_columns', array( __CLASS__, 'columns' ) ); 80 | // args: string $column_name, int $media_id 81 | add_action( 'manage_media_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 ); 82 | // make the groups column sortable 83 | add_filter( 'manage_upload_sortable_columns', array( __CLASS__, 'manage_edit_post_sortable_columns' ) ); 84 | } else { 85 | // filters to display the posts' access restriction groups 86 | add_filter( 'manage_' . $post_type . '_posts_columns', array( __CLASS__, 'columns' ) ); 87 | // args: string $column_name, int $post_id 88 | add_action( 'manage_' . $post_type . '_posts_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 ); 89 | // make the groups column sortable 90 | add_filter( 'manage_edit-' . $post_type . '_sortable_columns', array( __CLASS__, 'manage_edit_post_sortable_columns' ) ); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Adds a new column to the post type's table showing the access 99 | * restriction groups. 100 | * 101 | * @param array $column_headers 102 | * 103 | * @return array column headers 104 | */ 105 | public static function columns( $column_headers ) { 106 | $column_headers[self::GROUPS] = sprintf( 107 | '%s' . 108 | ' ', 109 | esc_attr__( 'One or more groups granting access to entries.', 'groups' ), 110 | esc_html_x( 'Groups', 'Column header', 'groups' ) 111 | ); 112 | return $column_headers; 113 | } 114 | 115 | /** 116 | * Renders custom column content. 117 | * 118 | * @param string $column_name 119 | * @param int $post_id 120 | */ 121 | public static function custom_column( $column_name, $post_id ) { 122 | $output = ''; 123 | switch ( $column_name ) { 124 | case self::GROUPS : 125 | $entries = array(); 126 | $groups_read = Groups_Post_Access::get_read_group_ids( $post_id ); 127 | 128 | if ( count( $groups_read ) > 0 ) { 129 | $groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $groups_read ) ); 130 | if ( ( count( $groups ) > 0 ) ) { 131 | foreach( $groups as $group ) { 132 | $entries[] = $group->name ? stripslashes( wp_strip_all_tags( $group->name ) ) : ''; 133 | } 134 | } 135 | } 136 | if ( 137 | function_exists( 'get_term_meta' ) && // >= WordPress 4.4 138 | class_exists( 'Groups_Restrict_Categories' ) && 139 | method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) && 140 | method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0 141 | ) { 142 | $taxonomies = Groups_Restrict_Categories::get_controlled_taxonomies(); 143 | if ( count( $taxonomies ) > 0 ) { 144 | $terms = wp_get_object_terms( $post_id, $taxonomies ); 145 | if ( !( $terms instanceof WP_Error ) ) { 146 | foreach( $terms as $term ) { 147 | if ( in_array( $term->taxonomy, $taxonomies ) ) { 148 | $term_group_ids = Groups_Restrict_Categories::get_term_read_groups( $term->term_id ); 149 | if ( count( $term_group_ids ) > 0 ) { 150 | 151 | if ( !empty( $term_group_ids ) ) { 152 | $edit_term_link = self::get_edit_term_link( $term->term_id, $term->taxonomy ); 153 | $taxonomy_label = ''; 154 | if ( $taxonomy = get_taxonomy( $term->taxonomy ) ) { 155 | $taxonomy_label = isset( $taxonomy->label ) ? $taxonomy->label : ''; // $taxonomy->label is already translated 156 | $labels = isset( $taxonomy->labels ) ? $taxonomy->labels : null; 157 | if ( $labels !== null ) { 158 | if ( isset( $labels->singular_name ) ) { 159 | $taxonomy_label = $labels->singular_name; // this is already translated 160 | } 161 | } 162 | } 163 | $term_taxonomy_title = !empty( $term->name ) ? $term->name : ''; 164 | $term_taxonomy_title.= !empty( $taxonomy_label ) ? ' ' . $taxonomy_label : ''; 165 | foreach( $term_group_ids as $group_id ) { 166 | if ( $group = Groups_Group::read( $group_id ) ) { 167 | $entries[] = sprintf( 168 | '%s %s', 169 | $group->name ? stripslashes( wp_strip_all_tags( $group->name ) ) : '', 170 | esc_url( $edit_term_link ), 171 | esc_attr( $term_taxonomy_title), 172 | esc_html( $term->name ) 173 | ); 174 | } 175 | } 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | } 183 | if ( !empty( $entries ) ) { 184 | sort( $entries ); 185 | $output .= ''; 192 | } 193 | break; 194 | } 195 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 196 | } 197 | 198 | /** 199 | * Helper to reduce query redundancy due to usage of current_user_can() in get_edit_term_link() etc. 200 | * 201 | * @param int $term_id 202 | * @param string $taxonomy 203 | * 204 | * @return string or null if edit link could not be retrieved 205 | */ 206 | private static function get_edit_term_link( $term_id, $taxonomy ) { 207 | $result = null; 208 | $user_id = get_current_user_id(); 209 | $cached = Groups_Cache::get( self::EDIT_TERM_LINK . '_' . $term_id . '_' . $user_id, self::CACHE_GROUP ); 210 | if ( $cached !== null ) { 211 | $result = $cached->value; 212 | unset( $cached ); 213 | } else { 214 | $result = get_edit_term_link( $term_id, $taxonomy ); 215 | Groups_Cache::set( self::EDIT_TERM_LINK . '_' . $term_id . '_' . $user_id, $result, self::CACHE_GROUP ); 216 | } 217 | return $result; 218 | } 219 | 220 | /** 221 | * Groups column is sortable. 222 | * 223 | * Sorting depends on the filters Groups_Admin_Posts::posts_join() and Groups_Admin_Posts::posts_orderby() 224 | * which add the relevant group information and sort by group name. 225 | * 226 | * @see Groups_Admin_Posts::posts_join() 227 | * @see Groups_Admin_Posts::posts_orderby() 228 | * 229 | * @param array $sortable_columns 230 | * 231 | * @return array 232 | */ 233 | public static function manage_edit_post_sortable_columns( $sortable_columns ) { 234 | $sortable_columns[self::GROUPS] = self::GROUPS; 235 | return $sortable_columns; 236 | } 237 | 238 | } 239 | Groups_Admin_Post_Columns::init(); 240 | -------------------------------------------------------------------------------- /lib/admin/class-groups-admin-welcome.php: -------------------------------------------------------------------------------- 1 | '; 71 | $message .= ''; 72 | $message .= esc_html__( 'Important', 'groups' ); 73 | $message .= ''; 74 | $message .= ' '; 75 | $message .= esc_html__( 'It seems that you have updated from Groups 1.x where access restrictions were based on capabilities.', 'groups' ); 76 | $message .= '

'; 77 | $message .= '

'; 78 | $message .= sprintf( 79 | /* translators: 1: opening tag 2: closing tag */ 80 | esc_html__( 'Please make sure to read the %1$sMigration Guide%2$s.', 'groups' ), 81 | '', 82 | '' 83 | ); 84 | $message .= '

'; 85 | wp_admin_notice( 86 | $message, 87 | array( 88 | 'type' => 'info', 89 | 'dismissible' => true, 90 | 'additional_classes' => array( 'inline', 'notice-alt' ), 91 | 'attributes' => array( 'data-slug' => 'plugin-slug' ) 92 | ) 93 | ); 94 | } 95 | } 96 | 97 | /** 98 | * Checks if the welcome screen should be shown and redirected to. 99 | */ 100 | public static function admin_init() { 101 | global $groups_version; 102 | if ( 103 | Groups_User::current_user_can( GROUPS_ACCESS_GROUPS ) && 104 | isset( $_GET['groups-welcome-dismiss'] ) && 105 | isset( $_GET['_groups_welcome_nonce'] ) 106 | ) { 107 | if ( wp_verify_nonce( $_GET['_groups_welcome_nonce'], 'groups_welcome_dismiss' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 108 | Groups_Options::update_user_option( 'groups-welcome-dismiss', $groups_version ); 109 | } 110 | } 111 | $groups_welcome_dismiss = Groups_Options::get_user_option( 'groups-welcome-dismiss', '' ); 112 | if ( version_compare( $groups_version, $groups_welcome_dismiss ) > 0 ) { 113 | // @see Groups_Controller::activate() 114 | if ( get_transient( 'groups_plugin_activated' ) ) { 115 | $doing_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX; 116 | $doing_cron = defined( 'DOING_CRON' ) && DOING_CRON; 117 | // we'll delete the transients in the welcome screen handler 118 | if ( 119 | !$doing_ajax && 120 | !$doing_cron && 121 | ( empty( $_GET['page'] ) || $_GET['page'] !== 'groups-welcome' ) && 122 | !is_network_admin() && 123 | Groups_User::current_user_can( GROUPS_ACCESS_GROUPS ) && 124 | apply_filters( 'groups_welcome_show', true ) 125 | ) { 126 | wp_safe_redirect( admin_url( 'index.php?page=groups-welcome' ) ); 127 | exit; 128 | } else { 129 | // @since 3.3.1 remove the transient as we don't want to trigger a redirect in any other case than direct activation of the Groups plugin 130 | delete_transient( 'groups_plugin_activated' ); 131 | } 132 | } 133 | } 134 | } 135 | 136 | /** 137 | * Adds an entry leading to the welcome screen. 138 | * 139 | * @param array $links 140 | * @param string $file plugin file basename 141 | * @return array 142 | */ 143 | public static function plugin_row_meta( $links, $file ) { 144 | if ( $file == plugin_basename( GROUPS_FILE ) ) { 145 | $row_meta = array( 146 | 'welcome' => sprintf( 147 | '%s', 148 | esc_url( admin_url( 'index.php?page=groups-welcome' ) ), 149 | esc_attr__( 'View the Welcome screen for this version of Groups', 'groups' ), 150 | esc_html__( 'Welcome', 'groups' ) 151 | ) 152 | ); 153 | return array_merge( $links, $row_meta ); 154 | } 155 | return (array) $links; 156 | } 157 | 158 | /** 159 | * Renders the welcome screen. 160 | */ 161 | public static function groups_welcome() { 162 | 163 | global $groups_version; 164 | 165 | wp_enqueue_style( 'groups_admin' ); 166 | 167 | delete_transient( 'groups_plugin_activated' ); 168 | // As of Groups 3.3.1 this transient does not trigger a redirect to the welcome screen. 169 | // Instead, an admin notice pointing to the migration guide is displayed for as long as the transient has not expired. 170 | // We still delete the transient here as we are displaying the welcome screen, and the notice will be visible on top of it. 171 | delete_transient( 'groups_plugin_updated_legacy' ); 172 | 173 | echo '
'; 174 | echo '
'; 175 | 176 | echo '
'; 177 | 178 | printf( '', esc_attr( GROUPS_PLUGIN_URL . 'images/groups-256x256.png' ) ); 179 | 180 | echo '

'; 181 | /* translators: version number */ 182 | printf( esc_html__( 'Welcome to Groups %s', 'groups' ), esc_html( $groups_version ) ); 183 | echo '

'; 184 | 185 | printf( 186 | '', 187 | esc_url( wp_nonce_url( add_query_arg( 'groups-welcome-dismiss', '1', admin_url() ), 'groups_welcome_dismiss', '_groups_welcome_nonce' ) ), 188 | esc_attr__( 'Dismiss', 'groups' ), 189 | esc_html__( 'Dismiss', 'groups' ) 190 | ); 191 | 192 | echo '

'; 193 | esc_html_e( 'Thanks for using Groups! We have made it even easier to protect your content and hope you like it :)', 'groups' ); 194 | echo '

'; 195 | 196 | echo '

'; 197 | esc_html_e( "What's New?", 'groups' ); 198 | echo '

'; 199 | 200 | echo '

'; 201 | esc_html_e( 'Protect Content Easily', 'groups' ); 202 | echo '

'; 203 | echo '

'; 204 | esc_html_e( 'We have made it even easier to protect your content!', 'groups' ); 205 | echo ' '; 206 | esc_html_e( 'Now you can protect your posts, pages and any other custom post type like products or events by simply assigning them to one or more groups.', 'groups' ); 207 | echo '

'; 208 | 209 | echo '

'; 210 | esc_html_e( 'Efficient User Interface', 'groups' ); 211 | echo '

'; 212 | echo '

'; 213 | esc_html_e( 'Manage groups and users with a minimal footprint on the administrative screens.', 'groups' ); 214 | echo '

'; 215 | 216 | echo '

'; 217 | esc_html_e( 'Documentation', 'groups' ); 218 | echo '

'; 219 | echo '

'; 220 | printf( 221 | /* translators: 1: opening tag, 2: closing tag */ 222 | esc_html__( 'Whether you are new to Groups or have been using it before, please make sure to visit the %1$sDocumentation%2$s pages to know more about how to use it.', 'groups' ), 223 | '', 224 | '' 225 | ); 226 | echo '

'; 227 | 228 | echo '
'; // .groups-welcome-panel-description 229 | 230 | echo '

'; 231 | esc_html_e( 'Add-Ons', 'groups' ); 232 | echo '

'; 233 | echo '

'; 234 | esc_html_e( 'Perfect complements to memberships and access control with Groups.', 'groups' ); 235 | echo '

'; 236 | echo '
'; 237 | groups_admin_add_ons_content( array( 'offset' => 1 ) ); 238 | echo '
'; // .groups-admin-add-ons 239 | 240 | echo '
'; // .groups-welcome-panel-content 241 | echo '
'; // .groups-welcome-panel 242 | } 243 | } 244 | Groups_Admin_Welcome::init(); 245 | -------------------------------------------------------------------------------- /lib/admin/groups-admin-capabilities-add.php: -------------------------------------------------------------------------------- 1 | '; 44 | $output .= '

'; 45 | $output .= esc_html__( 'Add a new capability', 'groups' ); 46 | $output .= '

'; 47 | $output .= Groups_Admin::render_messages(); 48 | $output .= '
'; 49 | $output .= '
'; 50 | 51 | $output .= '
'; 52 | $output .= sprintf( '', esc_html__( 'Capability', 'groups' ) ); 53 | $output .= sprintf( 54 | '', 55 | esc_attr( stripslashes( $capability ) ) 56 | ); 57 | $output .= '
'; 58 | 59 | $output .= '
'; 60 | $output .= sprintf( '', esc_html__( 'Description', 'groups' ) ); 61 | $output .= sprintf( 62 | '', 63 | stripslashes( wp_filter_nohtml_kses( $description ) ) 64 | ); 65 | $output .= '
'; 66 | 67 | $output .= '
'; 68 | $output .= wp_nonce_field( 'capabilities-add', GROUPS_ADMIN_GROUPS_NONCE, true, false ); 69 | $output .= sprintf( '', esc_attr__( 'Add', 'groups' ) ); 70 | $output .= ''; 71 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 72 | $output .= '
'; 73 | $output .= '
'; // .capability.new 74 | $output .= '
'; 75 | $output .= ''; // .manage-capabilities 76 | 77 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 78 | 79 | } // function groups_admin_capabilities_add 80 | 81 | /** 82 | * Handle add capability form submission. 83 | * 84 | * @return int new capability's id or false if unsuccessful 85 | */ 86 | function groups_admin_capabilities_add_submit() { 87 | 88 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 89 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 90 | } 91 | 92 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'capabilities-add' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 93 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 94 | } 95 | 96 | $capability = isset( $_POST['capability-field'] ) ? sanitize_text_field( $_POST['capability-field'] ) : null; 97 | $description = isset( $_POST['description-field'] ) ? sanitize_textarea_field( $_POST['description-field'] ) : ''; 98 | 99 | $capability_id = Groups_Capability::create( compact( "capability", "description" ) ); 100 | if ( !$capability_id ) { 101 | if ( empty( $capability ) ) { 102 | Groups_Admin::add_message( __( 'The Capability must not be empty.', 'groups' ), 'error' ); 103 | } else if ( Groups_Capability::read_by_capability( $capability ) ) { 104 | /* translators: capability name */ 105 | Groups_Admin::add_message( sprintf( __( 'The %s capability already exists.', 'groups' ), stripslashes( wp_filter_nohtml_kses( ( $capability ) ) ) ), 'error' ); 106 | } 107 | } 108 | return $capability_id; 109 | } // function groups_admin_capabilities_add_submit 110 | -------------------------------------------------------------------------------- /lib/admin/groups-admin-capabilities-edit.php: -------------------------------------------------------------------------------- 1 | capability !== null ? $capability->capability : '' ); 50 | $description = isset( $_POST['description-field'] ) ? sanitize_textarea_field( $_POST['description-field'] ) : ( $capability->description !==null ? $capability->description : '' ); 51 | 52 | $capability_readonly = ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) ? "" : ' readonly="readonly" '; 53 | 54 | $output = '
'; 55 | $output .= '

'; 56 | $output .= esc_html__( 'Edit a capability', 'groups' ); 57 | $output .= '

'; 58 | 59 | $output .= Groups_Admin::render_messages(); 60 | 61 | $output .= sprintf( '
', esc_url( $current_url ) ); 62 | $output .= '
'; 63 | $output .= sprintf( '', esc_attr( intval( $capability_id ) ) ); 64 | 65 | $output .= '
'; 66 | $output .= sprintf( '', esc_html__( 'Capability', 'groups' ) ); 67 | $output .= sprintf( 68 | '', 69 | $capability_readonly, 70 | esc_attr( stripslashes( $capability_capability ) ) 71 | ); 72 | $output .= '
'; 73 | 74 | $output .= '
'; 75 | $output .= sprintf( '', esc_html__( 'Description', 'groups' ) ); 76 | $output .= sprintf( '', stripslashes( wp_filter_nohtml_kses( $description ) ) ); 77 | $output .= '
'; 78 | 79 | $output .= '
'; 80 | $output .= wp_nonce_field( 'capabilities-edit', GROUPS_ADMIN_GROUPS_NONCE, true, false ); 81 | $output .= sprintf( '', esc_attr__( 'Save', 'groups' ) ); 82 | $output .= ''; 83 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 84 | $output .= '
'; 85 | $output .= '
'; // .capability.edit 86 | $output .= '
'; 87 | $output .= '
'; // .manage-capabilities 88 | 89 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 90 | } // function groups_admin_capabilities_edit 91 | 92 | /** 93 | * Handle edit form submission. 94 | * 95 | * @return int|boolean the capability ID if it was updated, otherwise false 96 | */ 97 | function groups_admin_capabilities_edit_submit() { 98 | 99 | $result = false; 100 | 101 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 102 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 103 | } 104 | 105 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'capabilities-edit' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 106 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 107 | } 108 | 109 | $capability_id = isset( $_POST['capability-id-field'] ) ? $_POST['capability-id-field'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 110 | $capability = Groups_Capability::read( $capability_id ); 111 | if ( $capability ) { 112 | $capability = new Groups_Capability( $capability_id ); 113 | $capability_id = $capability->get_capability_id(); 114 | if ( $capability->get_capability() !== Groups_Post_Access::READ_POST_CAPABILITY ) { 115 | $capability_field = isset( $_POST['capability-field'] ) ? sanitize_text_field( $_POST['capability-field'] ) : null; 116 | } else { 117 | $capability_field = Groups_Post_Access::READ_POST_CAPABILITY; 118 | } 119 | if ( !empty( $capability_field ) ) { 120 | $update = true; 121 | if ( $other_capability = Groups_Capability::read_by_capability( $capability_field ) ) { 122 | if ( $other_capability->capability_id != $capability_id ) { 123 | /* translators: capability name */ 124 | Groups_Admin::add_message( sprintf( __( 'The %s capability already exists and cannot be assigned to this one.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $other_capability->capability ) ) ), 'error' ); 125 | $update = false; 126 | } 127 | } 128 | if ( $update ) { 129 | $description = isset( $_POST['description-field'] ) ? sanitize_textarea_field( $_POST['description-field'] ) : ''; 130 | $capability_id = Groups_Capability::update( array( 'capability_id' => $capability_id, 'capability' => $capability_field, 'description' => $description ) ); 131 | if ( $capability_id ) { 132 | $result = $capability_id; 133 | } else { 134 | /* translators: capability name */ 135 | Groups_Admin::add_message( sprintf( __( 'The %s capability could not be updated.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $capability->get_capability() ) ) ), 'error' ); 136 | } 137 | } 138 | } else { 139 | Groups_Admin::add_message( __( 'The Capability must not be empty.', 'groups' ), 'error' ); 140 | } 141 | } 142 | return $result; 143 | } // function groups_admin_capabilities_edit_submit 144 | -------------------------------------------------------------------------------- /lib/admin/groups-admin-capabilities-remove.php: -------------------------------------------------------------------------------- 1 | '; 48 | $output .= '

'; 49 | $output .= esc_html__( 'Remove a capability', 'groups' ); 50 | $output .= '

'; 51 | $output .= sprintf( '
', esc_url( $current_url ) ); 52 | $output .= '
'; 53 | $output .= sprintf( '', esc_attr( intval( $capability->get_capability_id() ) ) ); 54 | $output .= '
    '; 55 | $output .= '
  • '; 56 | $output .= sprintf( '%s : %s', esc_html__( 'Capability', 'groups' ), stripslashes( wp_filter_nohtml_kses( $capability->get_capability() ) ) ); 57 | $output .= '
  • '; 58 | $output .= '
'; 59 | $output .= wp_nonce_field( 'capabilities-remove', GROUPS_ADMIN_GROUPS_NONCE, true, false ); 60 | $output .= sprintf( '', esc_attr__( 'Remove', 'groups' ) ); 61 | $output .= ''; 62 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 63 | $output .= '
'; 64 | $output .= ''; // .capability.remove 65 | $output .= '
'; 66 | $output .= ''; // .manage-capabilities 67 | 68 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 69 | } // function groups_admin_capabilities_remove 70 | 71 | /** 72 | * Handle remove form submission. 73 | * 74 | * @return int|boolean ID of the deleted capability or false on failure 75 | */ 76 | function groups_admin_capabilities_remove_submit() { 77 | 78 | $result = false; 79 | 80 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 81 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 82 | } 83 | 84 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'capabilities-remove' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 85 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 86 | } 87 | 88 | $capability_id = isset( $_POST['capability-id-field'] ) ? $_POST['capability-id-field'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 89 | $capability = Groups_Capability::read( $capability_id ); 90 | if ( $capability ) { 91 | if ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) { 92 | $result = Groups_Capability::delete( $capability_id ); 93 | } 94 | } 95 | return $result; 96 | } // function groups_admin_capabilities_remove_submit 97 | 98 | /** 99 | * Shows form to confirm removal bulk capabilities 100 | */ 101 | function groups_admin_capabilities_bulk_remove() { 102 | 103 | $output = ''; 104 | 105 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 106 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 107 | } 108 | 109 | $capability_ids = isset( $_POST['capability_ids'] ) ? $_POST['capability_ids'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 110 | 111 | if ( $capability_ids === null || !is_array( $capability_ids ) ) { 112 | wp_die( esc_html__( 'No such capabilities.', 'groups' ) ); 113 | } 114 | 115 | $capabilities = array(); 116 | foreach ( $capability_ids as $capability_id ) { 117 | $capability = Groups_Capability::read( intval( $capability_id ) ); 118 | if ( $capability ) { 119 | $capabilities[] = $capability; 120 | } 121 | } 122 | 123 | $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 124 | $current_url = remove_query_arg( 'action', $current_url ); 125 | $current_url = remove_query_arg( 'capability_id', $current_url ); 126 | 127 | $output .= '
'; 128 | $output .= '

'; 129 | $output .= esc_html__( 'Remove capabilities', 'groups' ); 130 | $output .= '

'; 131 | 132 | $output .= '
'; 133 | $output .= '
'; 134 | $output .= '

'; 135 | $output .= esc_html__( 'Please confirm to remove the following capabilities. This action cannot be undone.', 'groups' ); 136 | $output .= '

'; 137 | $output .= '
    '; 138 | foreach ( $capabilities as $capability ) { 139 | $output .= sprintf( '', esc_attr( intval( $capability->capability_id ) ) ); 140 | $output .= '
  • '; 141 | $output .= sprintf( '%s', stripslashes( wp_filter_nohtml_kses( $capability->capability ) ) ); 142 | $output .= '
  • '; 143 | } 144 | $output .= '
'; 145 | $output .= sprintf( '', esc_attr__( 'Remove', 'groups' ) ); 146 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 147 | 148 | $output .= ''; 149 | $output .= ''; 150 | $output .= ''; 151 | $output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false ); 152 | 153 | $output .= '
'; 154 | $output .= '
'; 155 | $output .= '
'; 156 | 157 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 158 | } // function groups_admin_capabilities_bulk_remove 159 | 160 | /** 161 | * Handle remove form submission. 162 | * 163 | * @return array of deleted capabilities' ids 164 | */ 165 | function groups_admin_capabilities_bulk_remove_submit() { 166 | 167 | $result = array(); 168 | 169 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 170 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 171 | } 172 | 173 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_ACTION_NONCE], 'admin' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 174 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 175 | } 176 | 177 | $capability_ids = isset( $_POST['capability_ids'] ) ? $_POST['capability_ids'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 178 | 179 | if ( $capability_ids !== null && is_array( $capability_ids ) ) { 180 | foreach ( $capability_ids as $capability_id ) { 181 | $capability = Groups_Capability::read( $capability_id ); 182 | if ( $capability ) { 183 | if ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) { 184 | if ( Groups_Capability::delete( $capability_id ) ) { 185 | $result[] = $capability->capability_id; 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | return $result; 193 | } // function groups_admin_capabilities_bulk_remove_submit 194 | -------------------------------------------------------------------------------- /lib/admin/groups-admin-groups-add.php: -------------------------------------------------------------------------------- 1 | '; 49 | $parent_select .= sprintf( 50 | '', 51 | empty( $parent_id ) ? 'selected="selected"' : '' 52 | ); 53 | $tree = Groups_Utility::get_group_tree(); 54 | Groups_Utility::render_group_tree_options( $tree, $parent_select, 0, array( $parent_id ) ); 55 | $parent_select .= ''; 56 | 57 | $output .= '
'; 58 | $output .= '

'; 59 | $output .= esc_html__( 'Add a new group', 'groups' ); 60 | $output .= '

'; 61 | 62 | $output .= Groups_Admin::render_messages(); 63 | 64 | $output .= '
'; 65 | $output .= '
'; 66 | 67 | $output .= '
'; 68 | $output .= ''; 71 | $output .= sprintf( '', esc_attr( stripslashes( $name ) ) ); 72 | $output .= '
'; 73 | 74 | $output .= '
'; 75 | $output .= ''; 78 | $output .= $parent_select; 79 | $output .= '
'; 80 | 81 | $output .= '
'; 82 | $output .= ''; 85 | $output .= ''; 88 | $output .= '
'; 89 | 90 | $output .= '
'; 91 | 92 | $capability_table = _groups_get_tablename( "capability" ); 93 | $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 94 | $selected_capabilities = isset( $_POST['capability_ids'] ) && is_array( $_POST['capability_ids'] ) ? $_POST['capability_ids'] : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 95 | 96 | $output .= '
'; 97 | $output .= ''; 113 | $output .= '
'; 114 | $output .= '

'; 115 | $output .= esc_html__( 'These capabilities will be assigned to the group.', 'groups' ); 116 | $output .= '

'; 117 | 118 | $output .= Groups_UIE::render_select( '.select.capability' ); 119 | $output .= '
'; 120 | 121 | $output .= apply_filters( 'groups_admin_groups_add_form_after_fields', '' ); 122 | 123 | $output .= '
'; 124 | $output .= wp_nonce_field( 'groups-add', GROUPS_ADMIN_GROUPS_NONCE, true, false ); 125 | $output .= sprintf( '', esc_attr__( 'Add', 'groups' ) ); 126 | $output .= ''; 127 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 128 | $output .= '
'; 129 | $output .= '
'; // .group.new 130 | $output .= '
'; 131 | $output .= '
'; // .manage-groups 132 | 133 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 134 | } // function groups_admin_groups_add 135 | 136 | /** 137 | * Handle add group form submission. 138 | * 139 | * @return int new group's id or false if unsuccessful 140 | */ 141 | function groups_admin_groups_add_submit() { 142 | 143 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 144 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 145 | } 146 | 147 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'groups-add' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 148 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 149 | } 150 | 151 | $creator_id = get_current_user_id(); 152 | $datetime = date( 'Y-m-d H:i:s', time() ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date 153 | $parent_id = isset( $_POST['parent-id-field'] ) ? sanitize_text_field( $_POST['parent-id-field'] ) : null; 154 | $description = isset( $_POST['description-field'] ) ? sanitize_textarea_field( $_POST['description-field'] ) : ''; 155 | $name = isset( $_POST['name-field'] ) ? sanitize_text_field( $_POST['name-field'] ) : null; 156 | 157 | $group_id = Groups_Group::create( compact( "creator_id", "datetime", "parent_id", "description", "name" ) ); 158 | if ( $group_id ) { 159 | if ( !empty( $_POST['capability_ids'] ) ) { 160 | $caps = $_POST['capability_ids']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 161 | if ( is_array( $caps ) ) { 162 | $caps = array_map( 'sanitize_text_field', $caps ); 163 | foreach( $caps as $cap ) { 164 | Groups_Group_Capability::create( array( 'group_id' => $group_id, 'capability_id' => $cap ) ); 165 | } 166 | } 167 | } 168 | do_action( 'groups_admin_groups_add_submit_success', $group_id ); 169 | } else { 170 | if ( !$name ) { 171 | Groups_Admin::add_message( __( 'The name must not be empty.', 'groups' ), 'error' ); 172 | } else if ( Groups_Group::read_by_name( $name ) ) { 173 | /* translators: group name */ 174 | Groups_Admin::add_message( sprintf( __( 'The %s group already exists.', 'groups' ), stripslashes( wp_filter_nohtml_kses( ( $name ) ) ) ), 'error' ); 175 | } 176 | } 177 | 178 | return $group_id; 179 | } // function groups_admin_groups_add_submit 180 | -------------------------------------------------------------------------------- /lib/admin/groups-admin-groups-remove.php: -------------------------------------------------------------------------------- 1 | '; 48 | $output .= '

'; 49 | $output .= esc_html__( 'Remove a group', 'groups' ); 50 | $output .= '

'; 51 | $output .= sprintf( '
', esc_url( $current_url ) ); 52 | $output .= '
'; 53 | $output .= sprintf( '', esc_attr( intval( $group->group_id ) ) ); 54 | $output .= '
    '; 55 | $output .= '
  • '; 56 | $output .= sprintf( '%s : %s [%d]', esc_html__( 'Group', 'groups' ), stripslashes( wp_filter_nohtml_kses( $group->name ) ), esc_html( $group->group_id ) ); 57 | $output .= '
  • '; 58 | $output .= '
'; 59 | $output .= wp_nonce_field( 'groups-remove', GROUPS_ADMIN_GROUPS_NONCE, true, false ); 60 | $output .= sprintf( '', esc_attr__( 'Remove', 'groups' ) ); 61 | $output .= ''; 62 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 63 | $output .= '
'; // .group.remove 64 | $output .= '
'; 65 | $output .= ''; // .manage-groups 66 | 67 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 68 | } // function groups_admin_groups_remove 69 | 70 | /** 71 | * Handle remove form submission. 72 | * 73 | * @return int|false group ID if successful, otherwise false 74 | */ 75 | function groups_admin_groups_remove_submit() { 76 | 77 | $result = false; 78 | 79 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 80 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 81 | } 82 | 83 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'groups-remove' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 84 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 85 | } 86 | 87 | $group_id = isset( $_POST['group-id-field'] ) ? $_POST['group-id-field'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 88 | $group = Groups_Group::read( $group_id ); 89 | if ( $group ) { 90 | if ( $group->name !== Groups_Registered::REGISTERED_GROUP_NAME ) { 91 | $result = Groups_Group::delete( $group_id ); 92 | } 93 | } 94 | return $result; 95 | } // function groups_admin_groups_remove_submit 96 | 97 | /** 98 | * Shows form to confirm bulk-removal of groups. 99 | */ 100 | function groups_admin_groups_bulk_remove() { 101 | 102 | $output = ''; 103 | 104 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 105 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 106 | } 107 | 108 | $group_ids = isset( $_POST['group_ids'] ) ? $_POST['group_ids'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 109 | if ( $group_ids === null || !is_array( $group_ids ) ) { 110 | wp_die( esc_html__( 'No such groups.', 'groups' ) ); 111 | } 112 | 113 | $groups = array(); 114 | foreach ( $group_ids as $group_id ) { 115 | $group = Groups_Group::read( intval( $group_id ) ); 116 | if ( $group ) { 117 | $groups[] = $group; 118 | } 119 | } 120 | 121 | $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 122 | $current_url = remove_query_arg( 'action', $current_url ); 123 | $current_url = remove_query_arg( 'group_id', $current_url ); 124 | 125 | $output .= '
'; 126 | $output .= '

'; 127 | $output .= esc_html__( 'Remove groups', 'groups' ); 128 | $output .= '

'; 129 | 130 | $output .= '
'; 131 | $output .= '
'; 132 | 133 | $output .= '

'; 134 | $output .= esc_html__( 'Please confirm removal of the following groups. This action cannot be undone.', 'groups' ); 135 | $output .= '

'; 136 | 137 | $output .= '
    '; 138 | foreach ( $groups as $group ) { 139 | $output .= sprintf( '', esc_attr( intval( $group->group_id ) ) ); 140 | $output .= '
  • '; 141 | $output .= sprintf( '%s [%d]', stripslashes( wp_filter_nohtml_kses( $group->name ) ), esc_html( $group->group_id ) ); 142 | $output .= '
  • '; 143 | } 144 | $output .= '
'; 145 | $output .= sprintf( '', esc_attr__( 'Remove', 'groups' ) ); 146 | $output .= sprintf( '%s', esc_url( $current_url ), esc_html__( 'Cancel', 'groups' ) ); 147 | 148 | $output .= ''; 149 | $output .= ''; 150 | $output .= ''; 151 | $output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false ); 152 | 153 | $output .= '
'; 154 | $output .= '
'; 155 | $output .= '
'; 156 | 157 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 158 | } // function groups_admin_groups_bulk_remove 159 | 160 | /** 161 | * Handle remove form submission. 162 | * 163 | * @return array of deleted groups' ids 164 | */ 165 | function groups_admin_groups_bulk_remove_submit() { 166 | 167 | $result = array(); 168 | if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { 169 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 170 | } 171 | 172 | if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_ACTION_NONCE], 'admin' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 173 | wp_die( esc_html__( 'Access denied.', 'groups' ) ); 174 | } 175 | 176 | $group_ids = isset( $_POST['group_ids'] ) ? $_POST['group_ids'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 177 | if ( $group_ids !== null && is_array( $group_ids ) ) { 178 | foreach ( $group_ids as $group_id ) { 179 | $group = Groups_Group::read( $group_id ); 180 | if ( $group ) { 181 | if ( $group->name !== Groups_Registered::REGISTERED_GROUP_NAME ) { 182 | if ( Groups_Group::delete( $group_id ) ) { 183 | $result[] = $group->group_id; 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | return $result; 191 | } // function groups_admin_groups_bulk_remove_submit 192 | -------------------------------------------------------------------------------- /lib/admin/groups-admin-tree-view.php: -------------------------------------------------------------------------------- 1 | '; 38 | $output .= '

'; 39 | $output .= esc_html__( 'Tree of Groups', 'groups' ); 40 | $output .= '

'; 41 | 42 | $tree = Groups_Utility::get_group_tree(); 43 | $tree_output = ''; 44 | $linked = Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ); 45 | Groups_Utility::render_group_tree( $tree, $tree_output, $linked ); 46 | $output .= $tree_output; 47 | 48 | $output .= ''; // .groups-tree-view 49 | 50 | echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 51 | } // function groups_admin_tree_view() 52 | -------------------------------------------------------------------------------- /lib/auto/class-groups-registered.php: -------------------------------------------------------------------------------- 1 | self::REGISTERED_GROUP_NAME ) ); 48 | } else { 49 | $group_id = $group->group_id; 50 | } 51 | if ( $group_id ) { 52 | $user_group_table = _groups_get_tablename( 'user_group' ); 53 | $query = $wpdb->prepare( 54 | // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 55 | "INSERT IGNORE INTO $user_group_table " . 56 | "SELECT ID, %d FROM $wpdb->users", 57 | Groups_Utility::id( $group_id ) 58 | ); 59 | $rows = $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 60 | } 61 | } 62 | 63 | /** 64 | * Initialize hooks that handle addition and removal of users and blogs. 65 | */ 66 | public static function init() { 67 | 68 | // @since 3.3.1 69 | add_action( 'init', array( __CLASS__, 'wp_init' ) ); 70 | 71 | // When a blog is added, create a new "Registered" group for that blog. 72 | add_action( 'wpmu_new_blog', array( __CLASS__, 'wpmu_new_blog' ), 10, 2 ); 73 | 74 | // Remove group when a blog is deleted? When a blog is deleted, 75 | // Groups_Controller::delete_blog() takes appropriate action. 76 | 77 | // When a user is added, add it to the "Registered" group. 78 | add_action( 'user_register', array( __CLASS__, 'user_register' ) ); 79 | 80 | // Note : When a user is deleted this is handled from core. 81 | 82 | // When a user is added to a blog, add it to the blog's "Registered" group. 83 | add_action( 'add_user_to_blog', array( __CLASS__, 'add_user_to_blog' ), 10, 3 ); 84 | 85 | // Note : When a user is removed from a blog it's handled from core. 86 | } 87 | 88 | /** 89 | * Hooked on the init action. 90 | * 91 | * @since 3.3.1 92 | */ 93 | public static function wp_init() { 94 | // For translation of the "Registered" group(s) 95 | // @since 3.3.1 postponed to after the init action fired 96 | __( 'Registered', 'groups' ); 97 | } 98 | 99 | /** 100 | * Create "Registered" group for new blog and add its admin user. 101 | * 102 | * @see Groups_Controller::wpmu_new_blog() 103 | * 104 | * @param int $blog_id 105 | * @param int $user_id blog's admin user's id 106 | * @param string $domain (optional) 107 | * @param string $path (optional) 108 | * @param int $site_id (optional) 109 | * @param array $meta (optional) 110 | */ 111 | public static function wpmu_new_blog( $blog_id, $user_id, $domain = null, $path = null, $site_id = null, $meta = null ) { 112 | if ( is_multisite() ) { 113 | Groups_Controller::switch_to_blog( $blog_id ); 114 | } 115 | if ( !( $group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ) ) ) { 116 | $group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) ); 117 | } else { 118 | $group_id = $group->group_id; 119 | } 120 | // add the blog's admin user to the group 121 | if ( $group_id ) { 122 | // Do NOT use Groups_User::user_is_member( ... ) here as we do not allow the result of this to be filtered: 123 | if ( !Groups_User_Group::read( $user_id, $group_id ) ) { 124 | Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $group_id ) ); 125 | } 126 | } 127 | if ( is_multisite() ) { 128 | Groups_Controller::restore_current_blog(); 129 | } 130 | } 131 | 132 | /** 133 | * Assign a newly created user to its "Registered" group. 134 | * 135 | * @param int $user_id 136 | */ 137 | public static function user_register( $user_id ) { 138 | 139 | $registered_group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ); 140 | if ( !$registered_group ) { 141 | $registered_group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) ); 142 | } else { 143 | $registered_group_id = $registered_group->group_id; 144 | } 145 | if ( $registered_group_id ) { 146 | // Multisite: If a new user registers with the main blog, 147 | // the user is added, but it doesn't appear on the Users admin 148 | // screen of the main blog. It doesn't have the Subscriber role 149 | // (or any other) for that blog, unless it is explicitly added by the 150 | // blog's admin to the site. In other words, a user that has just 151 | // registered with the site's main blog can access the profile page 152 | // on the back end, but doesn't appear as a user to the site's admin. 153 | // Currently, on WP 3.3.2, like it or not, it's like that. 154 | // Unless the user actually has a capability (role) 155 | // for a blog, it won't appear in the blog's users list. After 156 | // registering with the blog, the user does not have a capability. 157 | // Thus, we need to check that is_user_member_of_blog( $user_id ) here. 158 | if ( !is_multisite() || is_user_member_of_blog( $user_id ) ) { 159 | Groups_User_Group::create( 160 | array( 161 | 'user_id' => $user_id, 162 | 'group_id' => $registered_group_id 163 | ) 164 | ); 165 | } 166 | } 167 | } 168 | 169 | /** 170 | * Assign a user to its "Registered" group for the given blog. 171 | * 172 | * @param int $user_id User ID. 173 | * @param string $role User role. 174 | * @param int $blog_id Blog ID. 175 | */ 176 | public static function add_user_to_blog( $user_id, $role, $blog_id ) { 177 | 178 | if ( is_multisite() ) { 179 | Groups_Controller::switch_to_blog( $blog_id ); 180 | } 181 | 182 | global $wpdb; 183 | 184 | // Check if the group table exists, if it does not exist, we are 185 | // probably here because the action has been triggered in the middle 186 | // of wpmu_create_blog() before the wpmu_new_blog action has been 187 | // triggered. In that case, just skip this as the user will be added 188 | // later when wpmu_new_blog is triggered, the activation sequence has 189 | // created the tables and all users of the new blog are added to 190 | // that blog's "Registered" group. 191 | $group_table = _groups_get_tablename( 'group' ); 192 | if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $group_table . "'" ) == $group_table ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 193 | $registered_group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ); 194 | if ( !$registered_group ) { 195 | $registered_group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) ); 196 | } else { 197 | $registered_group_id = $registered_group->group_id; 198 | } 199 | if ( $registered_group_id ) { 200 | Groups_User_Group::create( 201 | array( 202 | 'user_id' => $user_id, 203 | 'group_id' => $registered_group_id 204 | ) 205 | ); 206 | } 207 | } 208 | 209 | if ( is_multisite() ) { 210 | Groups_Controller::restore_current_blog(); 211 | } 212 | } 213 | 214 | } 215 | Groups_Registered::init(); 216 | -------------------------------------------------------------------------------- /lib/blocks/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | # package.json 3 | package-lock.json 4 | # src/common.scss 5 | # blocks.style.build.css 6 | 7 | ## Uncomment line below if you prefer to 8 | ## keep compiled files out of version control 9 | # dist/ 10 | -------------------------------------------------------------------------------- /lib/blocks/build/index.asset.php: -------------------------------------------------------------------------------- 1 | array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-i18n'), 'version' => '638078029bd27e96b93f'); 2 | -------------------------------------------------------------------------------- /lib/blocks/build/index.css: -------------------------------------------------------------------------------- 1 | .groups-inspector__control{width:100%}.groups-inspector__control>div>div{margin-top:0}.wp-block-groups-groups-member,.wp-block-groups-groups-member__selected{border:1px dashed rgba(51,51,51,.2);border-radius:2px;box-sizing:border-box;margin:-1px;padding:0}.wp-block-groups-groups-member:before,.wp-block-groups-groups-member__selected:before{content:url("data:image/svg+xml;utf8,");display:block;font-size:20px;height:24px;opacity:.3;position:absolute;right:4px;top:4px;width:24px}body.rtl .wp-block-groups-groups-member:before{left:4px;right:unset}.wp-block-groups-groups-member__inner-block{padding-left:5px} 2 | .groups-inspector__control{width:100%}.groups-inspector__control>div>div{margin-top:0}.wp-block-groups-groups-non-member,.wp-block-groups-groups-non-member__selected{border:1px dashed rgba(51,51,51,.2);border-radius:2px;box-sizing:border-box;margin:-1px;padding:0}.wp-block-groups-groups-non-member:before,.wp-block-groups-groups-non-member__selected:before{content:url("data:image/svg+xml;utf8,");display:block;font-size:20px;height:24px;opacity:.3;position:absolute;right:4px;top:4px;width:24px}body.rtl .wp-block-groups-groups-non-member:before{left:4px;right:unset}.wp-block-groups-groups-non-member__inner-block{padding-left:5px} 3 | -------------------------------------------------------------------------------- /lib/blocks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "groups-blocks", 3 | "version": "3.2.1", 4 | "description": "Groups Blocks", 5 | "main": "build/index.js", 6 | "author": "itthinx", 7 | "license": "GPL-3.0", 8 | "devDependencies": { 9 | "@wordpress/scripts": "^25.1.0" 10 | }, 11 | "dependencies": { 12 | "@wordpress/data": "^8.1.0", 13 | "classnames": "^2.3.1", 14 | "react-select": "^5.0.0" 15 | }, 16 | "peerDependencies": { 17 | "react" : "^18.0.0" 18 | }, 19 | "scripts": { 20 | "start": "wp-scripts start", 21 | "build": "wp-scripts build", 22 | "test": "echo \"Error: no test specified\" && exit 1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/blocks/src/blocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * blocks.js 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Denitsa Slavcheva 17 | * @author itthinx 18 | * @package groups 19 | * @since groups 2.8.0 20 | */ 21 | 22 | import './blocks/groups-member/block.js'; 23 | import './blocks/groups-non-member/block.js'; 24 | 25 | // Change the 'groups' category icon in the block editor. 26 | wp.blocks.updateCategory( 27 | 'groups', 28 | { 29 | icon : wp.element.createElement( 30 | 'svg', 31 | { 32 | width : 20, 33 | height : 20, 34 | viewBox: '0 0 20 20', 35 | fill: '#7392cf' 36 | }, 37 | wp.element.createElement( 38 | 'path', 39 | { 40 | d:"M 5.14,2.85 C 5.11,2.85 5.09,2.87 5.09,2.90 5.09,2.93 5.11,2.96 5.14,2.96 5.17,2.96 5.19,2.93 5.19,2.90 5.19,2.87 5.17,2.85 5.14,2.85 Z M 1.53,1.64 C 1.49,1.64 1.45,1.68 1.45,1.72 1.45,1.76 1.49,1.80 1.53,1.80 1.58,1.80 1.61,1.76 1.61,1.72 1.61,1.68 1.58,1.64 1.53,1.64 Z M 4.17,1.24 C 4.09,1.24 4.03,1.30 4.03,1.37 4.03,1.44 4.09,1.50 4.17,1.50 4.24,1.50 4.30,1.44 4.30,1.37 4.30,1.30 4.24,1.24 4.17,1.24 Z M 16.67,16.32 C 16.55,16.32 16.45,16.41 16.45,16.53 16.45,16.65 16.55,16.74 16.67,16.74 16.79,16.74 16.88,16.65 16.88,16.53 16.88,16.41 16.79,16.32 16.67,16.32 Z M 3.97,4.68 C 3.78,4.68 3.63,4.83 3.63,5.02 3.63,5.21 3.78,5.37 3.97,5.37 4.16,5.37 4.32,5.21 4.32,5.02 4.32,4.83 4.16,4.68 3.97,4.68 Z M 13.28,8.84 C 12.97,8.84 12.72,9.09 12.72,9.40 12.72,9.71 12.97,9.96 13.28,9.96 13.59,9.96 13.84,9.71 13.84,9.40 13.84,9.09 13.59,8.84 13.28,8.84 Z M 16.63,11.50 C 16.13,11.50 15.73,11.90 15.73,12.40 15.73,12.90 16.13,13.30 16.63,13.30 17.13,13.30 17.53,12.90 17.53,12.40 17.53,11.90 17.13,11.50 16.63,11.50 Z M 8.57,1.55 C 7.26,1.55 6.21,2.60 6.21,3.91 6.21,5.21 7.26,6.27 8.57,6.27 9.87,6.27 10.93,5.21 10.93,3.91 10.93,2.60 9.87,1.55 8.57,1.55 Z M 15.30,1.07 C 13.19,1.07 11.48,2.78 11.48,4.89 11.48,7.00 13.19,8.71 15.30,8.71 17.41,8.71 19.12,7.00 19.12,4.89 19.12,2.78 17.41,1.07 15.30,1.07 Z M 7.25,6.76 C 3.84,6.76 1.07,9.52 1.07,12.94 1.07,16.35 3.84,19.12 7.25,19.12 10.66,19.12 13.43,16.35 13.43,12.94 13.43,9.52 10.66,6.76 7.25,6.76 Z" 41 | } 42 | ) 43 | ) 44 | } 45 | ); 46 | -------------------------------------------------------------------------------- /lib/blocks/src/blocks/groups-member/block.js: -------------------------------------------------------------------------------- 1 | /** 2 | * groups-member/block.js 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Denitsa Slavcheva 17 | * @author itthinx 18 | * @package groups 19 | * @since groups 2.8.0 20 | */ 21 | 22 | import Select from 'react-select'; 23 | import classnames from 'classnames'; 24 | import { registerStore, withSelect } from '@wordpress/data'; 25 | import { registerBlockType } from '@wordpress/blocks'; 26 | import { __, _x, _n, sprintf } from '@wordpress/i18n'; 27 | import { InspectorControls, InnerBlocks } from '@wordpress/block-editor'; 28 | import { PanelBody, PanelRow, Spinner } from '@wordpress/components'; 29 | import apiFetch from '@wordpress/api-fetch'; 30 | import { store } from '../store'; 31 | 32 | // Import CSS. 33 | import './editor.scss'; 34 | 35 | /** 36 | * Icon for the Groups Member block. 37 | */ 38 | const memberIcon = wp.element.createElement( 39 | 'svg', 40 | { 41 | width: 24, 42 | height: 24, 43 | viewBox: '0 0 24 24' 44 | }, 45 | wp.element.createElement( 46 | 'path', 47 | { 48 | d: "M 14.42,9.58 C 13.75,8.91 12.95,8.57 12.00,8.57 11.05,8.57 10.25,8.91 9.58,9.58 8.91,10.25 8.57,11.05 8.57,12.00 8.57,12.95 8.91,13.75 9.58,14.42 10.25,15.09 11.05,15.43 12.00,15.43 12.95,15.43 13.75,15.09 14.42,14.42 15.09,13.75 15.43,12.95 15.43,12.00 15.43,11.05 15.09,10.25 14.42,9.58 Z M 15.66,5.69 C 16.77,6.34 17.66,7.23 18.31,8.34 18.96,9.46 19.29,10.68 19.29,12.00 19.29,13.32 18.96,14.54 18.31,15.66 17.66,16.77 16.77,17.66 15.66,18.31 14.54,18.96 13.32,19.29 12.00,19.29 10.68,19.29 9.46,18.96 8.34,18.31 7.23,17.66 6.34,16.77 5.69,15.66 5.04,14.54 4.71,13.32 4.71,12.00 4.71,10.68 5.04,9.46 5.69,8.34 6.34,7.23 7.23,6.34 8.34,5.69 9.46,5.04 10.68,4.71 12.00,4.71 13.32,4.71 14.54,5.04 15.66,5.69 Z M 20.91,6.84 C 19.99,5.26 18.74,4.01 17.16,3.09 15.59,2.17 13.87,1.71 12.00,1.71 10.13,1.71 8.41,2.17 6.84,3.09 5.26,4.01 4.01,5.26 3.09,6.84 2.17,8.41 1.71,10.13 1.71,12.00 1.71,13.87 2.17,15.59 3.09,17.16 4.01,18.74 5.26,19.99 6.84,20.91 8.41,21.83 10.13,22.29 12.00,22.29 13.87,22.29 15.59,21.83 17.16,20.91 18.74,19.99 19.99,18.74 20.91,17.16 21.83,15.59 22.29,13.87 22.29,12.00 22.29,10.13 21.83,8.41 20.91,6.84 Z " 49 | } 50 | ) 51 | ); 52 | 53 | /** 54 | * Register: Groups Member Gutenberg Block. 55 | * 56 | * Registers a new block provided a unique name and an object defining its 57 | * behavior. Once registered, the block is made editor as an option to any 58 | * editor interface where blocks are implemented. 59 | * 60 | * @link https://wordpress.org/gutenberg/handbook/block-api/ 61 | * @param {string} name Block name. 62 | * @param {Object} settings Block settings. 63 | * @return {?WPBlock} The block, if it has been successfully 64 | * registered; otherwise `undefined`. 65 | */ 66 | registerBlockType( 67 | // Block name. 68 | // Block names must be string that contains a namespace prefix. 69 | // Example: my-plugin/my-custom-block. 70 | 'groups/groups-member', 71 | { 72 | title: __( 'Groups Member', 'groups' ), // Block title. 73 | description: __( 'Show content for group members', 'groups' ), 74 | icon: memberIcon, // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/. 75 | category: 'groups', // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed. 76 | keywords: [ __( 'groups', 'groups' ), __( 'access', 'groups' ), __( 'members', 'groups' ) ], 77 | attributes: { 78 | groups_select: { 79 | type: 'string', 80 | default: null 81 | }, 82 | }, 83 | 84 | /** 85 | * The edit function describes the structure of the block in the context of the editor. 86 | * This represents what the editor will render when the block is used. 87 | * 88 | * The "edit" property must be a valid function. 89 | * 90 | * Use withSelect to inject state-derived props into a component. 91 | * 92 | * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/ 93 | */ 94 | edit: withSelect( 95 | ( select ) => { 96 | return { 97 | // Uses select() to return an object of the store's selectors. Pre-bound to pass the current state automatically. 98 | groups: select( 'groups/groups-blocks' ).receiveGroups(), 99 | }; 100 | } 101 | ) 102 | 103 | ( 104 | props => { 105 | 106 | const { 107 | attributes: { groups_select }, 108 | groups, 109 | className, 110 | setAttributes, 111 | isSelected 112 | } = props; 113 | 114 | const handleGroupsChange = ( groups_select ) => setAttributes( 115 | { groups_select: JSON.stringify( groups_select ) } 116 | ); 117 | 118 | let selectedGroups = []; 119 | 120 | if ( null !== groups_select ) { 121 | selectedGroups = JSON.parse( groups_select ); 122 | } 123 | 124 | // Show if the data is not loaded yet. 125 | if ( ! groups.length ) { 126 | return ( 127 |

128 | 129 | { __( 'Loading...', 'groups' ) } 130 |

131 | ); 132 | } 133 | 134 | return [ 135 | 136 | 137 | 138 | 141 | 142 | 143 | 152 | 153 | 154 | , 155 |
156 |
157 | 158 |
159 |
160 | ]; 161 | } 162 | ), 163 | 164 | /** 165 | * The save function defines the way in which the different attributes should be combined 166 | * into the final markup, which is then serialized by Gutenberg into post_content. 167 | * 168 | * The "save" property must be specified and must be a valid function. 169 | * 170 | * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/ 171 | */ 172 | save: props => { 173 | return ( 174 |
175 | 176 |
177 | ); 178 | }, 179 | } 180 | ); 181 | -------------------------------------------------------------------------------- /lib/blocks/src/blocks/groups-non-member/editor.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * groups-non-member/editor.scss 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Denitsa Slavcheva 17 | * @package groups 18 | * @since groups 2.8.0 19 | */ 20 | 21 | .groups-inspector__control { 22 | width: 100%; 23 | 24 | > div > div { 25 | margin-top: 0px; 26 | } 27 | } 28 | 29 | .wp-block-groups-groups-non-member, 30 | .wp-block-groups-groups-non-member__selected { 31 | border: 1px dashed #33333333; 32 | border-radius: 2px; 33 | padding: 0; 34 | box-sizing: border-box; 35 | margin: -1px; 36 | 37 | &:before { 38 | content: url("data:image/svg+xml;utf8,"); 39 | display: block; 40 | position: absolute; 41 | top: 4px; 42 | right: 4px; 43 | opacity: 0.3; 44 | width: 24px; 45 | height: 24px; 46 | font-size: 20px; 47 | } 48 | } 49 | 50 | body.rtl .wp-block-groups-groups-non-member::before { 51 | left: 4px; 52 | right: unset; 53 | } 54 | 55 | .wp-block-groups-groups-non-member__inner-block { 56 | padding-left: 5px; 57 | } 58 | -------------------------------------------------------------------------------- /lib/blocks/src/blocks/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * store.js 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Karim Rahimpur 17 | * @author itthinx 18 | * @package groups 19 | * @since groups 3.2.1 20 | */ 21 | 22 | import { registerStore, withSelect } from '@wordpress/data'; 23 | import apiFetch from '@wordpress/api-fetch'; 24 | 25 | /** 26 | * Default state - no groups selected. 27 | */ 28 | const DEFAULT_STATE = { 29 | groups: {}, 30 | }; 31 | 32 | /** 33 | * Actions object. 34 | * Actions are payloads of information that send data from the application to the store. 35 | * Plain JavaScript objects. 36 | */ 37 | const actions = { 38 | // Action creator for the action called when settng a group by choosing from the options list. 39 | setGroups( groups ) { 40 | return { 41 | type: 'SET_GROUPS', 42 | groups, 43 | }; 44 | }, 45 | 46 | receiveGroups( path ) { 47 | return { 48 | type: 'RECEIVE_GROUPS', 49 | path, 50 | }; 51 | }, 52 | }; 53 | 54 | /** 55 | * Store 56 | */ 57 | export const store = registerStore( 58 | 'groups/groups-blocks', 59 | { 60 | // The reducer is a pure function that takes the previous state and an action, and returns the next state. 61 | reducer( state = DEFAULT_STATE, action ) { 62 | switch ( action.type ) { 63 | case 'SET_GROUPS': 64 | return { 65 | // To keep the reducer pure, state should not be mutated. 66 | // Use object state operator to copy enumerabale properties into a new object instead of Object.assign(). 67 | ...state, 68 | groups: action.groups, 69 | }; 70 | } 71 | // Return the previous state - for when there's an unknown action. 72 | return state; 73 | }, 74 | 75 | actions, 76 | // A selector accepts state and optional arguments and returns some value from state. 77 | selectors: { 78 | receiveGroups( state ) { 79 | const { groups } = state; 80 | return groups; 81 | }, 82 | }, 83 | // Defines the execution flow behavior associated with a specific action type. 84 | controls: { 85 | RECEIVE_GROUPS( action ) { 86 | return apiFetch( { path: action.path } ); 87 | }, 88 | }, 89 | // Side-effects for a selector. Used with data from an extrnal source. 90 | resolvers: { 91 | * receiveGroups( state ) { 92 | const groups = yield actions.receiveGroups( '/groups/groups-blocks/groups/' ); 93 | return actions.setGroups( groups ); 94 | }, 95 | }, 96 | } 97 | ); 98 | -------------------------------------------------------------------------------- /lib/blocks/src/class-groups-blocks.php: -------------------------------------------------------------------------------- 1 | 'GET', 60 | 'callback' => array( __CLASS__, 'get_groups' ), 61 | // Restrict access for the endpoint only to users that can administrate groups restrictions. 62 | 'permission_callback' => function () { 63 | return Groups_Access_Meta_Boxes::user_can_restrict(); // nosemgrep: scanner.php.wp.security.rest-route.permission-callback.incorrect-return 64 | }, 65 | ), 66 | ) 67 | ); 68 | } 69 | 70 | /** 71 | * Callback function to retrieve existing groups. 72 | * 73 | * @return array 74 | */ 75 | public static function get_groups() { 76 | $groups_options = array(); 77 | if ( Groups_Access_Meta_Boxes::user_can_restrict() ) { 78 | $include = Groups_Access_Meta_Boxes::get_user_can_restrict_group_ids(); 79 | $groups = Groups_Group::get_groups( 80 | array( 81 | 'order_by' => 'name', 82 | 'order' => 'ASC', 83 | 'include' => $include, 84 | ) 85 | ); 86 | foreach ( $groups as $group ) { 87 | $groups_options[] = array( 88 | 'value' => $group->group_id, 89 | 'label' => $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '', 90 | ); 91 | } 92 | } else { 93 | $groups_options = esc_html__( 'You cannot set any access restrictions.', 'groups' ); 94 | } 95 | return $groups_options; 96 | } 97 | 98 | /** 99 | * Register the Groups block category. 100 | * 101 | * @since 2.14.0 102 | * 103 | * @param array $block_categories 104 | * @param WP_Block_Editor_Context $block_editor_context 105 | * 106 | * @return array 107 | */ 108 | public static function block_categories_all( $block_categories, $block_editor_context ) { 109 | $block_categories = array_merge( 110 | $block_categories, 111 | array( 112 | array( 113 | 'slug' => 'groups', 114 | 'title' => 'Groups' // do NOT translate 115 | ) 116 | ) 117 | ); 118 | return $block_categories; 119 | } 120 | 121 | /** 122 | * Adds a new block category for 'groups' in the block editor. 123 | * 124 | * @deprecated as of 2.14.0 with WordPress 5.8.0 using the block_categories_all filter instead 125 | * 126 | * @param array $categories Array of block categories. 127 | * @param WP_Post $post Post being loaded. 128 | * 129 | * @return array 130 | */ 131 | public static function groups_block_categories( $categories, $post ) { 132 | $categories = array_merge( 133 | $categories, 134 | array( 135 | array( 136 | 'slug' => 'groups', 137 | 'title' => 'Groups', 138 | ), 139 | ) 140 | ); 141 | return $categories; 142 | } 143 | 144 | /** 145 | * Registers our blocks. 146 | */ 147 | public static function groups_blocks_block_init() { 148 | // Skip block registration if Gutenberg is not enabled/merged. 149 | if ( ! function_exists( 'register_block_type' ) ) { 150 | return; 151 | } 152 | 153 | $asset_file = include GROUPS_BLOCKS_LIB . '/build/index.asset.php'; 154 | 155 | $editor_dependencies = array_merge( 156 | $asset_file['dependencies'], 157 | array() 158 | ); 159 | 160 | // @todo if 'wp-edit-widgets' or 'wp-customize-widgets' script then don't use wp-editor ... so ? 161 | // Scripts. 162 | wp_register_script( 163 | 'groups_blocks-block-js', // Handle. 164 | GROUPS_PLUGIN_URL . 'lib/blocks/build/index.js', 165 | $editor_dependencies, 166 | GROUPS_CORE_VERSION 167 | ); 168 | 169 | wp_set_script_translations( 170 | 'groups_blocks-block-js', 171 | 'groups' 172 | ); 173 | 174 | // Frontend Styles - currently none required. 175 | // wp_register_style( 176 | // 'groups_blocks-style-css', // Handle. 177 | // GROUPS_PLUGIN_URL . 'lib/blocks/dist/blocks.style.build.css', 178 | // array(), // Dependency to include the CSS after it. 179 | // GROUPS_CORE_VERSION 180 | // ); 181 | 182 | // Editor Styles. 183 | wp_register_style( 184 | 'groups_blocks-block-editor-css', // Handle. 185 | GROUPS_PLUGIN_URL . 'lib/blocks/build/index.css', 186 | array( 'wp-edit-blocks' ), // Dependency to include the CSS after it. 187 | GROUPS_CORE_VERSION 188 | ); 189 | register_block_type( 190 | 'groups/groups-member', 191 | array( 192 | 'editor_script' => 'groups_blocks-block-js', 193 | 'editor_style' => 'groups_blocks-block-editor-css', 194 | 'style' => 'groups_blocks-style-css', 195 | 'render_callback' => array( __CLASS__, 'groups_member_render_content' ), 196 | ) 197 | ); 198 | register_block_type( 199 | 'groups/groups-non-member', 200 | array( 201 | 'editor_script' => 'groups_blocks-block-js', 202 | 'editor_style' => 'groups_blocks-block-editor-css', 203 | 'style' => 'groups_blocks-style-css', 204 | 'render_callback' => array( __CLASS__, 'groups_non_member_render_content' ), 205 | ) 206 | ); 207 | } 208 | 209 | /** 210 | * Rendering callback for our Groups Member block. 211 | * 212 | * @param array $attributes 213 | * @param string $content 214 | * 215 | * @return string 216 | */ 217 | public static function groups_member_render_content( $attributes, $content ) { 218 | 219 | $output = ''; 220 | $show_content = false; 221 | $selected_groups = array(); 222 | 223 | if ( isset( $attributes['groups_select'] ) ) { 224 | $decoded_groups = json_decode( $attributes['groups_select'] ); 225 | if ( ! empty( $decoded_groups ) ) { 226 | foreach ( $decoded_groups as $group ) { 227 | $selected_groups[] = $group->value; 228 | } 229 | } 230 | } 231 | 232 | $groups_user = new Groups_User( get_current_user_id() ); 233 | foreach ( $selected_groups as $group ) { 234 | $current_group = Groups_Group::read( $group ); 235 | if ( ! $current_group ) { 236 | $current_group = Groups_Group::read_by_name( $group ); 237 | } 238 | 239 | if ( $current_group ) { 240 | if ( $groups_user->is_member( $current_group->group_id ) ) { 241 | $show_content = true; 242 | break; 243 | } 244 | } 245 | } 246 | 247 | if ( $show_content ) { 248 | $output = '
' . $content . '
'; 249 | } 250 | 251 | return $output; 252 | } 253 | 254 | /** 255 | * Rendering callback for our Groups Non-member block. 256 | * 257 | * @param array $attributes 258 | * @param string $content 259 | * 260 | * @return string 261 | */ 262 | public static function groups_non_member_render_content( $attributes, $content ) { 263 | 264 | $output = ''; 265 | $show_content = true; 266 | $selected_groups = array(); 267 | 268 | if ( isset( $attributes['groups_select'] ) ) { 269 | $decoded_groups = json_decode( $attributes['groups_select'] ); 270 | if ( ! empty( $decoded_groups ) ) { 271 | foreach ( $decoded_groups as $group ) { 272 | $selected_groups[] = $group->value; 273 | } 274 | } 275 | } 276 | 277 | $groups_user = new Groups_User( get_current_user_id() ); 278 | foreach ( $selected_groups as $group ) { 279 | $current_group = Groups_Group::read( $group ); 280 | if ( ! $current_group ) { 281 | $current_group = Groups_Group::read_by_name( $group ); 282 | } 283 | 284 | if ( $current_group ) { 285 | if ( $groups_user->is_member( $current_group->group_id ) ) { 286 | $show_content = false; 287 | break; 288 | } 289 | } 290 | } 291 | 292 | if ( $show_content ) { 293 | $output = '
' . $content . '
'; 294 | } 295 | 296 | return $output; 297 | } 298 | 299 | } 300 | 301 | Groups_Blocks::init(); 302 | -------------------------------------------------------------------------------- /lib/blocks/src/common.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * #.# Common SCSS 3 | * 4 | * Can include things like variables and mixins 5 | * that are used across the project. 6 | */ 7 | -------------------------------------------------------------------------------- /lib/blocks/src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * blocks.js 3 | * 4 | * Copyright (c) "kento" Karim Rahimpur www.itthinx.com 5 | * 6 | * This code is released under the GNU General Public License. 7 | * See COPYRIGHT.txt and LICENSE.txt. 8 | * 9 | * This code is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * This header and all notices must be kept intact. 15 | * 16 | * @author Denitsa Slavcheva 17 | * @author itthinx 18 | * @package groups 19 | * @since groups 2.8.0 20 | */ 21 | 22 | // Version 2.14.0 Notes: 23 | // - Updated package.json to use wp-scripts instead of cgb-scripts 24 | // - Packages needed to be added to build: 25 | // $ npm i react-select 26 | // $ npm i classnames 27 | // - Now using standard wp-scripts build/index.js instead of prior dist/blocks.build.js 28 | // - To generate build/index.css which replaces the prior dist/blocks.editor.build.css: 29 | // $ npm run build 30 | // - Using appropriate import statements instead of prior const. 31 | 32 | import './blocks/groups-member/block.js'; 33 | import './blocks/groups-non-member/block.js'; 34 | 35 | // Change the 'groups' category icon in the block editor. 36 | wp.blocks.updateCategory( 37 | 'groups', 38 | { 39 | icon : wp.element.createElement( 40 | 'svg', 41 | { 42 | width : 20, 43 | height : 20, 44 | viewBox: '0 0 20 20', 45 | fill: '#7392cf' 46 | }, 47 | wp.element.createElement( 48 | 'path', 49 | { 50 | d:"M 5.14,2.85 C 5.11,2.85 5.09,2.87 5.09,2.90 5.09,2.93 5.11,2.96 5.14,2.96 5.17,2.96 5.19,2.93 5.19,2.90 5.19,2.87 5.17,2.85 5.14,2.85 Z M 1.53,1.64 C 1.49,1.64 1.45,1.68 1.45,1.72 1.45,1.76 1.49,1.80 1.53,1.80 1.58,1.80 1.61,1.76 1.61,1.72 1.61,1.68 1.58,1.64 1.53,1.64 Z M 4.17,1.24 C 4.09,1.24 4.03,1.30 4.03,1.37 4.03,1.44 4.09,1.50 4.17,1.50 4.24,1.50 4.30,1.44 4.30,1.37 4.30,1.30 4.24,1.24 4.17,1.24 Z M 16.67,16.32 C 16.55,16.32 16.45,16.41 16.45,16.53 16.45,16.65 16.55,16.74 16.67,16.74 16.79,16.74 16.88,16.65 16.88,16.53 16.88,16.41 16.79,16.32 16.67,16.32 Z M 3.97,4.68 C 3.78,4.68 3.63,4.83 3.63,5.02 3.63,5.21 3.78,5.37 3.97,5.37 4.16,5.37 4.32,5.21 4.32,5.02 4.32,4.83 4.16,4.68 3.97,4.68 Z M 13.28,8.84 C 12.97,8.84 12.72,9.09 12.72,9.40 12.72,9.71 12.97,9.96 13.28,9.96 13.59,9.96 13.84,9.71 13.84,9.40 13.84,9.09 13.59,8.84 13.28,8.84 Z M 16.63,11.50 C 16.13,11.50 15.73,11.90 15.73,12.40 15.73,12.90 16.13,13.30 16.63,13.30 17.13,13.30 17.53,12.90 17.53,12.40 17.53,11.90 17.13,11.50 16.63,11.50 Z M 8.57,1.55 C 7.26,1.55 6.21,2.60 6.21,3.91 6.21,5.21 7.26,6.27 8.57,6.27 9.87,6.27 10.93,5.21 10.93,3.91 10.93,2.60 9.87,1.55 8.57,1.55 Z M 15.30,1.07 C 13.19,1.07 11.48,2.78 11.48,4.89 11.48,7.00 13.19,8.71 15.30,8.71 17.41,8.71 19.12,7.00 19.12,4.89 19.12,2.78 17.41,1.07 15.30,1.07 Z M 7.25,6.76 C 3.84,6.76 1.07,9.52 1.07,12.94 1.07,16.35 3.84,19.12 7.25,19.12 10.66,19.12 13.43,16.35 13.43,12.94 13.43,9.52 10.66,6.76 7.25,6.76 Z" 51 | } 52 | ) 53 | ) 54 | } 55 | ); 56 | -------------------------------------------------------------------------------- /lib/core/class-groups-cache-object.php: -------------------------------------------------------------------------------- 1 | key = $key; 54 | $this->value = $value; 55 | } 56 | 57 | /** 58 | * Getter implementation for key and value properties. 59 | * 60 | * @param string $name 61 | * 62 | * @return mixed property value or null 63 | */ 64 | public function __get( $name ) { 65 | $result = null; 66 | switch ( $name ) { 67 | case 'key' : 68 | case 'value' : 69 | $result = $this->$name; 70 | break; 71 | } 72 | return $result; 73 | } 74 | 75 | /** 76 | * Setter for key and value properties. 77 | * 78 | * @param string $name 79 | * @param mixed $value 80 | */ 81 | public function __set( $name, $value ) { 82 | switch( $name ) { 83 | case 'key' : 84 | case 'value' : 85 | $this->$name = $value; 86 | break; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/core/class-groups-cache.php: -------------------------------------------------------------------------------- 1 | exists() ) { 140 | if ( $flags & self::GROUPS_CACHE_GROUP ) { 141 | if ( is_array( $user->roles ) ) { 142 | $roles = $user->roles; 143 | sort( $roles ); 144 | } 145 | } 146 | if ( $flags & self::ROLES_CACHE_GROUP ) { 147 | if ( class_exists( '\Groups_User' ) ) { 148 | $groups_user = new \Groups_User( $user->ID ); 149 | $group_ids = $groups_user->get_group_ids_deep(); 150 | $group_ids = array_map( 'intval', $group_ids ); 151 | sort( $group_ids, SORT_NUMERIC ); 152 | } 153 | } 154 | if ( $flags & self::USER_CACHE_GROUP ) { 155 | $user_id = $user->ID; 156 | } 157 | } 158 | } 159 | 160 | if ( count( $roles ) > 0 ) { 161 | $group .= '_'; 162 | $group .= implode( '_', $roles ); 163 | } 164 | if ( count( $group_ids ) > 0 ) { 165 | $group .= '_'; 166 | $group .= implode( '_', $group_ids ); 167 | } 168 | if ( $user_id !== null ) { 169 | $group .= '_'; 170 | $group .= $user_id; 171 | } 172 | 173 | /** 174 | * Additional specialization if needed by third-party extensions. 175 | * 176 | * @param string $suffix 177 | * @param string $group 178 | * @param int $flags 179 | * 180 | * @var string $group_suffix 181 | */ 182 | $group_suffix = apply_filters( 'groups_cache_group_suffix', '', $group, $flags ); 183 | if ( !is_string( $group_suffix ) ) { 184 | $group_suffix = ''; 185 | } else { 186 | $group_suffix = trim( $group_suffix ); 187 | } 188 | if ( strlen( $group_suffix ) > 0 ) { 189 | $group = $group . '_' . $group_suffix; 190 | } 191 | 192 | return $group; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /lib/core/class-groups-group-capability.php: -------------------------------------------------------------------------------- 1 | get_var( $wpdb->prepare( 65 | "SELECT COUNT(*) FROM $group_capability_table WHERE group_id = %d AND capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 66 | Groups_Utility::id( $group_id ), 67 | Groups_Utility::id( $capability_id ) 68 | ) ) ) ) { 69 | $data = array( 70 | 'group_id' => Groups_Utility::id( $group_id ), 71 | 'capability_id' => Groups_Utility::id( $capability_id ) 72 | ); 73 | $formats = array( '%d', '%d' ); 74 | if ( $wpdb->insert( $group_capability_table, $data, $formats ) ) { 75 | $result = true; 76 | do_action( 'groups_created_group_capability', $group_id, $capability_id ); 77 | } 78 | } 79 | } 80 | } 81 | return $result; 82 | } 83 | 84 | /** 85 | * Retrieve a group-capability relation. 86 | * 87 | * @param int $group_id group's id 88 | * @param int $capability_id capability's id 89 | * 90 | * @return object upon success, otherwise false 91 | */ 92 | public static function read( $group_id, $capability_id ) { 93 | global $wpdb; 94 | $result = false; 95 | 96 | $group_capability_table = _groups_get_tablename( 'group_capability' ); 97 | $group_capability = $wpdb->get_row( $wpdb->prepare( 98 | "SELECT * FROM $group_capability_table WHERE group_id = %d AND capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 99 | Groups_Utility::id( $group_id ), 100 | Groups_Utility::id( $capability_id ) 101 | ) ); 102 | if ( $group_capability !== null ) { 103 | $result = $group_capability; 104 | } 105 | return $result; 106 | } 107 | 108 | /** 109 | * Update group-capability relation. 110 | * 111 | * This changes nothing so as of now it's pointless to even call this. 112 | * 113 | * @param array $map 114 | * 115 | * @return true if successful, false otherwise 116 | */ 117 | public static function update( $map ) { 118 | $result = false; 119 | // @since 3.0.0 do not process until this actually changes anything 120 | if ( false ) { 121 | $group_id = isset( $map['group_id'] ) ? $map['group_id'] : null; 122 | $capability_id = isset( $map['capability_id'] ) ? $map['capability_id'] : null; 123 | if ( !empty( $group_id ) && !empty( $capability_id) ) { 124 | // make sure group and capability exist 125 | if ( Groups_Group::read( $group_id ) && Groups_Capability::read( $capability_id ) ) { 126 | $result = true; 127 | do_action( 'groups_updated_group_capability', $group_id, $capability_id ); 128 | } 129 | } 130 | } 131 | return $result; 132 | } 133 | 134 | /** 135 | * Remove group-capability relation. 136 | * 137 | * @param int $group_id 138 | * @param int $capability_id 139 | * 140 | * @return true if successful, false otherwise 141 | */ 142 | public static function delete( $group_id, $capability_id ) { 143 | 144 | global $wpdb; 145 | $result = false; 146 | 147 | // avoid nonsense requests 148 | if ( !empty( $group_id ) && !empty( $capability_id) ) { 149 | // we can omit checking if the group and capability exist, to 150 | // allow resolving the relationship after they have been deleted 151 | $group_capability_table = _groups_get_tablename( 'group_capability' ); 152 | // get rid of it 153 | $rows = $wpdb->query( $wpdb->prepare( 154 | "DELETE FROM $group_capability_table WHERE group_id = %d AND capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 155 | Groups_Utility::id( $group_id ), 156 | Groups_Utility::id( $capability_id ) 157 | ) ); 158 | // must have affected a row, otherwise no great success 159 | $result = ( $rows !== false ) && ( $rows > 0 ); 160 | if ( $result ) { 161 | do_action( 'groups_deleted_group_capability', $group_id, $capability_id ); 162 | } 163 | } 164 | return $result; 165 | } 166 | } 167 | Groups_Group_Capability::init(); 168 | -------------------------------------------------------------------------------- /lib/core/class-groups-help.php: -------------------------------------------------------------------------------- 1 | base; 65 | // The prefix of the $screen_id is translated, use only the suffix 66 | // to identify a screen: 67 | $ids = array( 68 | 'groups-admin' => _x( 'Groups', 'Help tab title and heading', 'groups' ), 69 | 'groups-admin-groups' => _x( 'Groups', 'Help tab title and heading', 'groups' ), 70 | 'groups-admin-options' => _x( 'Options', 'Help tab title and heading', 'groups' ), 71 | 'groups-admin-capabilities' => _x( 'Capabilities', 'Help tab title and heading', 'groups' ), 72 | ); 73 | foreach ( $ids as $id => $title ) { 74 | $i = strpos( $screen_id, $id ); 75 | if ( $i !== false ) { 76 | if ( $i + strlen( $id ) == strlen( $screen_id ) ) { 77 | $screen_id = $id; 78 | $show_groups_help = true; 79 | $help_title = $title; 80 | break; 81 | } 82 | } 83 | } 84 | if ( $show_groups_help ) { 85 | $help = '

'. $help_title .'

'; 86 | $help .= '

'; 87 | $help .= __( 'The complete documentation is available on the Documentation pages for Groups.', 'groups' ); 88 | $help .= '

'; 89 | switch ( $screen_id ) { 90 | case 'groups-admin' : 91 | case 'groups-admin-groups': 92 | $help .= '

' . __( 'Here you can add, edit and remove groups.', 'groups' ) . '

'; 93 | break; 94 | case 'groups-admin-options' : 95 | case 'groups-admin-capabilities' : 96 | break; 97 | } 98 | 99 | $screen->add_help_tab( 100 | array( 101 | 'id' => $screen_id, 102 | 'title' => $help_title, 103 | 'content' => $help 104 | ) 105 | ); 106 | } 107 | } 108 | } 109 | 110 | /** 111 | * Provides the footer text for Groups on relevant screens. 112 | * 113 | * @param string $footer_text 114 | * @return mixed 115 | */ 116 | public static function admin_footer_text( $text ) { 117 | if ( function_exists( 'get_current_screen' ) ) { 118 | $current_screen = get_current_screen(); 119 | if ( 120 | isset( $current_screen->id ) && 121 | ( 122 | stripos( $current_screen->id, 'groups-' ) === 0 || 123 | stripos( $current_screen->id, 'groups_' ) === 0 || 124 | stripos( $current_screen->id, 'toplevel_page_groups' ) === 0 125 | ) 126 | ) { 127 | $text = self::footer( false ); 128 | } 129 | } 130 | return $text; 131 | } 132 | 133 | /** 134 | * Returns or renders the footer. 135 | * 136 | * @param boolean $render 137 | */ 138 | public static function footer( $render = true ) { 139 | $footer = 140 | '' . 141 | __( 'Thank you for using Groups by itthinx.', 'groups' ) . 142 | ' ' . 143 | sprintf( 144 | /* translators: link */ 145 | __( 'Please give it a ★★★★★ rating.', 'groups' ), 146 | esc_attr( 'https://wordpress.org/support/view/plugin-reviews/groups?filter=5#postform' ) 147 | ) . 148 | ''; 149 | $footer = apply_filters( 'groups_footer', $footer ); 150 | if ( $render ) { 151 | echo $footer; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 152 | } else { 153 | return $footer; 154 | } 155 | } 156 | } 157 | Groups_Help::init(); 158 | -------------------------------------------------------------------------------- /lib/core/class-groups-options.php: -------------------------------------------------------------------------------- 1 | array() ); 52 | add_option( self::option_key, $options, '', 'no' ); 53 | } 54 | } 55 | 56 | /** 57 | * Returns the current Groups options and initializes them 58 | * through init() if needed. 59 | * @return array Groups options 60 | */ 61 | private static function get_options() { 62 | $options = get_option( self::option_key ); 63 | if ( $options === false ) { 64 | self::init(); 65 | $options = get_option( self::option_key ); 66 | } 67 | return $options; 68 | } 69 | 70 | /** 71 | * Returns the value of a general setting. 72 | * 73 | * @param string $option the option id 74 | * @param mixed $default default value to retrieve if option is not set 75 | * 76 | * @return mixed option value, $default if set or null 77 | */ 78 | public static function get_option( $option, $default = null ) { 79 | $options = self::get_options(); 80 | $value = isset( $options[self::general][$option] ) ? $options[self::general][$option] : null; 81 | if ( $value === null ) { 82 | $value = $default; 83 | } 84 | return $value; 85 | } 86 | 87 | 88 | /** 89 | * Returns the value of a user setting. 90 | * 91 | * @param string $option the option id 92 | * @param mixed $default default value to retrieve if option is not set 93 | * @param int $user_id retrieve option for this user, defaults to null for current user 94 | * 95 | * @return mixed option value, $default if set or null 96 | */ 97 | public static function get_user_option( $option, $default = null, $user_id = null ) { 98 | if ( $user_id === null ) { 99 | $current_user = wp_get_current_user(); 100 | if ( !empty( $current_user ) ) { 101 | $user_id = $current_user->ID; 102 | } 103 | } 104 | $value = null; 105 | if ( $user_id !== null ) { 106 | $options = self::get_options(); 107 | $value = isset( $options[$user_id][$option] ) ? $options[$user_id][$option] : null; 108 | } 109 | if ( $value === null ) { 110 | $value = $default; 111 | } 112 | return $value; 113 | } 114 | 115 | /** 116 | * Updates a general setting. 117 | * 118 | * @param string $option the option's id 119 | * @param mixed $new_value the new value 120 | */ 121 | public static function update_option( $option, $new_value ) { 122 | $options = self::get_options(); 123 | $options[self::general][$option] = $new_value; 124 | update_option( self::option_key, $options ); 125 | } 126 | 127 | /** 128 | * Updates a user setting. 129 | * 130 | * @param string $option the option's id 131 | * @param mixed $new_value the new value 132 | * @param int $user_id update option for this user, defaults to null for current user 133 | */ 134 | public static function update_user_option( $option, $new_value, $user_id = null ) { 135 | 136 | if ( $user_id === null ) { 137 | $current_user = wp_get_current_user(); 138 | if ( !empty( $current_user ) ) { 139 | $user_id = $current_user->ID; 140 | } 141 | } 142 | 143 | if ( $user_id !== null ) { 144 | $options = self::get_options(); 145 | $options[$user_id][$option] = $new_value; 146 | update_option( self::option_key, $options ); 147 | } 148 | } 149 | 150 | /** 151 | * Deletes a general setting. 152 | * 153 | * @param string $option the option's id 154 | */ 155 | public static function delete_option( $option ) { 156 | $options = self::get_options(); 157 | if ( isset( $options[self::general][$option] ) ) { 158 | unset( $options[self::general][$option] ); 159 | update_option( self::option_key, $options ); 160 | } 161 | } 162 | 163 | /** 164 | * Deletes a user setting. 165 | * 166 | * @param string $option the option's id 167 | * @param int $user_id delete option for this user, defaults to null for current user 168 | */ 169 | public static function delete_user_option( $option, $user_id = null ) { 170 | 171 | if ( $user_id === null ) { 172 | $current_user = wp_get_current_user(); 173 | if ( !empty( $current_user ) ) { 174 | $user_id = $current_user->ID; 175 | } 176 | } 177 | 178 | if ( $user_id !== null ) { 179 | $options = self::get_options(); 180 | if ( isset( $options[$user_id][$option] ) ) { 181 | unset( $options[$user_id][$option] ); 182 | update_option( self::option_key, $options ); 183 | } 184 | } 185 | } 186 | 187 | /** 188 | * Deletes all settings - this includes user and general options. 189 | */ 190 | public static function flush_options() { 191 | delete_option( self::option_key ); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /lib/core/class-groups-pagination.php: -------------------------------------------------------------------------------- 1 | set_pagination_args( 49 | array( 50 | 'total_items' => $total_items, 51 | 'total_pages' => $total_pages, 52 | 'per_page' => $per_page 53 | ) 54 | ); 55 | } 56 | 57 | /** 58 | * Get the current page number 59 | * 60 | * @return int the current page number 61 | */ 62 | public function get_pagenum() { 63 | $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0; 64 | if ( !isset( $_REQUEST['paged'] ) ) { // needed with rewritten page added 65 | $matches = array(); 66 | if ( preg_match( "/(\/page\/)(\d+)/", $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], $matches ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 67 | $pagenum = absint( $matches[2] ); 68 | } 69 | } 70 | 71 | if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) { 72 | $pagenum = $this->_pagination_args['total_pages']; 73 | } 74 | return max( 1, $pagenum ); 75 | } 76 | 77 | /** 78 | * An internal method that sets all the necessary pagination arguments 79 | * 80 | * @param array $args An associative array with information about the pagination 81 | * 82 | * @access protected 83 | */ 84 | public function set_pagination_args( $args ) { 85 | $args = wp_parse_args( $args, array( 86 | 'total_items' => 0, 87 | 'total_pages' => 0, 88 | 'per_page' => 0, 89 | ) ); 90 | 91 | if ( !$args['total_pages'] && $args['per_page'] > 0 ) 92 | $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] ); 93 | 94 | $this->_pagination_args = $args; 95 | } 96 | 97 | /** 98 | * Returns or displays the pagination. 99 | * 100 | * @param string $which where it's displayed 101 | * @param boolean $echo displays if true, otherwise returns 102 | * 103 | * @return string|null 104 | */ 105 | public function pagination( $which, $echo = false ) { 106 | 107 | if ( empty( $this->_pagination_args ) ) { 108 | return; 109 | } 110 | 111 | $total_items = isset( $this->_pagination_args['total_items'] ) ? $this->_pagination_args['total_items'] : 0; 112 | $total_pages = isset( $this->_pagination_args['total_pages'] ) ? $this->_pagination_args['total_pages'] : 0; 113 | 114 | /* translators: number of items */ 115 | $output = '' . sprintf( _n( '%s item', '%s items', $total_items, 'groups' ), number_format_i18n( $total_items ) ) . ''; 116 | 117 | $current = $this->get_pagenum(); 118 | 119 | $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 120 | 121 | $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url ); 122 | 123 | // needs to remove rewritten added page 124 | $current_url = preg_replace( '/\/page\/\d+/', '', $current_url ); 125 | 126 | $page_links = array(); 127 | 128 | $disable_first = $disable_last = ''; 129 | if ( $current == 1 ) 130 | $disable_first = ' disabled'; 131 | if ( $current == $total_pages ) 132 | $disable_last = ' disabled'; 133 | 134 | $page_links[] = sprintf( '%s', 135 | 'button first-page' . $disable_first, 136 | esc_attr__( 'Go to the first page', 'groups' ), 137 | esc_url( remove_query_arg( 'paged', $current_url ) ), 138 | '«' 139 | ); 140 | 141 | $page_links[] = sprintf( '%s', 142 | 'button prev-page' . $disable_first, 143 | esc_attr__( 'Go to the previous page', 'groups' ), 144 | esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ), 145 | '‹' 146 | ); 147 | 148 | if ( 'bottom' == $which ) 149 | $html_current_page = $current; 150 | else 151 | $html_current_page = sprintf( '', 152 | esc_attr__( 'Current page', 'groups' ), 153 | esc_attr( 'paged' ), 154 | $current, 155 | strlen( $total_pages ) 156 | ); 157 | 158 | $html_total_pages = sprintf( '%s', number_format_i18n( $total_pages ) ); 159 | $page_links[] = '' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . ''; // phpcs:ignore WordPress.WP.I18n.MissingArgDomain, WordPress.WP.I18n.MissingTranslatorsComment 160 | 161 | $page_links[] = sprintf( '%s', 162 | 'button next-page' . $disable_last, 163 | esc_attr__( 'Go to the next page', 'groups' ), 164 | esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ), 165 | '›' 166 | ); 167 | 168 | $page_links[] = sprintf( '%s', 169 | 'button last-page' . $disable_last, 170 | esc_attr__( 'Go to the last page', 'groups' ), 171 | esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), 172 | '»' 173 | ); 174 | 175 | $output .= "\n" . join( "\n", $page_links ); 176 | 177 | $page_class = $total_pages < 2 ? ' one-page' : ''; 178 | 179 | $this->_pagination = "
$output
"; 180 | 181 | if ( $echo ) { 182 | echo $this->_pagination; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 183 | } else { 184 | return $this->_pagination; 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /lib/core/class-groups-user-capability.php: -------------------------------------------------------------------------------- 1 | get_var( $wpdb->prepare( 70 | "SELECT COUNT(*) FROM $user_capability_table WHERE user_id = %d AND capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 71 | Groups_Utility::id( $user_id ), 72 | Groups_Utility::id( $capability_id ) 73 | ) ) ) ) { 74 | $data = array( 75 | 'user_id' => Groups_Utility::id( $user_id ), 76 | 'capability_id' => Groups_Utility::id( $capability_id ) 77 | ); 78 | $formats = array( '%d', '%d' ); 79 | if ( $wpdb->insert( $user_capability_table, $data, $formats ) ) { 80 | $result = true; 81 | do_action( 'groups_created_user_capability', $user_id, $capability_id ); 82 | } 83 | } 84 | } 85 | 86 | } 87 | return $result; 88 | } 89 | 90 | /** 91 | * Retrieve a user-capability relation. 92 | * 93 | * @param int $user_id user's id 94 | * @param int $capability_id capability's id 95 | * 96 | * @return object upon success, otherwise false 97 | */ 98 | public static function read( $user_id, $capability_id ) { 99 | global $wpdb; 100 | $result = false; 101 | 102 | $user_capability_table = _groups_get_tablename( 'user_capability' ); 103 | $user_capability = $wpdb->get_row( $wpdb->prepare( 104 | "SELECT * FROM $user_capability_table WHERE user_id = %d AND capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 105 | Groups_Utility::id( $user_id ), 106 | Groups_Utility::id( $capability_id ) 107 | ) ); 108 | if ( $user_capability !== null ) { 109 | $result = $user_capability; 110 | } 111 | return $result; 112 | } 113 | 114 | /** 115 | * Update user-capability relation. 116 | * 117 | * As the relation has no properties that could be updated, this method does nothing and will return false. 118 | * 119 | * @param array $map 120 | * 121 | * @return true if successful, false otherwise 122 | */ 123 | public static function update( $map ) { 124 | $result = false; 125 | // @since 2.20.0 do not process 126 | if ( false ) { 127 | $capability_id = isset( $map['capability_id'] ) ? $map['capability_id'] : null; 128 | $user_id = isset( $map['user_id'] ) ? $map['user_id'] : null; 129 | if ( $capability_id !== null && $user_id !== null ) { 130 | // make sure user and capability exist 131 | if ( ( false !== Groups_Utility::id( $user_id ) ) && get_user_by( 'id', $user_id ) && Groups_Capability::read( $capability_id ) ) { 132 | $result = true; 133 | do_action( 'groups_updated_user_capability', $user_id, $capability_id ); 134 | } 135 | } 136 | } 137 | return $result; 138 | } 139 | 140 | /** 141 | * Remove user-capability relation. 142 | * 143 | * @param int $user_id 144 | * @param int $capability_id 145 | * 146 | * @return true if successful, false otherwise 147 | */ 148 | public static function delete( $user_id, $capability_id ) { 149 | 150 | global $wpdb; 151 | $result = false; 152 | 153 | // avoid nonsense requests 154 | if ( !empty( $capability_id ) ) { 155 | // to allow deletion of an entry after a user has been deleted, 156 | // we don't check if the user exists 157 | $user_capability_table = _groups_get_tablename( 'user_capability' ); 158 | // get rid of it 159 | $rows = $wpdb->query( $wpdb->prepare( 160 | "DELETE FROM $user_capability_table WHERE user_id = %d AND capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 161 | Groups_Utility::id( $user_id ), 162 | Groups_Utility::id( $capability_id ) 163 | ) ); 164 | // must have affected a row, otherwise no great success 165 | $result = ( $rows !== false ) && ( $rows > 0 ); 166 | if ( $result ) { 167 | do_action( 'groups_deleted_user_capability', $user_id, $capability_id ); 168 | } 169 | } 170 | return $result; 171 | } 172 | 173 | /** 174 | * Hooks into the deleted_user action to remove the deleted user from 175 | * all capabilities it is related to. 176 | * 177 | * @param int $user_id 178 | */ 179 | public static function deleted_user( $user_id ) { 180 | global $wpdb; 181 | 182 | $user_capability_table = _groups_get_tablename( 'user_capability' ); 183 | $rows = $wpdb->get_results( $wpdb->prepare( 184 | "SELECT * FROM $user_capability_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 185 | Groups_Utility::id( $user_id ) 186 | ) ); 187 | if ( $rows ) { 188 | foreach( $rows as $row ) { 189 | // don't optimize that in preference of a standard deletion 190 | // process (trigger actions ...) 191 | self::delete( $row->user_id, $row->capability_id ); 192 | } 193 | } 194 | } 195 | 196 | /** 197 | * Hooks into groups_deleted_capability to resolve all existing relations 198 | * between users and the deleted capability. 199 | * 200 | * @param int $capability_id 201 | */ 202 | public static function groups_deleted_capability( $capability_id ) { 203 | global $wpdb; 204 | 205 | $user_capability_table = _groups_get_tablename( 'user_capability' ); 206 | $rows = $wpdb->get_results( $wpdb->prepare( 207 | "SELECT * FROM $user_capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 208 | Groups_Utility::id( $capability_id ) 209 | ) ); 210 | if ( $rows ) { 211 | foreach( $rows as $row ) { 212 | // do NOT 'optimize' (must trigger actions ... same as above) 213 | self::delete( $row->user_id, $row->capability_id ); 214 | } 215 | } 216 | } 217 | } 218 | Groups_User_Capability::init(); 219 | -------------------------------------------------------------------------------- /lib/core/class-groups-utility.php: -------------------------------------------------------------------------------- 1 | anonymous). 33 | * 34 | * @param string|int $id 35 | * @return int|boolean if validated, the id as an int, otherwise false 36 | */ 37 | public static function id( $id ) { 38 | $result = false; 39 | if ( is_numeric( $id ) ) { 40 | $id = intval( $id ); 41 | //if ( $id > 0 ) { 42 | if ( $id >= 0 ) { // 0 => anonymous 43 | $result = $id; 44 | } 45 | } 46 | return $result; 47 | } 48 | 49 | /** 50 | * Returns an array of blog_ids for current blogs. 51 | * @return array of int with blog ids 52 | */ 53 | public static function get_blogs() { 54 | global $wpdb; 55 | $result = array(); 56 | if ( is_multisite() ) { 57 | $blogs = $wpdb->get_results( $wpdb->prepare( 58 | "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d AND archived = '0' AND spam = '0' AND deleted = '0' ORDER BY registered DESC", 59 | $wpdb->siteid 60 | ) ); 61 | if ( is_array( $blogs ) ) { 62 | foreach( $blogs as $blog ) { 63 | $result[] = $blog->blog_id; 64 | } 65 | } 66 | } else { 67 | $result[] = get_current_blog_id(); 68 | } 69 | return $result; 70 | } 71 | 72 | 73 | /** 74 | * Get the tree hierarchy of groups. 75 | * 76 | * @param array $tree 77 | * 78 | * @return array 79 | */ 80 | public static function get_group_tree( &$tree = null, $linked = false ) { 81 | global $wpdb; 82 | $group_table = _groups_get_tablename( 'group' ); 83 | if ( $tree === null ) { 84 | $tree = array(); 85 | $root_groups = $wpdb->get_results( "SELECT group_id FROM $group_table WHERE parent_id IS NULL ORDER BY name" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 86 | if ( $root_groups ) { 87 | foreach( $root_groups as $root_group ) { 88 | $group_id = Groups_Utility::id( $root_group->group_id ); 89 | $tree[$group_id] = array(); 90 | } 91 | } 92 | self::get_group_tree( $tree ); 93 | } else { 94 | foreach( $tree as $group_id => $nodes ) { 95 | $children = $wpdb->get_results( $wpdb->prepare( 96 | "SELECT group_id FROM $group_table WHERE parent_id = %d ORDER BY name", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 97 | Groups_Utility::id( $group_id ) 98 | ) ); 99 | foreach( $children as $child ) { 100 | $tree[$group_id][$child->group_id] = array(); 101 | } 102 | self::get_group_tree( $tree[$group_id] ); 103 | } 104 | } 105 | return $tree; 106 | } 107 | 108 | /** 109 | * Render options from tree for select. 110 | * 111 | * @since 2.19.0 112 | * 113 | * @param array $tree 114 | * @param string $output 115 | * @param int $level 116 | */ 117 | public static function render_group_tree_options( &$tree, &$output, $level = 0, $selected = array() ) { 118 | foreach( $tree as $group_id => $nodes ) { 119 | $output .= sprintf( 120 | ''; 132 | if ( !empty( $nodes ) ) { 133 | self::render_group_tree_options( $nodes, $output, $level + 1, $selected ); 134 | } 135 | } 136 | } 137 | 138 | /** 139 | * Render the group tree to the $output parameter passed by reference. 140 | * 141 | * @param array $tree 142 | * @param string $output 143 | */ 144 | public static function render_group_tree( &$tree, &$output, $linked = false ) { 145 | $output .= ''; 171 | } 172 | 173 | /** 174 | * Compares the two object's names, used for groups and 175 | * capabilities, i.e. Groups_Group and Groups_Capability can be compared 176 | * if both are of the same class. Otherwise this will return 0. 177 | * 178 | * @param Groups_Group|Groups_Capability $o1 179 | * @param Groups_Group|Groups_Capability $o2 must match the class of $o1 180 | * 181 | * @return number 182 | */ 183 | public static function cmp( $o1, $o2 ) { 184 | $result = 0; 185 | if ( $o1 instanceof Groups_Group && $o2 instanceof Groups_Group ) { 186 | $result = strcmp( $o1->get_name(), $o2->get_name() ); 187 | } else if ( $o1 instanceof Groups_Capability && $o2 instanceof Groups_Capability ) { 188 | $result = strcmp( $o1->get_capability(), $o2->get_capability() ); 189 | } 190 | return $result; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /lib/core/constants.php: -------------------------------------------------------------------------------- 1 | prefix The current WordPress table prefix. Allowed characters are a-z, A-Z, 0-9, the dash - and the underscore _. 157 | * 158 | * @return str The possibly-modified table prefix. 159 | */ 160 | $prefix = apply_filters( 'groups_get_table_prefix', $wpdb->prefix ); 161 | 162 | if ( is_string( $prefix ) ) { 163 | $prefix = preg_replace( '/[^a-zA-Z0-9-_]+/', '', $prefix ); 164 | } 165 | 166 | // use the default if the filter returned nonsense or null due to error 167 | if ( !is_string( $prefix ) ) { 168 | $prefix = $wpdb->prefix; 169 | } 170 | 171 | // Return the constructed table name. 172 | return $prefix . GROUPS_TP . $name; 173 | } 174 | 175 | /** 176 | * This returns true if admin override is enabled and the current user 177 | * is an administrator, otherwise false. 178 | * To enable admin override (AKA god mode for admins), add this to 179 | * your wp-config.php : 180 | * 181 | * define( 'GROUPS_ADMINISTRATOR_OVERRIDE', true ); 182 | * 183 | * Enabling this is NOT recommended for production sites. 184 | * 185 | * @param int $user_id indicate the user ID or omit to check for the current user 186 | * 187 | * @return boolean 188 | */ 189 | function _groups_admin_override( $user_id = null ) { 190 | $result = false; 191 | if ( ( $user_id === null ) && function_exists( 'get_current_user_id' ) ) { 192 | $user_id = get_current_user_id(); 193 | } 194 | if ( $user_id ) { 195 | if ( defined( 'GROUPS_ADMINISTRATOR_OVERRIDE' ) && ( GROUPS_ADMINISTRATOR_OVERRIDE === true ) ) { 196 | // @since 3.1.0 user_can() relies on get_userdata() which is defined in wp-includes/pluggable.php 197 | if ( function_exists( 'user_can' ) && function_exists( 'get_userdata' ) ) { 198 | if ( user_can( $user_id, 'administrator' ) ) { 199 | $result = true; 200 | } 201 | } 202 | } 203 | } 204 | return $result; 205 | } 206 | -------------------------------------------------------------------------------- /lib/extra/class-groups-extra.php: -------------------------------------------------------------------------------- 1 | get( 'wc_query', null ); 99 | if ( ! empty( $wc_query ) && $wc_query === 'product_query' ) { 100 | $post_types = 'product'; 101 | } 102 | } 103 | return $post_types; 104 | } 105 | 106 | /** 107 | * Declare WooCommerce feature compatibility. 108 | * 109 | * @since 3.4.1 110 | */ 111 | public static function before_woocommerce_init() { 112 | // HPOS 113 | if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { 114 | \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', GROUPS_FILE, true ); 115 | } 116 | } 117 | } 118 | Groups_Extra::boot(); 119 | -------------------------------------------------------------------------------- /lib/views/class-groups-uie.php: -------------------------------------------------------------------------------- 1 | '; 111 | $output .= '.components-panel { overflow: visible !important; }'; 112 | $output .= ''; 113 | // Act immediately if DOMContentLoaded was already dispatched, otherwise defer to handler. 114 | $output .= ''; 126 | } 127 | return $output; 128 | } 129 | 130 | public static function render_add_titles( $selector ) { 131 | $output = ''; 141 | return $output; 142 | } 143 | } 144 | Groups_UIE::init(); 145 | --------------------------------------------------------------------------------