├── 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 = '
';
98 | foreach( $valid_read_caps as $valid_read_cap ) {
99 | if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
100 | if ( in_array( $valid_read_cap, $read_caps ) ) {
101 | $output .= '';
102 | $output .= stripslashes( wp_strip_all_tags( $capability->capability ) );
103 | $output .= ' ';
104 | }
105 | }
106 | }
107 | $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( '', esc_attr( GROUPS_READ_POST_CAPABILITIES . '[]' ) );
71 | foreach( $capabilities as $capability ) {
72 | $selected = in_array( $capability->capability, $applicable_read_caps ) ? ' selected="selected" ' : '';
73 | if ( $capability->capability == Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) {
74 | $selected .= ' disabled="disabled" ';
75 | }
76 | printf( '%s ', esc_attr( $capability->capability_id ), $selected, stripslashes( wp_filter_nohtml_kses( $capability->capability ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
77 | }
78 | echo ' ';
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 .= '';
186 | foreach( $entries as $entry ) {
187 | $output .= '';
188 | $output .= $entry; // entries are already escaped for output
189 | $output .= ' ';
190 | }
191 | $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 .= '';
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( '%s ', 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( '%s ', esc_html__( 'Description', 'groups' ) );
76 | $output .= sprintf( '%s ', 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 .= '';
69 | $output .= esc_html__( 'Name', 'groups' );
70 | $output .= ' ';
71 | $output .= sprintf( ' ', esc_attr( stripslashes( $name ) ) );
72 | $output .= '
';
73 |
74 | $output .= '
';
75 | $output .= '';
76 | $output .= esc_html__( 'Parent', 'groups' );
77 | $output .= ' ';
78 | $output .= $parent_select;
79 | $output .= '
';
80 |
81 | $output .= '
';
82 | $output .= '';
83 | $output .= esc_html__( 'Description', 'groups' );
84 | $output .= ' ';
85 | $output .= '';
86 | $output .= stripslashes( wp_filter_nohtml_kses( $description ) );
87 | $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 .= '';
98 | $output .= esc_html__( 'Capabilities', 'groups' );
99 | $output .= sprintf(
100 | '',
101 | esc_attr__( 'Choose capabilities …', 'groups' )
102 | );
103 | foreach( $capabilities as $capability ) {
104 | $output .= sprintf(
105 | '%s ',
106 | esc_attr( $capability->capability_id ),
107 | in_array( $capability->capability_id, $selected_capabilities ) ? 'selected="selected"' : '',
108 | stripslashes( wp_filter_nohtml_kses( $capability->capability ) )
109 | );
110 | }
111 | $output .= ' ';
112 | $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 |
139 | { __( 'Content will be shown to users that are members of these groups:', 'groups' ) }
140 |
141 |
142 |
143 |
152 |
153 |
154 | ,
155 |
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-member/editor.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * groups-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-member,
30 | .wp-block-groups-groups-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-member::before {
51 | left: 4px;
52 | right: unset;
53 | }
54 |
55 | .wp-block-groups-groups-member__inner-block {
56 | padding-left: 5px;
57 | }
58 |
--------------------------------------------------------------------------------
/lib/blocks/src/blocks/groups-non-member/block.js:
--------------------------------------------------------------------------------
1 | /**
2 | * groups-non-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 Non-member Block.
37 | */
38 | const nonMemberIcon = 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 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-Non-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-non-member',
71 | {
72 | title: __( 'Groups Non-member','groups' ), // Block title.
73 | description: __( 'Hide content from group members', 'groups' ),
74 | icon: nonMemberIcon, // 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 |
139 | { __( 'Content will be shown to users that are not members of these groups:', 'groups' ) }
140 |
141 |
142 |
143 |
152 |
153 |
154 | ,
155 |
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 = '';
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 | '';
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 | '',
121 | esc_attr( $group_id ),
122 | in_array( $group_id, $selected ) ? 'selected' : ''
123 | );
124 | $group = Groups_Group::read( $group_id );
125 | if ( $group ) {
126 | if ( $level > 0 ) {
127 | $output .= str_repeat( " ", $level ) . "⌞";
128 | }
129 | $output .= $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '';
130 | }
131 | $output .= ' ';
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 |
--------------------------------------------------------------------------------