├── .gitignore
├── composer.json
├── wp-user-groups
├── includes
│ ├── functions
│ │ ├── hooks.php
│ │ ├── taxonomies.php
│ │ ├── sponsor.php
│ │ ├── admin.php
│ │ └── common.php
│ └── classes
│ │ └── class-user-taxonomy.php
└── assets
│ └── css
│ └── user-groups.css
├── README.md
├── wp-user-groups.php
├── readme.txt
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stuttter/wp-user-groups",
3 | "description": "Group users together with taxonomies & terms",
4 | "homepage": "https://github.com/stuttter/wp-user-groups",
5 | "type": "wordpress-plugin",
6 | "require": {
7 | "php": ">=8.0",
8 | "composer/installers": "^1.0"
9 | },
10 | "license": "GPL-2.0-or-later",
11 | "authors": [
12 | {
13 | "name": "John James Jacoby",
14 | "email": "johnjamesjacoby@me.com"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/wp-user-groups/includes/functions/hooks.php:
--------------------------------------------------------------------------------
1 | __( 'Group', 'wp-user-groups' ),
24 | 'plural' => __( 'Groups', 'wp-user-groups' ),
25 | 'managed' => false
26 | ) );
27 | }
28 |
29 | /**
30 | * Register default user group taxonomies
31 | *
32 | * This function is hooked onto WordPress's `init` action and creates two new
33 | * `WP_User_Taxonomy` objects for user "groups" and "types". It can be unhooked
34 | * and these taxonomies can be replaced with your own custom ones.
35 | *
36 | * @since 0.1.4
37 | */
38 | function wp_register_default_user_type_taxonomy() {
39 | new WP_User_Taxonomy( 'user-type', 'users/type', array(
40 | 'singular' => __( 'Type', 'wp-user-groups' ),
41 | 'plural' => __( 'Types', 'wp-user-groups' ),
42 | 'managed' => false
43 | ) );
44 | }
45 |
--------------------------------------------------------------------------------
/wp-user-groups/includes/functions/sponsor.php:
--------------------------------------------------------------------------------
1 | '' . esc_html( $text ) . ''
49 | ) );
50 | }
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WP User Groups
2 |
3 | WP User Groups allows users to be categorized using custom taxonomies & terms.
4 |
5 | * "Groups" & "Types" are created by default, and can be overridden
6 | * More user group types can be registered with custom arguments
7 | * Edit users and set their relationships
8 | * Bulk edit many users to quickly assign several at once
9 | * Filter the users list to see which users are in what groups
10 | * Not destructive data storage (plugin can be enabled & disabled without damage)
11 | * Works great with all WP User & Term plugins (see below)
12 |
13 | # Installation
14 |
15 | * Download and install using the built in WordPress plugin installer.
16 | * Activate in the "Plugins" area of your admin by clicking the "Activate" link.
17 | * Consider sponsoring future development by clicking "Sponsor".
18 | * Visit "Users > Groups" and create some groups.
19 | * Add users to groups by editing their profile and checking the boxes.
20 |
21 | # FAQ
22 |
23 | ### Does this create new database tables?
24 |
25 | No. There are no new database tables with this plugin.
26 |
27 | ### Does this modify existing database tables?
28 |
29 | No. All of the WordPress core database tables remain untouched.
30 |
31 | ### Does this plugin integrate with user roles?
32 |
33 | No. This is best left to plugins that choose to integrate with this plugin.
34 |
35 | ### Where can I get support?
36 |
37 | * Community: https://wordpress.org/support/plugin/wp-user-groups
38 | * Development: https://github.com/stuttter/wp-user-groups/discussions
39 |
40 | ### Contributing
41 |
42 | Please [open a new issue](/pull/new/master) to discuss whether the feature is a good fit for the project. Once you've decided to work on a pull request, please follow the [WordPress Coding Standards](http://make.wordpress.org/core/handbook/coding-standards/).
43 |
--------------------------------------------------------------------------------
/wp-user-groups.php:
--------------------------------------------------------------------------------
1 | $taxonomy,
47 | 'hide_empty' => false,
48 | 'number' => 1,
49 | 'fields' => 'ids',
50 | ) );
51 | if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
52 | $has_terms = true;
53 | break;
54 | }
55 | }
56 | }
57 |
58 | // Bail if no groups are registered
59 | if ( ! $has_terms ) {
60 | return $sections;
61 | }
62 |
63 | // Copy for modifying
64 | $new_sections = $sections;
65 |
66 | // Add the "Groups" section
67 | $new_sections['groups'] = array(
68 | 'id' => 'groups',
69 | 'slug' => 'groups',
70 | 'name' => esc_html__( 'Groups', 'wp-user-groups' ),
71 | 'cap' => 'edit_profile',
72 | 'icon' => 'dashicons-groups',
73 | 'parent' => '',
74 | 'order' => 90
75 | );
76 |
77 | // Filter & return
78 | return apply_filters( 'wp_user_groups_add_profile_section', $new_sections, $sections );
79 | }
80 |
--------------------------------------------------------------------------------
/wp-user-groups/assets/css/user-groups.css:
--------------------------------------------------------------------------------
1 | table.user-groups {
2 | margin: 0;
3 | }
4 |
5 | table.user-groups .row-actions {
6 | visibility: hidden;
7 | }
8 | table.user-groups tr:hover .row-actions {
9 | visibility: visible;
10 | }
11 | table.user-groups thead td.check-column,
12 | table.user-groups tfoot td.check-column,
13 | table.user-groups .inactive th.check-column{
14 | padding-left: 6px;
15 | }
16 | table.user-groups tbody th.check-column,
17 | table.user-groups tbody {
18 | padding: 12px 0 0 2px;
19 | }
20 | table.user-groups th,
21 | table.user-groups td {
22 | padding: 10px;
23 | vertical-align: top;
24 | width: auto;
25 | font-weight: normal;
26 | }
27 |
28 | table.user-groups .column-primary {
29 | width: 25%;
30 | }
31 |
32 | table.user-groups .column-primary strong {
33 | display: block;
34 | margin-bottom: .2em;
35 | font-size: 14px;
36 | }
37 |
38 | table.user-groups .column-users {
39 | width: 10%;
40 | padding-right: 0;
41 | text-align: center;
42 | }
43 |
44 | table.user-groups .description {
45 | color: #666;
46 | }
47 |
48 | table.user-groups .inactive td,
49 | table.user-groups .inactive th,
50 | table.user-groups .active td,
51 | table.user-groups .active th {
52 | padding: 10px 9px;
53 | }
54 |
55 | table.user-groups .active td,
56 | table.user-groups .active th {
57 | background-color: #f7fcfe;
58 | }
59 |
60 | table.user-groups .inactive td,
61 | table.user-groups .inactive th,
62 | table.user-groups .active td,
63 | table.user-groups .active th {
64 | -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
65 | box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
66 | }
67 |
68 | table.user-groups tr.active + tr.inactive th,
69 | table.user-groups tr.active + tr.inactive td {
70 | border-top: 1px solid rgba(0,0,0,0.03);
71 | -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,0.02), inset 0 -1px 0 #e1e1e1;
72 | box-shadow: inset 0 1px 0 rgba(0,0,0,0.02), inset 0 -1px 0 #e1e1e1;
73 | }
74 |
75 | table.user-groups tr.active + tr.inactive.update th,
76 | table.user-groups tr.active + tr.inactive.update td,
77 | table.user-groups tr.active + tr.inactive.updated th,
78 | table.user-groups tr.active + tr.inactive.updated td,
79 | table.user-groups tbody tr:last-of-type td,
80 | table.user-groups tbody tr:last-of-type th {
81 | -webkit-box-shadow: none;
82 | box-shadow: none;
83 | }
84 |
85 | table.user-groups .active th.check-column {
86 | border-left: 4px solid #00a0d2;
87 | }
88 |
89 | table.user-groups .plugin-title,
90 | table.user-groups .theme-title {
91 | padding-right: 12px;
92 | white-space:nowrap;
93 | }
94 |
95 | table.user-groups .inactive .plugin-title strong {
96 | font-weight: 400;
97 | }
98 |
99 | .user-tax-form fieldset {
100 | margin: 8px 10px 0 0;
101 | }
102 | .subsubsub + form + br.clear {
103 | display: none;
104 | }
105 | .tax-actions {
106 | margin-bottom: 5px;
107 | }
108 |
109 | @media screen and ( min-width: 786px ) {
110 | .wp-list-table.tags .column-users {
111 | width: 10%;
112 | text-align: center;
113 | }
114 | }
115 |
116 | @media screen and ( max-width: 786px ) {
117 | table.form-table table th {
118 | display: table-cell;
119 | }
120 |
121 | table.form-table table td {
122 | display: table-cell !important;
123 | }
124 | }
125 |
126 | @media screen and ( min-width: 1110px ) {
127 | table.user-groups {
128 | min-width: 650px;
129 | }
130 | }
131 |
132 | #wp_user_taxonomy_user-group .inside,
133 | #wp_user_taxonomy_user-type .inside {
134 | margin: 0;
135 | padding: 0;
136 | }
137 |
138 | body.toplevel_page_groups .metabox-holder table.user-groups,
139 | body.users_page_groups .metabox-holder table.user-groups {
140 | border: none;
141 | margin: 0;
142 | padding: 0;
143 | }
144 |
--------------------------------------------------------------------------------
/wp-user-groups/includes/functions/common.php:
--------------------------------------------------------------------------------
1 | ID )
26 | ? $user->ID
27 | : absint( $user );
28 |
29 | // Bail if empty
30 | if ( empty( $user_id ) ) {
31 | return false;
32 | }
33 |
34 | // Return user terms
35 | return wp_get_object_terms( $user_id, $taxonomy, array(
36 | 'fields' => 'all_with_object_id'
37 | ) );
38 | }
39 |
40 | /**
41 | * Save taxonomy terms for a specific user
42 | *
43 | * @since 0.1.0
44 | *
45 | * @param mixed $user
46 | * @param string $taxonomy
47 | * @param array $terms
48 | *
49 | * @return void
50 | */
51 | function wp_set_terms_for_user( $user = false, $taxonomy = '', $terms = array() ) {
52 |
53 | // Verify user ID
54 | $user_id = is_object( $user ) && ! empty( $user->ID )
55 | ? $user->ID
56 | : absint( $user );
57 |
58 | // Bail if empty
59 | if ( empty( $user_id ) ) {
60 | return false;
61 | }
62 |
63 | // Delete all terms for the user
64 | if ( empty( $terms ) ) {
65 | wp_delete_object_term_relationships( $user_id, $taxonomy );
66 |
67 | // Sets the terms for the user
68 | } else {
69 | wp_set_object_terms( $user_id, $terms, $taxonomy, false );
70 | }
71 |
72 | // Clean the cache
73 | clean_object_term_cache( $user_id, $taxonomy );
74 | }
75 |
76 | /**
77 | * Get all user groups
78 | *
79 | * @uses get_taxonomies() To get user-group taxonomies
80 | *
81 | * @since 0.1.5
82 | *
83 | * @param array $args Optional. An array of `key => value` arguments to
84 | * match against the taxonomy objects. Default empty array.
85 | * @param string $output Optional. The type of output to return in the array.
86 | * Accepts either taxonomy 'names' or 'objects'. Default 'names'.
87 | * @param string $operator Optional. The logical operation to perform.
88 | * Accepts 'and' or 'or'. 'or' means only one element from
89 | * the array needs to match; 'and' means all elements must
90 | * match. Default 'and'.
91 | *
92 | * @return array A list of taxonomy names or objects.
93 | */
94 | function wp_get_user_groups( $args = array(), $output = 'names', $operator = 'and' ) {
95 |
96 | // Parse arguments
97 | $r = wp_parse_args( $args, array(
98 | 'user_group' => true
99 | ) );
100 |
101 | // Return user group taxonomies
102 | return get_taxonomies( $r, $output, $operator );
103 | }
104 |
105 | /**
106 | * Get all user group objects
107 | *
108 | * @uses wp_get_user_groups() To get user group objects
109 | *
110 | * @since 0.1.5
111 | *
112 | * @param array $args See wp_get_user_groups()
113 | * @param string $operator See wp_get_user_groups()
114 | *
115 | * @return array
116 | */
117 | function wp_get_user_group_objects( $args = array(), $operator = 'and' ) {
118 | return wp_get_user_groups( $args, 'objects', $operator );
119 | }
120 |
121 | /**
122 | * Return a list of users in a specific group.
123 | *
124 | * @since 0.1.0
125 |
126 | * @param array $args {
127 | * Array or term information.
128 | *
129 | * @type string $taxomony Taxonomy name. Default is 'user-group'.
130 | * @type string|int $term Search for this term value.
131 | * @type string $term_by Either 'slug', 'name', 'id' (term_id), or 'term_taxonomy_id'.
132 | * Default is 'slug'.
133 | * }
134 | * @param array $user_args Optional. WP_User_Query arguments.
135 | *
136 | * @return array List of users in the user group.
137 | */
138 | function wp_get_users_of_group( $args = array(), $user_args = array() ) {
139 |
140 | // Parse arguments.
141 | $r = wp_parse_args( $args, array(
142 | 'taxonomy' => 'user-group',
143 | 'term' => '',
144 | 'term_by' => 'slug'
145 | ) );
146 |
147 | // Get user IDs in group.
148 | $term = get_term_by( $r['term_by'], $r['term'], $r['taxonomy'] );
149 | $user_ids = get_objects_in_term( $term->term_id, $r['taxonomy'] );
150 |
151 | // Bail if no users in this term.
152 | if ( empty( $term ) || empty( $user_ids ) ) {
153 | return array();
154 | }
155 |
156 | // Parse optional user arguments
157 | $user_args = wp_parse_args( $user_args, array(
158 | 'orderby' => 'display_name',
159 | ) );
160 |
161 | // Strictly enforce the inclusion of user IDs to this group
162 | $user_args['include'] = $user_ids;
163 |
164 | // Return queried users.
165 | return get_users( $user_args );
166 | }
167 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === WP User Groups ===
2 | Author: Triple J Software, Inc.
3 | Author URI: https://jjj.software
4 | Donate link: https://buy.stripe.com/7sI3cd2tK1Cy2lydQR
5 | Plugin URI: https://wordpress.org/plugins/wp-user-groups/
6 | License URI: https://www.gnu.org/licenses/gpl-2.0.html
7 | License: GPLv2 or later
8 | Contributors: johnjamesjacoby
9 | Tags: user, profile, group, taxonomy, term
10 | Requires PHP: 8.0
11 | Requires at least: 5.2
12 | Tested up to: 5.8
13 | Stable tag: 2.5.1
14 |
15 | == Description ==
16 |
17 | WP User Groups allows users to be categorized using custom taxonomies & terms.
18 |
19 | * "Groups" & "Types" are created by default, and can be overridden
20 | * More user group types can be registered with custom arguments
21 | * Edit users and set their relationships
22 | * Bulk edit many users to quickly assign several at once
23 | * Filter the users list to see which users are in what groups
24 | * Not destructive data storage (plugin can be enabled & disabled without damage)
25 | * Works great with all WP User & Term plugins (see below)
26 |
27 | = Recommended Plugins =
28 |
29 | If you like this plugin, you'll probably like these!
30 |
31 | * [WP User Profiles](https://wordpress.org/plugins/wp-user-profiles/ "A sophisticated way to edit users in WordPress.")
32 | * [WP User Activity](https://wordpress.org/plugins/wp-user-activity/ "The best way to log activity in WordPress.")
33 | * [WP User Avatars](https://wordpress.org/plugins/wp-user-avatars/ "Allow users to upload avatars or choose them from your media library.")
34 | * [WP User Groups](https://wordpress.org/plugins/wp-user-groups/ "Group users together with taxonomies & terms.")
35 | * [WP User Signups](https://wordpress.org/plugins/wp-user-signups/ "The best way to manage user & site sign-ups in WordPress.")
36 | * [WP Term Authors](https://wordpress.org/plugins/wp-term-authors/ "Authors for categories, tags, and other taxonomy terms.")
37 | * [WP Term Colors](https://wordpress.org/plugins/wp-term-colors/ "Pretty colors for categories, tags, and other taxonomy terms.")
38 | * [WP Term Families](https://wordpress.org/plugins/wp-term-families/ "Associate taxonomy terms with other taxonomy terms.")
39 | * [WP Term Icons](https://wordpress.org/plugins/wp-term-icons/ "Pretty icons for categories, tags, and other taxonomy terms.")
40 | * [WP Term Images](https://wordpress.org/plugins/wp-term-images/ "Pretty images for categories, tags, and other taxonomy terms.")
41 | * [WP Term Locks](https://wordpress.org/plugins/wp-term-locks/ "Protect categories, tags, and other taxonomy terms from being edited or deleted.")
42 | * [WP Term Order](https://wordpress.org/plugins/wp-term-order/ "Sort taxonomy terms, your way.")
43 | * [WP Term Visibility](https://wordpress.org/plugins/wp-term-visibility/ "Visibilities for categories, tags, and other taxonomy terms.")
44 | * [WP Media Categories](https://wordpress.org/plugins/wp-media-categories/ "Add categories to media & attachments.")
45 | * [WP Pretty Filters](https://wordpress.org/plugins/wp-pretty-filters/ "Makes post filters better match what's already in Media & Attachments.")
46 | * [WP Chosen](https://wordpress.org/plugins/wp-chosen/ "Make long, unwieldy select boxes much more user-friendly.")
47 |
48 | == Screenshots ==
49 |
50 | 1. Menu Items
51 | 2. Groups Taxonomy
52 | 3. Types Taxonomy
53 | 4. User Edit & Assignment
54 | 5. Users List
55 | 6. Users List (Filtered)
56 |
57 | == Installation ==
58 |
59 | 1. Download and install using the built in WordPress plugin installer.
60 | 1. Activate in the "Plugins" area of your admin by clicking the "Activate" link.
61 | 1. Visit "Users > Groups" and create some groups
62 | 1. Add users to groups by editing their profile and checking the boxes
63 |
64 | == Frequently Asked Questions ==
65 |
66 | = Does this create new database tables? =
67 |
68 | No. There are no new database tables with this plugin.
69 |
70 | = Does this modify existing database tables? =
71 |
72 | No. All of the WordPress core database tables remain untouched.
73 |
74 | = Does this plugin integrate with user roles? =
75 |
76 | No. This is best left to plugins that choose to integrate with this plugin.
77 |
78 | = Where can I get support? =
79 |
80 | * Community: https://wordpress.org/support/plugin/wp-user-groups
81 | * Development: https://github.com/stuttter/wp-user-groups/discussions
82 |
83 | == Changelog ==
84 |
85 | = [2.5.1] - 2021/05/29 =
86 | * Update author info
87 | * Add sponsor link
88 |
89 | = [2.5.0] - 2021/03/23 =
90 | * Improve compatibility with WP User Profiles plugin (props John Blackbourn)
91 |
92 | = [2.4.0] - 2018/10/04 =
93 | * Simplify get and set functions for user terms
94 | * Add support for advanced WP_User_Query arguments
95 | * Fix custom column support in user taxonomies
96 |
97 | = [2.3.0] - 2018/10/03 =
98 | * More descriptive text for bulk actions
99 | * Fix bulk actions not working
100 |
101 | = [2.2.0] - 2018/06/05 =
102 | * Add "Managed" taxonomy type, so users cannot assign their own groups
103 |
104 | = [2.1.0] - 2018/04/16 =
105 | * Add a dedicated nonce for each user taxonomy (thanks Tom Adams!)
106 |
107 | = [2.0.0] - 2017/10/24 =
108 | * Fix bug with user filtering
109 | * Fix bug with setting user terms
110 | * Add `exclusive` group argument to use radios instead of checkboxes
111 |
112 | = [1.1.0] - 2017/03/28 =
113 | * Change default taxonomy to `user-group` in wp_get_users_of_group()
114 |
115 | = [1.0.0] - 2016/12/07 =
116 | * WordPress 4.7 compatibility
117 | * Improved bulk actions (requires WordPress 4.7)
118 | * Official stable release
119 |
120 | = [0.2.1] - 2016/05/25 =
121 | * Fix bug with user list
122 | * Introduce wp_get_users_of_group() helper function
123 | * Add unique class to administration forms
124 |
125 | = [0.2.0] - 2015/12/23 =
126 | * Support for WP User Profiles 0.2.0
127 |
128 | = [0.1.9] - 2015/12/21 =
129 | * Fix bug with User Profiles integration
130 |
131 | = [0.1.8] - 2015/11/11 =
132 | * Support for WP User Profiles 0.1.9
133 |
134 | = [0.1.7] - 2015/11/09 =
135 | * Update assets & meta
136 |
137 | = [0.1.6] - 2015/10/23 =
138 | * Add support for WP User Profiles
139 |
140 | = [0.1.5] - 2015/10/13 =
141 | * Added `user_group` property to taxonomies
142 | * Added functions for retrieving only user-groups from taxonomies global
143 |
144 | = [0.1.0] - 2015/09/10 =
145 | * Refactor
146 | * Improve asset management
147 | * Styling tweaks
148 |
149 | = [0.1.2] - 2015/09/01 =
150 | * Namespace default taxonomy IDs
151 |
152 | = [0.1.1] - 2015/08/24 =
153 | * User profile UI uses a mock list-table
154 |
155 | = [0.1.0] - 2015/08/19 =
156 | * Initial release
157 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/wp-user-groups/includes/classes/class-user-taxonomy.php:
--------------------------------------------------------------------------------
1 | taxonomy = sanitize_key( $taxonomy );
129 | $this->slug = sanitize_text_field( $slug );
130 | $this->args = $args;
131 | $this->labels = $labels;
132 | $this->caps = $caps;
133 |
134 | // Label helpers
135 | $this->tax_singular = $args['singular'];
136 | $this->tax_plural = $args['plural'];
137 | $this->tax_singular_low = strtolower( $this->tax_singular );
138 | $this->tax_plural_low = strtolower( $this->tax_plural );
139 |
140 | // Register the taxonomy
141 | $this->register_user_taxonomy();
142 |
143 | // Hook into actions & filters
144 | $this->hooks();
145 |
146 | // JIT
147 | do_action( 'wp_user_taxonomy', $this );
148 | }
149 |
150 | /**
151 | * Hook in to actions & filters
152 | *
153 | * @since 0.1.1
154 | */
155 | protected function hooks() {
156 |
157 | // Bulk edit
158 | add_filter( 'admin_notices', array( $this, 'bulk_notice' ) );
159 | add_filter( 'bulk_actions-users', array( $this, 'bulk_actions' ) );
160 | add_filter( 'bulk_actions-users', array( $this, 'bulk_actions_sort' ), 99 );
161 | add_action( 'handle_bulk_actions-users', array( $this, 'handle_bulk_actions' ), 10, 3 );
162 |
163 | // Include users by taxonomy term in users.php
164 | add_action( 'pre_get_users', array( $this, 'pre_get_users' ) );
165 |
166 | // Custom list-table views
167 | add_filter( 'views_users', array( $this, 'list_table_views' ) );
168 |
169 | // Column styling
170 | add_action( 'admin_head', array( $this, 'admin_head' ) );
171 | add_action( 'admin_menu', array( $this, 'add_admin_page' ) );
172 |
173 | // WP User Profile support
174 | add_action( 'wp_user_profiles_add_meta_boxes', array( $this, 'add_meta_box' ), 10, 2 );
175 |
176 | // Taxonomy columns
177 | add_action( "manage_{$this->taxonomy}_custom_column", array( $this, 'manage_custom_column' ), 10, 3 );
178 | add_filter( "manage_edit-{$this->taxonomy}_columns", array( $this, 'manage_edit_users_column' ) );
179 |
180 | // User columns
181 | add_filter( 'manage_users_columns', array( $this, 'add_manage_users_columns' ), 15, 1 );
182 | add_action( 'manage_users_custom_column', array( $this, 'user_column_data' ), 15, 3 );
183 |
184 | // Update the groups when the edit user page is updated
185 | add_action( 'personal_options_update', array( $this, 'save_terms_for_user' ) );
186 | add_action( 'edit_user_profile_update', array( $this, 'save_terms_for_user' ) );
187 |
188 | // Add section to the edit user page in the admin to select group
189 | if ( ! function_exists( '_wp_user_profiles' ) ) {
190 | add_action( 'show_user_profile', array( $this, 'edit_user_relationships' ), 99 );
191 | add_action( 'edit_user_profile', array( $this, 'edit_user_relationships' ), 99 );
192 | }
193 |
194 | // Cleanup stuff
195 | add_action( 'delete_user', array( $this, 'delete_term_relationships' ) );
196 | add_filter( 'sanitize_user', array( $this, 'disable_username' ) );
197 | }
198 |
199 | /**
200 | * Add the administration page for this taxonomy
201 | *
202 | * @since 0.1.0
203 | */
204 | public function add_admin_page() {
205 |
206 | // Setup the URL
207 | $tax = get_taxonomy( $this->taxonomy );
208 |
209 | // No UI
210 | if ( false === $tax->show_ui ) {
211 | return;
212 | }
213 |
214 | // URL for the taxonomy
215 | $url = add_query_arg( array( 'taxonomy' => $tax->name ), 'edit-tags.php' );
216 |
217 | // Add page to users
218 | add_users_page(
219 | esc_attr( $tax->labels->menu_name ),
220 | esc_attr( $tax->labels->menu_name ),
221 | $tax->cap->manage_terms,
222 | $url
223 | );
224 |
225 | // Hook into early actions to load custom CSS and our init handler.
226 | add_action( 'load-users.php', array( $this, 'admin_load' ) );
227 | add_action( 'load-edit-tags.php', array( $this, 'admin_load' ) );
228 | add_action( 'load-term.php', array( $this, 'admin_menu_highlight' ) );
229 | add_action( 'load-edit-tags.php', array( $this, 'admin_menu_highlight' ) );
230 | }
231 |
232 | /**
233 | * This tells WordPress to highlight the "Users" menu item when viewing a
234 | * user taxonomy.
235 | *
236 | * @since 0.1.0
237 | *
238 | * @global string $plugin_page
239 | */
240 | public function admin_menu_highlight() {
241 | global $plugin_page;
242 |
243 | // Set plugin page to "users.php" to get highlighting to be correct
244 | if ( isset( $_GET['taxonomy'] ) && ( $_GET['taxonomy'] === $this->taxonomy ) ) {
245 | $plugin_page = 'users.php';
246 | }
247 | }
248 |
249 | /**
250 | * Filter the body class
251 | *
252 | * @since 0.1.0
253 | */
254 | public function admin_load() {
255 | add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
256 | }
257 |
258 | /**
259 | * Add a class for this taxonomy
260 | *
261 | * @since 0.1.0
262 | *
263 | * @param string $classes
264 | * @return string
265 | */
266 | public function admin_body_class( $classes = '' ) {
267 |
268 | // Add a body class for this taxonomy if it's currently selected
269 | if ( isset( $_GET[ $this->taxonomy ] ) ) {
270 | $classes .= " tax-{$this->taxonomy}";
271 | }
272 |
273 | // Return maybe modified class
274 | return $classes;
275 | }
276 |
277 | /**
278 | * Stylize custom columns
279 | *
280 | * @since 0.1.0
281 | */
282 | public function admin_head() {
283 |
284 | // Compile the style
285 | $style = "
286 | .column-{$this->taxonomy} {
287 | width: 10%;
288 | }
289 | body.users-php.tax-{$this->taxonomy} .wrap > h1,
290 | body.users-php.tax-{$this->taxonomy} .wrap > h1 + .page-title-action {
291 | display: none;
292 | }";
293 |
294 | // Add inline style
295 | wp_add_inline_style( 'wp_user_groups', $style );
296 | }
297 |
298 | /**
299 | * Metaboxes for profile sections
300 | *
301 | * @since 0.1.6
302 | */
303 | public function add_meta_box( $type = '' ) {
304 |
305 | // Get hookname
306 | $hooks = wp_user_profiles_get_section_hooknames( 'groups' );
307 |
308 | // Bail if not the correct type
309 | if ( ! in_array( $type, $hooks, true ) ) {
310 | return;
311 | }
312 |
313 | // Get the taxonomy
314 | $tax = get_taxonomy( $this->taxonomy );
315 | $user_id = ! empty( $_GET['user_id'] )
316 | ? (int) $_GET['user_id']
317 | : get_current_user_id();
318 |
319 | // Bail if current user cannot assign terms to this user for this taxonomy
320 | if ( ! $this->can_assign( $user_id ) ) {
321 | return;
322 | }
323 |
324 | // Bail if no UI for taxonomy
325 | if ( false === $tax->show_ui ) {
326 | return;
327 | }
328 |
329 | // Get the terms of the taxonomy.
330 | $terms = get_terms( $this->taxonomy, array(
331 | 'hide_empty' => false
332 | ) );
333 |
334 | // Maybe add the metabox
335 | add_meta_box(
336 | 'wp_user_taxonomy_' . $this->taxonomy,
337 | $tax->label,
338 | array( $this, 'user_profile_metabox' ),
339 | $hooks[0],
340 | 'normal',
341 | 'default',
342 | array(
343 | 'user_id' => $user_id,
344 | 'tax' => $tax,
345 | 'terms' => $terms
346 | )
347 | );
348 | }
349 |
350 | /**
351 | * Save terms for a user for this taxonomy
352 | *
353 | * @since 0.1.0
354 | *
355 | * @param int $user_id
356 | */
357 | public function save_terms_for_user( $user_id = 0 ) {
358 |
359 | // Bail if nonce problem
360 | if ( ! $this->verify_nonce() ) {
361 | return;
362 | }
363 |
364 | // Additional checks if User Profiles is active
365 | if ( function_exists( 'wp_user_profiles_get_section_hooknames' ) ) {
366 |
367 | // Bail if no page
368 | if ( empty( $_GET['page'] ) ) {
369 | return;
370 | }
371 |
372 | // Bail if not saving this section
373 | if ( sanitize_key( $_GET['page'] ) !== 'groups' ) {
374 | return;
375 | }
376 | }
377 |
378 | // Make sure the current user can edit the user and assign terms before proceeding
379 | if ( ! $this->can_assign( $user_id ) ) {
380 | return false;
381 | }
382 |
383 | // Get terms from the $_POST global if available
384 | $terms = isset( $_POST[ $this->taxonomy ] )
385 | ? $_POST[ $this->taxonomy ]
386 | : null;
387 |
388 | // Set terms for user
389 | wp_set_terms_for_user( $user_id, $this->taxonomy, $terms );
390 | }
391 |
392 | /**
393 | * Update the term count for a user and taxonomy
394 | *
395 | * @since 0.1.0
396 | *
397 | * @param int $user_id
398 | */
399 | public function update_term_user_count( $terms = array(), $taxonomy = '' ) {
400 |
401 | // Fallback to this taxonomy
402 | if ( empty( $taxonomy ) ) {
403 | $taxonomy = $this->taxonomy;
404 | }
405 |
406 | // Update counts
407 | _update_generic_term_count( $terms, $taxonomy );
408 | }
409 |
410 | /**
411 | * Manage columns for user taxonomies
412 | *
413 | * @since 0.1.0
414 | *
415 | * @param array $columns
416 | * @return array
417 | */
418 | public function manage_edit_users_column( $columns = array() ) {
419 |
420 | // Unset the "Posts" column
421 | unset( $columns['posts'] );
422 |
423 | // Add the "Users" column
424 | $columns['users'] = esc_html__( 'Users', 'wp-user-groups' );
425 |
426 | // Return modified columns
427 | return $columns;
428 | }
429 |
430 | /**
431 | * Output the data for the "Users" column when viewing user taxonomies
432 | *
433 | * @since 0.1.0
434 | *
435 | * @param string $display
436 | * @param string $column
437 | * @param string $term_id
438 | */
439 | public function manage_custom_column( $display = false, $column = '', $term_id = 0 ) {
440 |
441 | // Users column gets custom content
442 | if ( 'users' === $column ) {
443 | $term = get_term( $term_id, $this->taxonomy );
444 | $args = array( $this->taxonomy => $term->slug );
445 | $users = admin_url( 'users.php' );
446 | $url = add_query_arg( $args, $users );
447 | $text = number_format_i18n( $term->count );
448 | $display = '' . esc_html( $text ) . '';
449 | }
450 |
451 | // Return the new content for display
452 | return $display;
453 | }
454 |
455 | /**
456 | * Output a "Relationships" section to show off taxonomy groupings
457 | *
458 | * @since 0.1.0
459 | *
460 | * @param mixed $user
461 | */
462 | public function edit_user_relationships( $user = false ) {
463 |
464 | // Bail if current user cannot assign terms to this user for this taxonomy
465 | if ( ! $this->can_assign( $user->ID ) ) {
466 | return;
467 | }
468 |
469 | $tax = get_taxonomy( $this->taxonomy );
470 |
471 | // Bail if no UI for taxonomy
472 | if ( false === $tax->show_ui ) {
473 | return;
474 | }
475 |
476 | // Get the terms of the taxonomy.
477 | $terms = get_terms( $this->taxonomy, array(
478 | 'hide_empty' => false
479 | ) ); ?>
480 |
481 |
485 |
486 |
487 |
488 |
489 |
490 |
496 |
497 |
509 |
510 | table_contents( $user, $args['args']['tax'], $args['args']['terms'] );
520 | }
521 |
522 | /**
523 | * Output metabox contents
524 | *
525 | * @since 0.1.6
526 | */
527 | protected function table_contents( $user, $tax, $terms ) {
528 | ?>
529 |
530 |
600 |
601 | nonce_field();
605 | }
606 |
607 | /**
608 | * Output row actions when editing a user
609 | *
610 | * @since 0.1.1
611 | *
612 | * @param object $term
613 | */
614 | protected function row_actions( $tax = array(), $term = false ) {
615 | $actions = array();
616 |
617 | // List users in group
618 | if ( current_user_can( 'list_users' ) ) {
619 | $args = array( $tax->name => $term->slug );
620 | $users = admin_url( 'users.php' );
621 | $url = add_query_arg( $args, $users );
622 | $actions[] = '' . esc_html__( 'View', 'wp-user-groups' ) . '';
623 | }
624 |
625 | // Edit term
626 | if ( current_user_can( 'edit_term', $term->term_id ) ) {
627 | $args = array( 'action' => 'edit', 'taxonomy' => $tax->name, 'tag_ID' => $term->term_id, 'post_type' => 'post' );
628 | $edit_tags = admin_url( 'edit-tags.php' );
629 | $url = add_query_arg( $args, $edit_tags );
630 | $actions[] = '' . esc_html__( 'Edit', 'wp-user-groups' ) . '';
631 | }
632 |
633 | // Filter
634 | $actions = apply_filters( 'wp_user_groups_row_actions', $actions, $tax, $term, $this );
635 |
636 | return implode( ' | ', $actions );
637 | }
638 |
639 | /**
640 | * Disallow taxonomy as a username
641 | *
642 | * @since 0.1.0
643 | *
644 | * @param string $username
645 | * @return string
646 | */
647 | public function disable_username( $username = '' ) {
648 |
649 | // Set username to empty if it's this taxonomy
650 | if ( $this->taxonomy === $username ) {
651 | $username = '';
652 | }
653 |
654 | // Return possible emptied username
655 | return $username;
656 | }
657 |
658 | /**
659 | * Delete term relationships
660 | *
661 | * @since 0.1.0
662 | *
663 | * @param int $user_id
664 | */
665 | public function delete_term_relationships( $user_id = 0 ) {
666 | wp_delete_object_term_relationships( $user_id, $this->taxonomy );
667 | }
668 |
669 | /** Post Type *************************************************************/
670 |
671 | /**
672 | * Register the taxonomy
673 | *
674 | * @since 0.1.0
675 | */
676 | protected function register_user_taxonomy() {
677 |
678 | // Parse the options
679 | $options = $this->parse_options();
680 |
681 | /**
682 | * Filter the objects for this taxonomy, allowing for multiple
683 | * relationships to exist. This is risky, as ID collisions may occur, so
684 | * make sure that you're using it correctly
685 | *
686 | * @since 2.4.0
687 | *
688 | * @param array $defaults Default object types. 'user' by default.
689 | * @param string $taxonomy The current taxonomy
690 | * @param
691 | */
692 | $objects = (array) apply_filters( 'wp_user_groups_taxonomy_objects', array(
693 | 'user'
694 | ) , $this->taxonomy, $options );
695 |
696 | // Register the taxonomy
697 | register_taxonomy(
698 | $this->taxonomy,
699 | $objects,
700 | $options
701 | );
702 | }
703 |
704 | /**
705 | * Parse taxonomy labels
706 | *
707 | * @since 0.1.0
708 | *
709 | * @return array
710 | */
711 | protected function parse_labels() {
712 | return wp_parse_args( $this->labels, array(
713 | 'menu_name' => $this->tax_plural,
714 | 'name' => $this->tax_plural,
715 | 'singular_name' => $this->tax_singular,
716 | 'search_items' => sprintf( __( 'Search %s', 'wp-user-groups' ), $this->tax_plural ),
717 | 'popular_items' => sprintf( __( 'Popular %s', 'wp-user-groups' ), $this->tax_plural ),
718 | 'all_items' => sprintf( __( 'All %s', 'wp-user-groups' ), $this->tax_plural ),
719 | 'parent_item' => sprintf( __( 'Parent %s', 'wp-user-groups' ), $this->tax_singular ),
720 | 'parent_item_colon' => sprintf( __( 'Parent %s:', 'wp-user-groups' ), $this->tax_singular ),
721 | 'edit_item' => sprintf( __( 'Edit %s', 'wp-user-groups' ), $this->tax_singular ),
722 | 'view_item' => sprintf( __( 'View %s', 'wp-user-groups' ), $this->tax_singular ),
723 | 'update_item' => sprintf( __( 'Update %s', 'wp-user-groups' ), $this->tax_singular ),
724 | 'add_new_item' => sprintf( __( 'Add New %s', 'wp-user-groups' ), $this->tax_singular ),
725 | 'new_item_name' => sprintf( __( 'New %s Name', 'wp-user-groups' ), $this->tax_singular ),
726 | 'separate_items_with_commas' => sprintf( __( 'Separate %s with commas', 'wp-user-groups' ), $this->tax_plural_low ),
727 | 'add_or_remove_items' => sprintf( __( 'Add or remove %s', 'wp-user-groups' ), $this->tax_plural_low ),
728 | 'choose_from_most_used' => sprintf( __( 'Choose from most used %s', 'wp-user-groups' ), $this->tax_plural_low ),
729 | 'not_found' => sprintf( __( 'No %s found', 'wp-user-groups' ), $this->tax_plural_low ),
730 | 'no_item' => sprintf( __( 'No %s', 'wp-user-groups' ), $this->tax_singular ),
731 | 'no_items' => sprintf( __( 'No %s', 'wp-user-groups' ), $this->tax_plural_low )
732 | ) );
733 | }
734 |
735 | /**
736 | * Parse taxonomy capabilities
737 | *
738 | * @since 2.2.0
739 | *
740 | * @return array
741 | */
742 | protected function parse_caps() {
743 | return wp_parse_args( $this->caps, array(
744 | 'manage_terms' => 'list_users',
745 | 'edit_terms' => 'list_users',
746 | 'delete_terms' => 'list_users',
747 | 'assign_terms' => $this->is_managed()
748 | ? 'list_users'
749 | : 'read'
750 | ) );
751 | }
752 |
753 | /**
754 | * Parse taxonomy options
755 | *
756 | * @since 0.1.0
757 | *
758 | * @return array
759 | */
760 | protected function parse_options() {
761 | return wp_parse_args( $this->args, array(
762 |
763 | // Custom
764 | 'user_group' => true, // Make it easy to identify user groups
765 | 'exclusive' => false, // Check vs. Radio
766 |
767 | // Core
768 | 'hierarchical' => true,
769 | 'public' => false,
770 | 'show_ui' => true,
771 | 'meta_box_cb' => '',
772 | 'labels' => $this->parse_labels(),
773 | 'capabilities' => $this->parse_caps(),
774 | 'rewrite' => array(
775 | 'with_front' => false,
776 | 'slug' => $this->slug,
777 | 'hierarchical' => true
778 | ),
779 |
780 | // @see _update_post_term_count()
781 | 'update_count_callback' => array( $this, 'update_term_user_count' )
782 | ) );
783 | }
784 |
785 | /** Bulk Edit *************************************************************/
786 |
787 | /**
788 | * Add custom bulk actions
789 | *
790 | * @since 1.0.0
791 | *
792 | * @param array $actions
793 | *
794 | * @return array
795 | */
796 | public function bulk_actions( $actions = array() ) {
797 |
798 | // Get taxonomy & terms
799 | $tax = get_taxonomy( $this->taxonomy );
800 | $terms = get_terms( $this->taxonomy, array(
801 | 'hide_empty' => false
802 | ) );
803 |
804 | // Add to bulk actions array
805 | if ( ! empty( $terms ) ) {
806 | foreach ( $terms as $term ) {
807 | $actions[ "add-{$term->slug}-{$this->taxonomy}" ] = sprintf( esc_html__( 'Add to %s %s', 'wp-user-groups' ), $term->name, $tax->labels->singular_name );
808 | $actions[ "remove-{$term->slug}-{$this->taxonomy}" ] = sprintf( esc_html__( 'Remove from %s %s', 'wp-user-groups' ), $term->name, $tax->labels->singular_name );
809 | }
810 | }
811 |
812 | // Return actions, maybe with our bulks added
813 | return $actions;
814 | }
815 |
816 | /**
817 | * Group add/remove options together for improved UX
818 | *
819 | * @since 1.0.0
820 | *
821 | * @param array $actions
822 | */
823 | public function bulk_actions_sort( $actions = array() ) {
824 |
825 | // Actions array
826 | $old_actions = $add_actions = $rem_actions = array();
827 |
828 | // Loop through and separate out actions
829 | foreach ( $actions as $key => $name ) {
830 |
831 | // Add
832 | if ( 0 === strpos( $key, 'add-' ) ) {
833 | $add_actions[ $key ] = $name;
834 |
835 | // Remove
836 | } elseif ( 0 === strpos( $key, 'remove-' ) ) {
837 | $rem_actions[ $key ] = $name;
838 |
839 | // Old
840 | } else {
841 | $old_actions[ $key ] = $name;
842 | }
843 | }
844 |
845 | $new = array_merge( $old_actions, $add_actions, $rem_actions );
846 |
847 | return $new;
848 | }
849 |
850 | /**
851 | * Is this an exclusive user group type, where a user can only belong to one
852 | * group within the taxonomy?
853 | *
854 | * @since 2.0.0
855 | *
856 | * @return bool
857 | */
858 | public function is_exclusive() {
859 | return ! empty( $this->args['exclusive'] );
860 | }
861 |
862 | /**
863 | * Is this a managed user group type, where a user cannot assign their own
864 | * groups within the taxonomy?
865 | *
866 | * @since 2.2.0
867 | *
868 | * @return bool
869 | */
870 | public function is_managed() {
871 | if ( current_user_can('administrator') ) {
872 | return false;
873 | } else {
874 | return ! empty( $this->args['managed'] );
875 | }
876 | }
877 |
878 | /**
879 | * Handle bulk editing of users
880 | *
881 | * @since 1.0.0
882 | */
883 | public function handle_bulk_actions( $redirect_to = '', $action = '', $user_ids = array() ) {
884 |
885 | // Get terms
886 | $terms = get_terms( $this->taxonomy, array(
887 | 'hide_empty' => false
888 | ) );
889 |
890 | // Bail if no users or terms to work with
891 | if ( empty( $user_ids ) || empty( $terms ) ) {
892 | return $redirect_to;
893 | }
894 |
895 | // New actions array
896 | $actions = $changed_users = array();
897 |
898 | // Compile available actions
899 | foreach ( $terms as $term ) {
900 | $key = "{$term->slug}-{$this->taxonomy}";
901 | $actions[] = "add-{$key}";
902 | $actions[] = "remove-{$key}";
903 | }
904 |
905 | // Bail if not a supported bulk action
906 | if ( ! in_array( $action, $actions, true ) ) {
907 | return $redirect_to;
908 | }
909 |
910 | // Type & term
911 | $type = strstr( $action, '-', true );
912 | $term = str_replace( "{$type}-", '', $action );
913 | $term = str_replace( "-{$this->taxonomy}", '', $term );
914 |
915 | // Loop through users
916 | foreach ( $user_ids as $user_id ) {
917 |
918 | // Should we update this user's terms?
919 | $should_update = false;
920 |
921 | // Skip if current user cannot assign terms to this user for this taxonomy
922 | if ( ! $this->can_assign( $user_id ) ) {
923 | continue;
924 | }
925 |
926 | // Get term slugs of user for this taxonomy
927 | $terms = wp_get_terms_for_user( $user_id, $this->taxonomy );
928 | $update_terms = wp_list_pluck( $terms, 'slug' );
929 |
930 | // Adding
931 | if ( 'add' === $type ) {
932 | if ( ! in_array( $term, $update_terms, true ) ) {
933 | $update_terms[] = $term;
934 | $should_update = true;
935 | }
936 |
937 | // Removing
938 | } elseif ( 'remove' === $type ) {
939 |
940 | // Skip if nothing to remove
941 | if ( empty( $update_terms ) ) {
942 | continue;
943 | }
944 |
945 | // Check the terms for this one
946 | $index = array_search( $term, $update_terms );
947 | if ( ( false !== $index ) && isset( $update_terms[ $index ] ) ) {
948 | unset( $update_terms[ $index ] );
949 | $should_update = true;
950 | }
951 | }
952 |
953 | // Delete all groups if they're empty
954 | if ( empty( $update_terms ) ) {
955 | $update_terms = null;
956 | }
957 |
958 | // Update terms for users
959 | if ( ( $update_terms !== $terms ) && ( true === $should_update ) ) {
960 | $changed_users[] = $user_id;
961 | wp_set_terms_for_user( $user_id, $this->taxonomy, $update_terms, true );
962 | }
963 | }
964 |
965 | // Add count to redirection
966 | $redirect_to = add_query_arg( array(
967 | 'user_groups_count' => count( $changed_users ),
968 | 'action_type' => $type,
969 | 'term_slug' => $term,
970 | 'tax' => $this->taxonomy
971 | ), $redirect_to );
972 |
973 | // Return redirection
974 | return $redirect_to;
975 | }
976 |
977 | /**
978 | * Maybe output a notice when bulk actions occur
979 | *
980 | * @since 1.0.0
981 | *
982 | * @return void
983 | */
984 | public function bulk_notice() {
985 |
986 | // Bail if no count
987 | if ( ! isset( $_REQUEST['user_groups_count'] ) || empty( $_REQUEST['action_type'] ) || empty( $_REQUEST['tax'] ) ) {
988 | return;
989 | }
990 |
991 | // Get the changed count and sanitize a few keys
992 | $count = intval( $_REQUEST['user_groups_count'] );
993 | $action = sanitize_key( $_REQUEST['action_type'] );
994 | $group = sanitize_key( $_REQUEST['term_slug'] );
995 | $tax = sanitize_key( $_REQUEST['tax'] );
996 |
997 | // Bail if group is not for this taxonomy
998 | if ( $this->taxonomy !== $tax ) {
999 | return;
1000 | }
1001 |
1002 | // Get the labels
1003 | $tax = get_taxonomy( $this->taxonomy )->labels->singular_name;
1004 | $term = get_term_by( 'slug', $group, $this->taxonomy )->name;
1005 |
1006 | // Bail if term does not exist in taxonomy
1007 | if ( empty( $term ) ) {
1008 | return;
1009 | }
1010 |
1011 | // No users
1012 | if ( 0 === $count ) {
1013 | $type = 'warning';
1014 | $text = ( 'add' === $action )
1015 | ? sprintf( __( 'No users added to the "%s" %s.', 'wp-user-groups' ), $term, $tax )
1016 | : sprintf( __( 'No users removed from the "%s" %s.', 'wp-user-groups' ), $term, $tax );
1017 |
1018 | // Add/remove
1019 | } else {
1020 | $type = 'success';
1021 | $text = ( 'add' === $action )
1022 | ? sprintf( _n( '%s user added to the "%s" %s.', '%s users added to the "%s" %s.', $count, 'wp-user-groups' ), number_format_i18n( $count ), $term, $tax )
1023 | : sprintf( _n( '%s user removed from the "%s" %s.', '%s users removed from the "%s" %s.', $count, 'wp-user-groups' ), number_format_i18n( $count ), $term, $tax );
1024 | }
1025 |
1026 | // Output message
1027 | ?>taxonomy, array( 'hide_empty' => false ) );
1048 | $slugs = wp_list_pluck( $terms, 'slug' );
1049 | $current = isset( $_GET[ $this->taxonomy ] ) ? sanitize_key( $_GET[ $this->taxonomy ] ) : '';
1050 | $viewing = array_search( $current, $slugs, true );
1051 |
1052 | // Viewing a specific taxonomy term
1053 | if ( false !== $viewing ) {
1054 |
1055 | // Assemble the "Edit" h1 link
1056 | $edit = admin_url( 'edit-tags.php' );
1057 | $args = array(
1058 | 'action' => 'edit',
1059 | 'taxonomy' => $this->taxonomy,
1060 | 'tag_ID' => $terms[ $viewing ]->term_id,
1061 | );
1062 | $url = add_query_arg( $args, $edit ); ?>
1063 |
1064 |
1082 |
1083 |
1084 | taxonomy ] ) ) {
1109 | return;
1110 | }
1111 |
1112 | // Sanitize taxonomies
1113 | $groups = array_map( 'sanitize_key', explode( ',', $_GET[ $this->taxonomy ] ) );
1114 |
1115 | // Get terms
1116 | foreach ( $groups as $group ) {
1117 | $term = get_term_by( 'slug', $group, $this->taxonomy );
1118 | $user_ids = get_objects_in_term( $term->term_id, $this->taxonomy );
1119 | }
1120 |
1121 | // If no users are in this group, pass a 0 user ID
1122 | if ( empty( $user_ids ) ) {
1123 | $user_ids = array( 0 );
1124 | }
1125 |
1126 | // Set IDs to be included
1127 | $user_query->query_vars['include'] = $user_ids;
1128 | }
1129 |
1130 | /**
1131 | * Generated user taxonomy query SQL
1132 | *
1133 | * @since 0.1.0
1134 | *
1135 | * @param object $user_query
1136 | */
1137 | public function user_tax_query( $user_query = '' ) {
1138 | return get_tax_sql( $user_query->tax_query, $GLOBALS['wpdb']->users, 'ID' );
1139 | }
1140 |
1141 | /**
1142 | * Get links to user taxonomy terms
1143 | *
1144 | * @since 0.1.0
1145 | *
1146 | * @param mixed $user
1147 | * @param string $page
1148 | *
1149 | * @return string
1150 | */
1151 | private function get_user_term_links( $user, $page = null ) {
1152 |
1153 | // Get terms for user and this taxonomy
1154 | $terms = wp_get_terms_for_user( $user, $this->taxonomy );
1155 |
1156 | // Bail if user has no terms
1157 | if ( empty( $terms ) ) {
1158 | return false;
1159 | }
1160 |
1161 | $in = array();
1162 | $url = admin_url( 'users.php' );
1163 |
1164 | // Loop through terms
1165 | foreach ( $terms as $term ) {
1166 | $args = array( $this->taxonomy => $term->slug );
1167 | $href = empty( $page )
1168 | ? add_query_arg( $args, $url )
1169 | : add_query_arg( $args, $page );
1170 |
1171 | // Add link to array
1172 | $in[] = '' . esc_html( $term->name ) . '';
1173 | }
1174 |
1175 | return implode( ', ', $in );
1176 | }
1177 |
1178 | /**
1179 | * Add taxonomy links for a column
1180 | *
1181 | * @since 0.1.0
1182 | *
1183 | * @param string $value
1184 | * @param string $column_name
1185 | * @param string $user_id
1186 | * @return string
1187 | */
1188 | public function user_column_data( $value = '', $column_name = '', $user_id = 0 ) {
1189 |
1190 | // Only for this column name
1191 | if ( $column_name === $this->taxonomy ) {
1192 |
1193 | // Get term links
1194 | $links = $this->get_user_term_links( $user_id );
1195 |
1196 | // Use links
1197 | if ( ! empty( $links ) ) {
1198 | $value = $links;
1199 |
1200 | // No links
1201 | } else {
1202 | $value = '—';
1203 | }
1204 | }
1205 |
1206 | // Return possibly modified value
1207 | return $value;
1208 | }
1209 |
1210 | /**
1211 | * Add the label to the table header
1212 | *
1213 | * @since 0.1.0
1214 | *
1215 | * @param array $defaults
1216 | *
1217 | * @return array
1218 | */
1219 | public function add_manage_users_columns( $defaults = array() ) {
1220 |
1221 | // Get the taxonomy
1222 | $tax = get_taxonomy( $this->taxonomy );
1223 |
1224 | // Bail if no UI
1225 | if ( false === $tax->show_ui ) {
1226 | return $defaults;
1227 | }
1228 |
1229 | // Add the taxonomy
1230 | $defaults[ $this->taxonomy ] = $tax->labels->name;
1231 |
1232 | // Return columns
1233 | return $defaults;
1234 | }
1235 |
1236 | /** Nonce *****************************************************************/
1237 |
1238 | /**
1239 | * Return the concatenated nonce key
1240 | *
1241 | * @since 2.1.0
1242 | *
1243 | * @return string
1244 | */
1245 | private function get_nonce_key() {
1246 | return "wp_user_taxonomy_{$this->taxonomy}";
1247 | }
1248 |
1249 | /**
1250 | * Output the nonce field for this user taxonomy table
1251 | *
1252 | * @since 2.1.0
1253 | */
1254 | private function nonce_field() {
1255 | wp_nonce_field( $this->taxonomy, $this->get_nonce_key() );
1256 | }
1257 |
1258 | /**
1259 | * Try to verify the nonce for this use taxonomy
1260 | *
1261 | * @since 2.1.0
1262 | *
1263 | * @return boolean
1264 | */
1265 | private function verify_nonce() {
1266 |
1267 | // Nonce exists?
1268 | $retval = false;
1269 | $key = $this->get_nonce_key();
1270 | $nonce = isset( $_REQUEST[ $key ] )
1271 | ? $_REQUEST[ $key ]
1272 | : $retval;
1273 |
1274 | // Return true if nonce was verified
1275 | if ( ! empty( $nonce ) && wp_verify_nonce( $nonce, $this->taxonomy ) ) {
1276 | $retval = true;
1277 | }
1278 |
1279 | // Default return value
1280 | return $retval;
1281 | }
1282 |
1283 | /** Caps ******************************************************************/
1284 |
1285 | /**
1286 | * Whether the current user can assign terms to another user
1287 | *
1288 | * @since 2.2.0
1289 | *
1290 | * @param int $user_id
1291 | *
1292 | * @return boolean
1293 | */
1294 | private function can_assign( $user_id = 0 ) {
1295 |
1296 | // Default return value
1297 | $retval = false;
1298 |
1299 | // Get the taxonomy
1300 | $tax = get_taxonomy( $this->taxonomy );
1301 |
1302 | // Check edit_user and assign
1303 | if ( current_user_can( 'edit_user', $user_id ) && current_user_can( $tax->cap->assign_terms ) ) {
1304 | $retval = true;
1305 | }
1306 |
1307 | // Return
1308 | return (bool) $retval;
1309 | }
1310 | }
1311 | endif;
1312 |
--------------------------------------------------------------------------------