PluginProbe
Edit Flow / trunk
Edit Flow vtrunk
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
edit-flow / modules / user-groups / user-groups.php

user-groups.php in Edit Flow trunk, at modules/user-groups/user-groups.php

1,430 lines 54.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * User Groups module for Edit Flow.
4 *
5 * @package EditFlow
6 *
7 * @todo all of them PHPdocs
8 * @todo Resolve whether the notifications component of this class should be moved to "subscriptions"
9 * @todo Decide whether it's functional to store user_ids in the term description array
10 * - Argument against: it's going to be expensive to look up usergroups for a user
11 */
12
13 if ( ! class_exists( 'EF_User_Groups' ) ) {
14
15 /**
16 * User Groups module for Edit Flow.
17 */
18 class EF_User_Groups extends EF_Module {
19
20 /**
21 * The module instance.
22 *
23 * @var object
24 */
25 public $module;
26
27 /**
28 * Taxonomy key used for custom taxonomy.
29 *
30 * @var string
31 */
32 // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
33 const taxonomy_key = 'ef_usergroup';
34
35 /**
36 * Term prefix used for custom taxonomy terms.
37 *
38 * @var string
39 */
40 // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
41 const term_prefix = 'ef-usergroup-';
42
43 /**
44 * Capability required to manage user groups.
45 *
46 * @var string
47 */
48 public $manage_usergroups_cap = 'edit_usergroups';
49
50 /**
51 * Register the module with Edit Flow but don't do anything else.
52 *
53 * @since 0.7
54 */
55 public function __construct() {
56
57 $this->module_url = $this->get_module_url( __FILE__ );
58
59 // Register the User Groups module with Edit Flow.
60 $args = array(
61 'title' => __( 'User Groups', 'edit-flow' ),
62 'short_description' => __( 'Organize your users into groups to mimic your organizational structure.', 'edit-flow' ),
63 'extended_description' => __( 'Configure user groups to organize all of the users on your site. Each user can be in many user groups and you can change them at any time.', 'edit-flow' ),
64 'module_url' => $this->module_url,
65 'img_url' => $this->module_url . 'lib/usergroups_s128.png',
66 'slug' => 'user-groups',
67 'default_options' => array(
68 'enabled' => 'on',
69 'post_types' => array(
70 'post' => 'on',
71 'page' => 'off',
72 ),
73 ),
74 'messages' => array(
75 'usergroup-added' => __( 'User group created. Feel free to add users to the usergroup.', 'edit-flow' ),
76 'usergroup-updated' => __( 'User group updated.', 'edit-flow' ),
77 'usergroup-missing' => __( "User group doesn't exist.", 'edit-flow' ),
78 'usergroup-deleted' => __( 'User group deleted.', 'edit-flow' ),
79 ),
80 'configure_page_cb' => 'print_configure_view',
81 'configure_link_text' => __( 'Manage User Groups', 'edit-flow' ),
82 'autoload' => false,
83 'settings_help_tab' => array(
84 'id' => 'ef-user-groups-overview',
85 'title' => __( 'Overview', 'edit-flow' ),
86 'content' => __( '<p>For those with many people involved in the publishing process, user groups helps you keep them organized.</p><p>Currently, user groups are primarily used for subscribing a set of users to a post for notifications.</p>', 'edit-flow' ),
87 ),
88 'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="https://editflow.org/features/user-groups/">User Groups Documentation</a></p><p><a href="https://wordpress.org/support/plugin/edit-flow/">Edit Flow Forum</a></p><p><a href="https://github.com/Automattic/Edit-Flow">Edit Flow on GitHub</a></p>', 'edit-flow' ),
89 );
90 $this->module = EditFlow()->register_module( 'user_groups', $args );
91 }
92
93 /**
94 * Module startup
95 */
96
97 /**
98 * Initialize the rest of the stuff in the class if the module is active
99 *
100 * @since 0.7
101 */
102 public function init() {
103
104 // Register the objects where we'll be storing data and relationships.
105 $this->register_usergroup_objects();
106
107 $this->manage_usergroups_cap = apply_filters( 'ef_manage_usergroups_cap', $this->manage_usergroups_cap );
108
109 // Register our settings.
110 add_action( 'admin_init', array( $this, 'register_settings' ) );
111
112 // Handle any adding, editing or saving.
113 add_action( 'admin_init', array( $this, 'handle_add_usergroup' ) );
114 add_action( 'admin_init', array( $this, 'handle_edit_usergroup' ) );
115 add_action( 'admin_init', array( $this, 'handle_delete_usergroup' ) );
116 add_action( 'wp_ajax_inline_save_usergroup', array( $this, 'handle_ajax_inline_save_usergroup' ) );
117
118 // Usergroups can be managed from the User profile view.
119 add_action( 'show_user_profile', array( $this, 'user_profile_page' ) );
120 add_action( 'edit_user_profile', array( $this, 'user_profile_page' ) );
121 add_action( 'user_profile_update_errors', array( $this, 'user_profile_update' ), 10, 3 );
122
123 // Javascript and CSS if we need it.
124 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
125 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
126 }
127
128 /**
129 * Load the capabilities onto users the first time the module is run
130 *
131 * @since 0.7
132 */
133 public function install() {
134
135 // Add necessary capabilities to allow management of user groups.
136 $usergroup_roles = array(
137 'administrator' => array( 'edit_usergroups' ),
138 );
139 foreach ( $usergroup_roles as $role => $caps ) {
140 $this->add_caps_to_role( $role, $caps );
141 }
142
143 // Create our default usergroups.
144 $default_usergroups = array(
145 array(
146 'name' => __( 'Copy Editors', 'edit-flow' ),
147 'description' => __( 'Making sure the quality is top-notch.', 'edit-flow' ),
148 ),
149 array(
150 'name' => __( 'Photographers', 'edit-flow' ),
151 'description' => __( 'Capturing the story visually.', 'edit-flow' ),
152 ),
153 array(
154 'name' => __( 'Reporters', 'edit-flow' ),
155 'description' => __( 'Out in the field, writing stories.', 'edit-flow' ),
156 ),
157 array(
158 'name' => __( 'Section Editors', 'edit-flow' ),
159 'description' => __( 'Providing feedback and direction.', 'edit-flow' ),
160 ),
161 );
162 foreach ( $default_usergroups as $args ) {
163 $this->add_usergroup( $args );
164 }
165 }
166
167 /**
168 * Upgrade our data in case we need to.
169 *
170 * @since 0.7
171 *
172 * @param string $previous_version The previous plugin version.
173 */
174 public function upgrade( $previous_version ) {
175 global $edit_flow;
176
177 // Upgrade path to v0.7.
178 if ( version_compare( $previous_version, '0.7', '<' ) ) {
179 global $wpdb;
180
181 // Set all of the user group terms to our new taxonomy.
182 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-time upgrade query.
183 $wpdb->update( $wpdb->term_taxonomy, array( 'taxonomy' => self::taxonomy_key ), array( 'taxonomy' => 'following_usergroups' ) );
184
185 // Get all of the users who are a part of user groups and assign them to their new user group values.
186 $query = "SELECT * FROM $wpdb->usermeta WHERE meta_key='wp_ef_usergroups';";
187 // There's no userdata here in this query and it's an upgrade query.
188 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
189 $usergroup_users = $wpdb->get_results( $query );
190
191 // Sort all of the users based on their usergroup(s).
192 $users_to_add = array();
193 foreach ( (array) $usergroup_users as $usergroup_user ) {
194 if ( is_object( $usergroup_user ) ) {
195 $users_to_add[ $usergroup_user->meta_value ][] = (int) $usergroup_user->user_id;
196 }
197 }
198 // Add user IDs to each usergroup.
199 foreach ( $users_to_add as $usergroup_slug => $users_array ) {
200 $usergroup = $this->get_usergroup_by( 'slug', $usergroup_slug );
201 $this->add_users_to_usergroup( $users_array, $usergroup->term_id );
202 }
203 // Update the term slugs for each user group.
204 $all_usergroups = $this->get_usergroups();
205 foreach ( $all_usergroups as $usergroup ) {
206 $new_slug = str_replace( 'ef_', self::term_prefix, $usergroup->slug );
207 $this->update_usergroup( $usergroup->term_id, array( 'slug' => $new_slug ) );
208 }
209
210 // Delete all of the previous usermeta values.
211 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-time upgrade query.
212 $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key='wp_ef_usergroups';" );
213
214 // Technically we've run this code before so we don't want to auto-install new data.
215 $edit_flow->update_module_option( $this->module->name, 'loaded_once', true );
216 }
217 // Upgrade path to v0.7.4.
218 if ( version_compare( $previous_version, '0.7.4', '<' ) ) {
219 // Usergroup descriptions become base64_encoded, instead of maybe json_encoded.
220 $this->upgrade_074_term_descriptions( self::taxonomy_key );
221 }
222 }
223
224 /**
225 * Register usergroup objects.
226 *
227 * Individual Usergroups are stored using a custom taxonomy.
228 * Posts are associated with usergroups based on taxonomy relationship.
229 * User associations are stored serialized in the term's description field.
230 *
231 * @since 0.7
232 *
233 * @uses register_taxonomy()
234 */
235 public function register_usergroup_objects() {
236
237 // Load the currently supported post types so we only register against those.
238 $supported_post_types = $this->get_post_types_for_module( $this->module );
239
240 // Use a taxonomy to manage relationships between posts and usergroups.
241 $args = array(
242 'public' => false,
243 'rewrite' => false,
244 );
245 register_taxonomy( self::taxonomy_key, $supported_post_types, $args );
246 }
247
248 /**
249 * Enqueue necessary admin scripts
250 *
251 * @since 0.7
252 *
253 * @uses wp_enqueue_script()
254 */
255 public function enqueue_admin_scripts() {
256
257 if ( $this->is_post_management_page( $this->module->name ) || $this->is_whitelisted_settings_view( $this->module->name ) ) {
258 wp_enqueue_script( 'jquery-listfilterizer' );
259 wp_enqueue_script( 'edit-flow-user-groups-js', $this->module_url . 'lib/user-groups.js', array( 'jquery', 'jquery-listfilterizer' ), EDIT_FLOW_VERSION, true );
260 }
261
262 if ( $this->is_whitelisted_settings_view( $this->module->name ) ) {
263 wp_enqueue_script( 'edit-flow-user-groups-configure-js', $this->module_url . 'lib/user-groups-configure.js', array( 'jquery' ), EDIT_FLOW_VERSION, true );
264 }
265 }
266
267 /**
268 * Enqueue necessary admin styles, but only on the proper pages
269 *
270 * @since 0.7
271 *
272 * @uses wp_enqueue_style()
273 */
274 public function enqueue_admin_styles() {
275
276
277 if ( $this->is_post_management_page( $this->module->name ) || $this->is_whitelisted_settings_view( $this->module->name ) ) {
278 wp_enqueue_style( 'jquery-listfilterizer' );
279 wp_enqueue_style( 'edit-flow-user-groups-css', $this->module_url . 'lib/user-groups.css', false, EDIT_FLOW_VERSION );
280 }
281 }
282
283 /**
284 * Module ???
285 */
286
287 /**
288 * Handles a POST request to add a new Usergroup. Redirects to edit view after
289 * for admin to add users to usergroup
290 * Hooked into 'admin_init' and kicks out right away if no action
291 *
292 * @since 0.7
293 */
294 public function handle_add_usergroup() {
295 // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- Nonce verified below.
296 if ( ! isset( $_POST['submit'], $_POST['form-action'], $_GET['page'] )
297 || $_GET['page'] != $this->module->settings_slug || 'add-usergroup' != $_POST['form-action'] ) {
298 return;
299 }
300 // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
301
302 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce().
303 if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'add-usergroup' ) ) {
304 wp_die( esc_html( $this->module->messages['nonce-failed'] ) );
305 }
306
307 if ( ! current_user_can( $this->manage_usergroups_cap ) ) {
308 wp_die( esc_html( $this->module->messages['invalid-permissions'] ) );
309 }
310
311 // Sanitize all of the user-entered values.
312 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized below.
313 $name = ( isset( $_POST['name'] ) ) ? sanitize_text_field( trim( $_POST['name'] ) ) : '';
314 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized below.
315 $description = ( isset( $_POST['description'] ) ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['description'] ) ) ) : '';
316
317 EditFlow()->settings->form_errors = array();
318
319 /*
320 * Form validation for adding new Usergroup.
321 *
322 * Details:
323 * - 'name' is a required field, but can't match an existing name or slug. Needs to be 40 characters or less.
324 * - "description" can accept a limited amount of HTML, and is optional.
325 */
326 // Field is required.
327 if ( empty( $name ) ) {
328 EditFlow()->settings->form_errors['name'] = __( 'Please enter a name for the user group.', 'edit-flow' );
329 }
330 // Check to ensure a term with the same name doesn't exist.
331 if ( $this->get_usergroup_by( 'name', $name ) ) {
332 EditFlow()->settings->form_errors['name'] = __( 'Name already in use. Please choose another.', 'edit-flow' );
333 }
334 // Check to ensure a term with the same slug doesn't exist.
335 if ( $this->get_usergroup_by( 'slug', sanitize_title( $name ) ) ) {
336 EditFlow()->settings->form_errors['name'] = __( 'Name conflicts with slug for another term. Please choose again.', 'edit-flow' );
337 }
338 if ( strlen( $name ) > 40 ) {
339 EditFlow()->settings->form_errors['name'] = __( 'User group name cannot exceed 40 characters. Please try a shorter name.', 'edit-flow' );
340 }
341 // Kick out if there are any errors.
342 if ( count( EditFlow()->settings->form_errors ) ) {
343 $_REQUEST['error'] = 'form-error';
344 return;
345 }
346
347 // Try to add the Usergroup.
348 $args = array(
349 'name' => $name,
350 'description' => $description,
351 );
352 $usergroup = $this->add_usergroup( $args );
353 if ( is_wp_error( $usergroup ) ) {
354 wp_die( esc_html__( 'Error adding usergroup.', 'edit-flow' ) );
355 }
356
357 $args = array(
358 'action' => 'edit-usergroup',
359 'usergroup-id' => $usergroup->term_id,
360 'message' => 'usergroup-added',
361 );
362 $redirect_url = $this->get_link( $args );
363 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally.
364 wp_redirect( $redirect_url );
365 exit;
366 }
367
368 /**
369 * Handles a POST request to edit a Usergroup.
370 *
371 * Hooked into 'admin_init' and kicks out right away if no action.
372 *
373 * @since 0.7
374 */
375 public function handle_edit_usergroup() {
376 // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- Nonce verified below.
377 if ( ! isset( $_POST['submit'], $_POST['form-action'], $_GET['page'] )
378 || $_GET['page'] != $this->module->settings_slug || 'edit-usergroup' != $_POST['form-action'] ) {
379 return;
380 }
381 // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
382
383 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce().
384 if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'edit-usergroup' ) ) {
385 wp_die( esc_html( $this->module->messages['nonce-failed'] ) );
386 }
387
388 if ( ! current_user_can( $this->manage_usergroups_cap ) ) {
389 wp_die( esc_html( $this->module->messages['invalid-permissions'] ) );
390 }
391
392 $usergroup_id = isset( $_POST['usergroup_id'] ) ? (int) $_POST['usergroup_id'] : 0;
393 $existing_usergroup = $this->get_usergroup_by( 'id', $usergroup_id );
394 if ( ! $existing_usergroup ) {
395 wp_die( esc_html( $this->module->messages['usergroup-missing'] ) );
396 }
397
398 // Sanitize all of the user-entered values.
399 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized below.
400 $name = isset( $_POST['name'] ) ? sanitize_text_field( trim( $_POST['name'] ) ) : '';
401 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized below.
402 $description = isset( $_POST['description'] ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['description'] ) ) ) : '';
403
404 EditFlow()->settings->form_errors = array();
405
406 /*
407 * Form validation for editing a Usergroup.
408 *
409 * Details:
410 * - 'name' is a required field, but can't match an existing name or slug. Needs to be 40 characters or less.
411 * - "description" can accept a limited amount of HTML, and is optional.
412 */
413 // Field is required.
414 if ( empty( $name ) ) {
415 EditFlow()->settings->form_errors['name'] = __( 'Please enter a name for the user group.', 'edit-flow' );
416 }
417 // Check to ensure a term with the same name doesn't exist.
418 $search_term = $this->get_usergroup_by( 'name', $name );
419 if ( is_object( $search_term ) && (int) $search_term->term_id !== (int) $existing_usergroup->term_id ) {
420 EditFlow()->settings->form_errors['name'] = __( 'Name already in use. Please choose another.', 'edit-flow' );
421 }
422 // Check to ensure a term with the same slug doesn't exist.
423 $search_term = $this->get_usergroup_by( 'slug', sanitize_title( $name ) );
424 if ( is_object( $search_term ) && (int) $search_term->term_id !== (int) $existing_usergroup->term_id ) {
425 EditFlow()->settings->form_errors['name'] = __( 'Name conflicts with slug for another term. Please choose again.', 'edit-flow' );
426 }
427 if ( strlen( $name ) > 40 ) {
428 EditFlow()->settings->form_errors['name'] = __( 'User group name cannot exceed 40 characters. Please try a shorter name.', 'edit-flow' );
429 }
430 // Kick out if there are any errors.
431 if ( count( EditFlow()->settings->form_errors ) ) {
432 $_REQUEST['error'] = 'form-error';
433 return;
434 }
435
436 // Try to edit the Usergroup.
437 $args = array(
438 'name' => $name,
439 'description' => $description,
440 );
441 // Gracefully handle the case where all users have been unsubscribed from the user group.
442 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized with intval.
443 $users = isset( $_POST['usergroup_users'] ) ? (array) $_POST['usergroup_users'] : array();
444 $users = array_map( 'intval', $users );
445 $usergroup = $this->update_usergroup( $existing_usergroup->term_id, $args, $users );
446 if ( is_wp_error( $usergroup ) ) {
447 wp_die( esc_html__( 'Error updating user group.', 'edit-flow' ) );
448 }
449
450 $args = array(
451 'message' => 'usergroup-updated',
452 );
453 $redirect_url = $this->get_link( $args );
454 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally.
455 wp_redirect( $redirect_url );
456 exit;
457 }
458
459 /**
460 * Handles a request to delete a Usergroup.
461 *
462 * Hooked into 'admin_init' and kicks out right away if no action.
463 *
464 * @since 0.7
465 */
466 public function handle_delete_usergroup() {
467 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified below.
468 if ( ! isset( $_GET['page'], $_GET['action'], $_GET['usergroup-id'] )
469 || $_GET['page'] != $this->module->settings_slug || 'delete-usergroup' != $_GET['action'] ) {
470 return;
471 }
472 // phpcs:enable WordPress.Security.NonceVerification.Recommended
473
474 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce().
475 if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'delete-usergroup' ) ) {
476 wp_die( esc_html( $this->module->messages['nonce-failed'] ) );
477 }
478
479 if ( ! current_user_can( $this->manage_usergroups_cap ) ) {
480 wp_die( esc_html( $this->module->messages['invalid-permissions'] ) );
481 }
482
483 $result = $this->delete_usergroup( (int) $_GET['usergroup-id'] );
484 if ( ! $result || is_wp_error( $result ) ) {
485 wp_die( esc_html__( 'Error deleting user group.', 'edit-flow' ) );
486 }
487
488 $redirect_url = $this->get_link( array( 'message' => 'usergroup-deleted' ) );
489 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally.
490 wp_redirect( $redirect_url );
491 exit;
492 }
493
494 /**
495 * Handle the request to update a given Usergroup via inline edit.
496 *
497 * @since 0.7
498 */
499 public function handle_ajax_inline_save_usergroup() {
500 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce().
501 if ( ! isset( $_POST['inline_edit'] ) || ! wp_verify_nonce( $_POST['inline_edit'], 'usergroups-inline-edit-nonce' ) ) {
502 wp_die( esc_html( $this->module->messages['nonce-failed'] ) );
503 }
504
505 if ( ! current_user_can( $this->manage_usergroups_cap ) ) {
506 wp_die( esc_html( $this->module->messages['invalid-permissions'] ) );
507 }
508
509 $usergroup_id = isset( $_POST['usergroup_id'] ) ? (int) $_POST['usergroup_id'] : 0;
510 $existing_term = $this->get_usergroup_by( 'id', $usergroup_id );
511 if ( ! $existing_term ) {
512 wp_die( esc_html( $this->module->messages['usergroup-missing'] ) );
513 }
514
515 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized below.
516 $name = isset( $_POST['name'] ) ? sanitize_text_field( trim( $_POST['name'] ) ) : '';
517 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPressVIPMinimum.Functions.StripTagsRegistry.strip_tags -- Values are sanitized.
518 $description = isset( $_POST['description'] ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['description'] ) ) ) : '';
519
520 // Form validation for editing Usergroup.
521 // Check if name field was filled in.
522 if ( empty( $name ) ) {
523 $change_error = new WP_Error( 'invalid', esc_html__( 'Please enter a name for the user group.', 'edit-flow' ) );
524 wp_die( esc_html( $change_error->get_error_message() ) );
525 }
526 // Check that the name doesn't exceed 40 chars.
527 if ( strlen( $name ) > 40 ) {
528 $change_error = new WP_Error( 'invalid', esc_html__( 'User group name cannot exceed 40 characters. Please try a shorter name.', 'edit-flow' ) );
529 wp_die( esc_html( $change_error->get_error_message() ) );
530 }
531 // Check to ensure a term with the same name doesn't exist.
532 $search_term = $this->get_usergroup_by( 'name', $name );
533 if ( is_object( $search_term ) && (int) $search_term->term_id !== (int) $existing_term->term_id ) {
534 $change_error = new WP_Error( 'invalid', esc_html__( 'Name already in use. Please choose another.', 'edit-flow' ) );
535 wp_die( esc_html( $change_error->get_error_message() ) );
536 }
537 // Check to ensure a term with the same slug doesn't exist.
538 $search_term = $this->get_usergroup_by( 'slug', sanitize_title( $name ) );
539 if ( is_object( $search_term ) && (int) $search_term->term_id !== (int) $existing_term->term_id ) {
540 $change_error = new WP_Error( 'invalid', esc_html__( 'Name conflicts with slug for another term. Please choose again.', 'edit-flow' ) );
541 wp_die( esc_html( $change_error->get_error_message() ) );
542 }
543
544 // Prepare the term name and description for saving.
545 $args = array(
546 'name' => $name,
547 'description' => $description,
548 );
549 $return = $this->update_usergroup( $existing_term->term_id, $args );
550 if ( ! is_wp_error( $return ) ) {
551 set_current_screen( 'edit-usergroup' );
552 $wp_list_table = new EF_Usergroups_List_Table();
553 $wp_list_table->prepare_items();
554 // single_row() echoes its own output; column_* callbacks are
555 // responsible for escaping, as per WP_List_Table's contract.
556 $wp_list_table->single_row( $return );
557 wp_die();
558 } else {
559 // translators: %s is the name of the user group.
560 $change_error = new WP_Error( 'invalid', sprintf( __( 'Could not update the user group: <strong>%s</strong>', 'edit-flow' ), esc_html( $name ) ) );
561 wp_die( wp_kses( $change_error->get_error_message(), array( 'strong' => array() ) ) );
562 }
563 }
564
565 /**
566 * Register settings for notifications so we can partially use the Settings API.
567 *
568 * We use the Settings API for form generation, but not saving.
569 *
570 * @since 0.7
571 * @uses add_settings_section(), add_settings_field()
572 */
573 public function register_settings() {
574 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
575 add_settings_field( 'post_types', __( 'Add to these post types:', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
576 }
577
578 /**
579 * Choose the post types for Usergroups.
580 *
581 * @since 0.7
582 */
583 public function settings_post_types_option() {
584 global $edit_flow;
585 $edit_flow->settings->helper_option_custom_post_type( $this->module );
586 }
587
588 /**
589 * Validate data entered by the user.
590 *
591 * @since 0.7
592 *
593 * @param array $new_options New values that have been entered by the user.
594 * @return array Form values after they've been sanitized.
595 */
596 public function settings_validate( $new_options ) {
597
598
599 // Whitelist validation for the post type options.
600 if ( ! isset( $new_options['post_types'] ) ) {
601 $new_options['post_types'] = array();
602 }
603 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
604
605 return $new_options;
606 }
607
608 /**
609 * Build a configuration view so we can manage our usergroups.
610 *
611 * @since 0.7
612 */
613 public function print_configure_view() {
614 global $edit_flow;
615
616 // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended -- Nonce verification happens in handler functions, this is just rendering.
617 if ( isset( $_GET['action'], $_GET['usergroup-id'] ) && 'edit-usergroup' == $_GET['action'] ) :
618 // Full page width view for editing a given usergroup.
619 // Check whether the usergroup exists.
620 $usergroup_id = (int) $_GET['usergroup-id'];
621 $usergroup = $this->get_usergroup_by( 'id', $usergroup_id );
622 if ( ! $usergroup ) {
623 echo '<div class="error"><p>' . esc_html( $this->module->messages['usergroup-missing'] ) . '</p></div>';
624 return;
625 }
626 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Value is sanitized via stripslashes.
627 $name = ( isset( $_POST['name'] ) ) ? stripslashes( $_POST['name'] ) : $usergroup->name;
628 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized with wp_strip_all_tags.
629 $description = ( isset( $_POST['description'] ) ) ? wp_strip_all_tags( stripslashes( $_POST['description'] ) ) : $usergroup->description;
630 ?>
631 <form method="post" action="
632 <?php
633 echo esc_url( $this->get_link( array(
634 'action' => 'edit-usergroup',
635 'usergroup-id' => $usergroup_id,
636 ) ) );
637 ?>
638 ">
639 <div id="col-right"><div class="col-wrap"><div id="ef-usergroup-users" class="form-wrap">
640 <h4><?php _e( 'Users', 'edit-flow' ); ?></h4>
641 <?php
642 $select_form_args = array(
643 'list_class' => 'ef-post_following_list',
644 'input_id' => 'usergroup_users',
645 );
646 ?>
647 <?php $this->users_select_form( $usergroup->user_ids, $select_form_args ); ?>
648 </div></div></div>
649 <div id="col-left"><div class="col-wrap"><div class="form-wrap">
650 <input type="hidden" name="form-action" value="edit-usergroup" />
651 <input type="hidden" name="usergroup_id" value="<?php echo esc_attr( $usergroup_id ); ?>" />
652 <?php
653 wp_original_referer_field();
654 wp_nonce_field( 'edit-usergroup' );
655 ?>
656 <div class="form-field form-required">
657 <label for="name"><?php _e( 'Name', 'edit-flow' ); ?></label>
658 <input name="name" id="name" type="text" value="<?php echo esc_attr( $name ); ?>" size="40" maxlength="40" aria-required="true" />
659 <?php $edit_flow->settings->helper_print_error_or_description( 'name', __( 'The name is used to identify the user group.', 'edit-flow' ) ); ?>
660 </div>
661 <div class="form-field">
662 <label for="description"><?php _e( 'Description', 'edit-flow' ); ?></label>
663 <textarea name="description" id="description" rows="5" cols="40"><?php echo esc_html( $description ); ?></textarea>
664 <?php $edit_flow->settings->helper_print_error_or_description( 'description', __( 'The description is primarily for administrative use, to give you some context on what the user group is to be used for.', 'edit-flow' ) ); ?>
665 </div>
666 <p class="submit">
667 <?php submit_button( __( 'Update User Group', 'edit-flow' ), 'primary', 'submit', false ); ?>
668 <a class="cancel-settings-link" href="<?php echo esc_url( $this->get_link() ); ?>"><?php _e( 'Cancel', 'edit-flow' ); ?></a>
669 </p>
670 </div></div></div>
671 </form>
672
673 <?php
674 else :
675 // Full page width view to allow adding a usergroup and edit the existing ones.
676 $wp_list_table = new EF_Usergroups_List_Table();
677 $wp_list_table->prepare_items();
678 ?>
679 <script type="text/javascript">
680 var ef_confirm_delete_usergroup_string = "<?php _e( 'Are you sure you want to delete the user group?', 'edit-flow' ); ?>";
681 </script>
682 <div id="col-right"><div class="col-wrap">
683 <?php $wp_list_table->display(); ?>
684 </div></div>
685 <div id="col-left"><div class="col-wrap"><div class="form-wrap">
686 <h3 class="nav-tab-wrapper">
687 <?php $add_new_nav_class = ! isset( $_GET['action'] ) || 'change-options' != $_GET['action'] ? 'nav-tab-active' : ''; ?>
688 <a href="<?php echo esc_url( $this->get_link() ); ?>" class="nav-tab <?php echo esc_attr( $add_new_nav_class ); ?>"><?php esc_html_e( 'Add New', 'edit-flow' ); ?></a>
689 <?php $options_nav_class = isset( $_GET['action'] ) && 'change-options' == $_GET['action'] ? 'nav-tab-active' : ''; ?>
690 <a href="<?php echo esc_url( $this->get_link( array( 'action' => 'change-options' ) ) ); ?>" class="nav-tab <?php echo esc_attr( $options_nav_class ); ?>"><?php esc_html_e( 'Options', 'edit-flow' ); ?></a>
691 </h3>
692 <?php if ( isset( $_GET['action'] ) && 'change-options' == $_GET['action'] ) : ?>
693 <form class="basic-settings" action="<?php echo esc_url( $this->get_link( array( 'action' => 'change-options' ) ) ); ?>" method="post">
694 <?php settings_fields( $this->module->options_group_name ); ?>
695 <?php do_settings_sections( $this->module->options_group_name ); ?>
696 <input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="<?php echo esc_attr( $this->module->name ); ?>" />
697 <?php submit_button(); ?>
698 </form>
699 <?php else : ?>
700 <?php // Custom form for adding a new Usergroup. ?>
701 <form class="add:the-list:" action="<?php echo esc_url( $this->get_link() ); ?>" method="post" id="addusergroup" name="addusergroup">
702 <div class="form-field form-required">
703 <label for="name"><?php _e( 'Name', 'edit-flow' ); ?></label>
704 <?php // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Value is escaped with esc_attr. ?>
705 <input type="text" aria-required="true" id="name" name="name" maxlength="40" value="<?php echo ( empty( $_POST['name'] ) ? '' : esc_attr( wp_unslash( $_POST['name'] ) ) ); ?>"/>
706 <?php $edit_flow->settings->helper_print_error_or_description( 'name', __( 'The name is used to identify the user group.', 'edit-flow' ) ); ?>
707 </div>
708 <div class="form-field">
709 <label for="description"><?php _e( 'Description', 'edit-flow' ); ?></label>
710 <textarea cols="40" rows="5" id="description" name="description"><?php echo ( empty( $_POST['description'] ) ? '' : esc_textarea( sanitize_textarea_field( wp_unslash( $_POST['description'] ) ) ) ); ?></textarea>
711 <?php $edit_flow->settings->helper_print_error_or_description( 'description', __( 'The description is primarily for administrative use, to give you some context on what the user group is to be used for.', 'edit-flow' ) ); ?>
712 </div>
713 <?php wp_nonce_field( 'add-usergroup' ); ?>
714 <input id="form-action" name="form-action" type="hidden" value="add-usergroup" />
715 <?php submit_button( __( 'Add New User Group', 'edit-flow' ) ); ?>
716 </form>
717 <?php endif; ?>
718 </div></div></div>
719 <?php $wp_list_table->inline_edit(); ?>
720 <?php
721 endif;
722 // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended
723 }
724
725 /**
726 * Adds a form to the user profile page to allow adding usergroup selecting options.
727 */
728 public function user_profile_page() {
729 global $user_id, $profileuser;
730
731 if ( ! $user_id || ! current_user_can( $this->manage_usergroups_cap ) ) {
732 return;
733 }
734
735 // Don't allow display of user groups from network.
736 if ( ( ! is_null( get_current_screen() ) ) && ( get_current_screen()->is_network ) ) {
737 return;
738 }
739
740 // Assemble all necessary data.
741 $usergroups = $this->get_usergroups();
742 $selected_usergroups = $this->get_usergroups_for_user( $user_id );
743 $usergroups_form_args = array( 'input_id' => 'ef_usergroups' );
744 ?>
745 <table id="ef-user-usergroups" class="form-table"><tbody><tr>
746 <th>
747 <h3><?php _e( 'Usergroups', 'edit-flow' ); ?></h3>
748 <?php if ( wp_get_current_user()->ID === $user_id ) : ?>
749 <p><?php _e( 'Select the user groups that you would like to be a part of:', 'edit-flow' ); ?></p>
750 <?php else : ?>
751 <p><?php _e( 'Select the user groups that this user should be a part of:', 'edit-flow' ); ?></p>
752 <?php endif; ?>
753 </th>
754 <td>
755 <?php $this->usergroups_select_form( $selected_usergroups, $usergroups_form_args ); ?>
756 <script type="text/javascript">
757 jQuery(document).ready(function(){
758 jQuery('#ef-user-usergroups ul').listFilterizer();
759 });
760 </script>
761 </td>
762 </tr></tbody></table>
763 <?php wp_nonce_field( 'ef_edit_profile_usergroups_nonce', 'ef_edit_profile_usergroups_nonce' ); ?>
764 <?php
765 }
766
767 /**
768 * Function called when a user's profile is updated.
769 *
770 * Adds user to specified usergroups.
771 *
772 * @since 0.7
773 *
774 * @param WP_Error $errors Error object for validation errors.
775 * @param bool $update Whether this is a user update.
776 * @param WP_User $user The user object being updated.
777 * @return array The errors, update flag, and user.
778 */
779 public function user_profile_update( $errors, $update, $user ) {
780
781 if ( ! $update ) {
782 return array( &$errors, $update, &$user );
783 }
784
785 // `get_current_screen()` is defined on most admin pages, but not all.
786 if ( function_exists( 'get_current_screen' ) ) {
787 // Don't allow update of user groups from network.
788 $screen = get_current_screen();
789 if ( ! is_null( $screen ) && $screen->is_network ) {
790 return;
791 }
792 }
793
794 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce().
795 if ( isset( $_POST['ef_edit_profile_usergroups_nonce'] ) && current_user_can( $this->manage_usergroups_cap ) && wp_verify_nonce( $_POST['ef_edit_profile_usergroups_nonce'], 'ef_edit_profile_usergroups_nonce' ) ) {
796 // Sanitize the data and save.
797 // Gracefully handle the case where the user was unsubscribed from all usergroups.
798 $usergroups = isset( $_POST['ef_usergroups'] ) ? array_map( 'intval', (array) $_POST['ef_usergroups'] ) : array();
799 $all_usergroups = $this->get_usergroups();
800 foreach ( $all_usergroups as $usergroup ) {
801 if ( in_array( (int) $usergroup->term_id, $usergroups, true ) ) {
802 $this->add_user_to_usergroup( $user->ID, $usergroup->term_id );
803 } else {
804 $this->remove_user_from_usergroup( $user->ID, $usergroup->term_id );
805 }
806 }
807 }
808
809 return array( &$errors, $update, &$user );
810 }
811
812 /**
813 * Generate a link to one of the usergroups actions.
814 *
815 * @since 0.7
816 *
817 * @param array $args Any query args to add to the URL.
818 * @return string Direct link to usergroup action.
819 */
820 public function get_link( $args = array() ) {
821 if ( ! isset( $args['action'] ) ) {
822 $args['action'] = '';
823 }
824 if ( ! isset( $args['page'] ) ) {
825 $args['page'] = $this->module->settings_slug;
826 }
827 // Add other things we may need depending on the action.
828 switch ( $args['action'] ) {
829 case 'delete-usergroup':
830 $args['nonce'] = wp_create_nonce( $args['action'] );
831 break;
832 default:
833 break;
834 }
835 return add_query_arg( $args, get_admin_url( null, 'admin.php' ) );
836 }
837
838 /**
839 * Displays a list of usergroups with checkboxes.
840 *
841 * @since 0.7
842 *
843 * @param array $selected List of usergroup keys that should be checked.
844 * @param array $args Optional arguments for the form.
845 */
846 public function usergroups_select_form( $selected = array(), $args = null ) {
847
848 // @todo Add $args for additional options, e.g. showing members assigned to group,
849 // before/after tags, class names, id names, etc.
850 $defaults = array(
851 'list_class' => 'ef-post_following_list',
852 'list_id' => 'ef-following_usergroups',
853 'input_id' => 'following_usergroups',
854 );
855
856 $parsed_args = wp_parse_args( $args, $defaults );
857 $list_class = $parsed_args['list_class'];
858 $list_id = $parsed_args['list_id'];
859 $input_id = $parsed_args['input_id'];
860 $usergroups = $this->get_usergroups();
861 if ( empty( $usergroups ) ) {
862 ?>
863 <p><?php esc_html_e( 'No user groups were found.', 'edit-flow' ); ?> <a href="<?php echo esc_url( $this->get_link() ); ?>" title="<?php esc_attr_e( 'Add a new user group. Opens new window.', 'edit-flow' ); ?>" target="_blank"><?php esc_html_e( 'Add a User Group', 'edit-flow' ); ?></a></p>
864 <?php
865 } else {
866
867 ?>
868 <ul id="<?php echo esc_attr( $list_id ); ?>" class="<?php echo esc_attr( $list_class ); ?>">
869 <?php
870 foreach ( $usergroups as $usergroup ) {
871 ?>
872 <li>
873 <label for="<?php echo esc_attr( $input_id ) . esc_attr( $usergroup->term_id ); ?>" title="<?php echo esc_attr( $usergroup->description ); ?>">
874 <div class="ef-user-subscribe-actions">
875 <input type="checkbox" id="<?php echo esc_attr( $input_id . $usergroup->term_id ); ?>" name="<?php echo esc_attr( $input_id ); ?>[]" value="<?php echo esc_attr( $usergroup->term_id ); ?>"<?php echo checked( in_array( (int) $usergroup->term_id, array_map( 'intval', (array) $selected ), true ) ); ?> />
876 </div>
877 <span class="ef-usergroup_name"><?php echo esc_html( $usergroup->name ); ?></span>
878 <span class="ef-usergroup_description" title="<?php echo esc_attr( $usergroup->description ); ?>">
879 <?php echo esc_html( wp_html_excerpt( $usergroup->description, 50, '...' ) ); ?>
880 </span>
881 </label>
882 </li>
883 <?php
884 }
885 ?>
886 </ul>
887 <?php
888 }
889 }
890
891 /**
892 * Core Usergroups Module Functionality
893 */
894
895 /**
896 * Get all of the registered usergroups. Returns an array of objects.
897 *
898 * @since 0.7
899 *
900 * @param array $args Arguments to filter/sort by.
901 * @return array|bool $usergroups Array of Usergroups with relevant data, false if none.
902 */
903 public function get_usergroups( $args = array() ) {
904
905 // We want empty terms by default.
906 $usergroup_terms = get_terms( array(
907 'taxonomy' => self::taxonomy_key,
908 'hide_empty' => isset( $args['hide_empty'] ),
909 ));
910 if ( ! $usergroup_terms ) {
911 return false;
912 }
913
914 // Run the usergroups through get_usergroup_by() so we load users too.
915 $usergroups = array();
916 foreach ( $usergroup_terms as $usergroup_term ) {
917 $usergroups[] = $this->get_usergroup_by( 'id', $usergroup_term->term_id );
918 }
919 return $usergroups;
920 }
921
922 /**
923 * Get all of the data associated with a single usergroup.
924 *
925 * Usergroup contains:
926 * - ID (key = term_id)
927 * - Slug (prefixed with our special key to avoid conflicts)
928 * - Name
929 * - Description
930 * - User IDs (array of IDs)
931 *
932 * @since 0.7
933 *
934 * @param string $field 'id', 'name', or 'slug'.
935 * @param int|string $value Value for the search field.
936 * @return object|array|WP_Error $usergroup Usergroup information as specified by $output.
937 */
938 public function get_usergroup_by( $field, $value ) {
939
940 $usergroup = get_term_by( $field, $value, self::taxonomy_key );
941
942 if ( ! $usergroup || is_wp_error( $usergroup ) ) {
943 return $usergroup;
944 }
945
946 // We store extra values in an encoded description field.
947 // Initialize the users array in case it's empty in the stored data.
948 $usergroup->user_ids = array();
949 $unencoded_description = $this->get_unencoded_description( $usergroup->description );
950 if ( is_array( $unencoded_description ) ) {
951 foreach ( $unencoded_description as $key => $value ) {
952 $usergroup->$key = $value;
953 }
954 }
955
956 $usergroup = apply_filters( 'ef_usergroup_object', $usergroup );
957
958 return $usergroup;
959 }
960
961 /**
962 * Create a new usergroup.
963 *
964 * Usergroup contains:
965 * - Name
966 * - Slug (prefixed with our special key to avoid conflicts)
967 * - Description
968 * - Users
969 *
970 * @since 0.7
971 *
972 * @param array $args Name (optional), slug and description for the usergroup.
973 * @param array $user_ids IDs for the users to be added to the Usergroup.
974 * @return object|WP_Error $usergroup Object for the new Usergroup on success, WP_Error otherwise.
975 */
976 public function add_usergroup( $args = array(), $user_ids = array() ) {
977
978 if ( ! isset( $args['name'] ) ) {
979 return new WP_Error( 'invalid', __( 'New user groups must have a name', 'edit-flow' ) );
980 }
981
982 $name = $args['name'];
983 $default = array(
984 'name' => '',
985 'slug' => self::term_prefix . sanitize_title( $name ),
986 'description' => '',
987 );
988 $args = array_merge( $default, $args );
989
990 // Encode our extra fields and then store them in the description field.
991 $args_to_encode = array(
992 'description' => $args['description'],
993 'user_ids' => array_unique( $user_ids ),
994 );
995 $encoded_description = $this->get_encoded_description( $args_to_encode );
996 $args['description'] = $encoded_description;
997 $usergroup = wp_insert_term( $name, self::taxonomy_key, $args );
998 if ( is_wp_error( $usergroup ) ) {
999 return $usergroup;
1000 }
1001
1002 return $this->get_usergroup_by( 'id', $usergroup['term_id'] );
1003 }
1004
1005 /**
1006 * Update a usergroup with new data.
1007 *
1008 * Fields can include:
1009 * - Name
1010 * - Slug (prefixed with our special key, of course)
1011 * - Description
1012 * - Users
1013 *
1014 * @since 0.7
1015 *
1016 * @param int $id Unique ID for the usergroup.
1017 * @param array $args Usergroup meta to update (name, slug, description).
1018 * @param array $users Users to be added to the Usergroup. If set, removes existing users first.
1019 * @return object|WP_Error $usergroup Object for the updated Usergroup on success, WP_Error otherwise.
1020 */
1021 public function update_usergroup( $id, $args = array(), $users = null ) {
1022
1023 $existing_usergroup = $this->get_usergroup_by( 'id', $id );
1024 if ( is_wp_error( $existing_usergroup ) ) {
1025 return new WP_Error( 'invalid', __( "User group doesn't exist.", 'edit-flow' ) );
1026 }
1027
1028 // Encode our extra fields and then store them in the description field.
1029 $args_to_encode = array();
1030 $args_to_encode['description'] = ( isset( $args['description'] ) ) ? $args['description'] : $existing_usergroup->description;
1031 $args_to_encode['user_ids'] = ( is_array( $users ) ) ? $users : $existing_usergroup->user_ids;
1032 $args_to_encode['user_ids'] = array_unique( $args_to_encode['user_ids'] );
1033 $encoded_description = $this->get_encoded_description( $args_to_encode );
1034 $args['description'] = $encoded_description;
1035
1036 $usergroup = wp_update_term( $id, self::taxonomy_key, $args );
1037 if ( is_wp_error( $usergroup ) ) {
1038 return $usergroup;
1039 }
1040
1041 return $this->get_usergroup_by( 'id', $usergroup['term_id'] );
1042 }
1043
1044 /**
1045 * Delete a usergroup based on its term ID.
1046 *
1047 * @since 0.7
1048 *
1049 * @param int $id Unique ID for the Usergroup.
1050 * @return bool|WP_Error Returns true on success, WP_Error on failure.
1051 */
1052 public function delete_usergroup( $id ) {
1053 $retval = wp_delete_term( $id, self::taxonomy_key );
1054 return $retval;
1055 }
1056
1057 /**
1058 * Add an array of user logins or IDs to a given usergroup.
1059 *
1060 * @since 0.7
1061 *
1062 * @param array $user_ids_or_logins User IDs or logins to be added to the usergroup.
1063 * @param int $id Usergroup to perform the action on.
1064 * @param bool $reset Delete all of the relationships before adding.
1065 * @return bool|WP_Error Whether or not we were successful.
1066 */
1067 public function add_users_to_usergroup( $user_ids_or_logins, $id, $reset = true ) {
1068
1069 if ( ! is_array( $user_ids_or_logins ) ) {
1070 return new WP_Error( 'invalid', __( 'Invalid users variable. Should be array.', 'edit-flow' ) );
1071 }
1072
1073 // To dump the existing users from a usergroup, we need to pass an empty array.
1074 $usergroup = $this->get_usergroup_by( 'id', $id );
1075 if ( $reset ) {
1076 $retval = $this->update_usergroup( $id, null, array() );
1077 if ( is_wp_error( $retval ) ) {
1078 return $retval;
1079 }
1080 }
1081
1082 // Add the new users one by one to an array we'll pass back to the usergroup.
1083 $new_users = array();
1084 foreach ( (array) $user_ids_or_logins as $user_id_or_login ) {
1085 if ( ! is_numeric( $user_id_or_login ) ) {
1086 $new_users[] = get_user_by( 'login', $user_id_or_login )->ID;
1087 } else {
1088 $new_users[] = (int) $user_id_or_login;
1089 }
1090 }
1091 $retval = $this->update_usergroup( $id, null, $new_users );
1092 if ( is_wp_error( $retval ) ) {
1093 return $retval;
1094 }
1095 return true;
1096 }
1097
1098 /**
1099 * Add a given user to a Usergroup. Can use User ID or user login.
1100 *
1101 * @since 0.7
1102 *
1103 * @param int|string $user_id_or_login User ID or login to be added to the Usergroups.
1104 * @param int|array $ids ID for the Usergroup(s).
1105 * @return bool|WP_Error Return true on success, WP_Error on error.
1106 */
1107 public function add_user_to_usergroup( $user_id_or_login, $ids ) {
1108
1109 if ( ! is_numeric( $user_id_or_login ) ) {
1110 $user_id = get_user_by( 'login', $user_id_or_login )->ID;
1111 } else {
1112 $user_id = (int) $user_id_or_login;
1113 }
1114
1115 foreach ( (array) $ids as $usergroup_id ) {
1116 $usergroup = $this->get_usergroup_by( 'id', $usergroup_id );
1117
1118 // Skip if user is already in this group.
1119 if ( in_array( (int) $user_id, array_map( 'intval', (array) $usergroup->user_ids ), true ) ) {
1120 continue;
1121 }
1122
1123 $usergroup->user_ids[] = $user_id;
1124 $retval = $this->update_usergroup( $usergroup_id, null, $usergroup->user_ids );
1125 if ( is_wp_error( $retval ) ) {
1126 return $retval;
1127 }
1128 }
1129 return true;
1130 }
1131
1132 /**
1133 * Remove a given user from one or more usergroups.
1134 *
1135 * @since 0.7
1136 *
1137 * @param int|string $user_id_or_login User ID or login to be removed from the Usergroups.
1138 * @param int|array $ids ID for the Usergroup(s).
1139 * @return bool|WP_Error Return true on success, WP_Error on error.
1140 */
1141 public function remove_user_from_usergroup( $user_id_or_login, $ids ) {
1142
1143 if ( ! is_numeric( $user_id_or_login ) ) {
1144 $user_id = get_user_by( 'login', $user_id_or_login )->ID;
1145 } else {
1146 $user_id = (int) $user_id_or_login;
1147 }
1148
1149 // Remove the user from each usergroup specified.
1150 foreach ( (array) $ids as $usergroup_id ) {
1151 $usergroup = $this->get_usergroup_by( 'id', $usergroup_id );
1152 // @todo I bet there's a PHP function for this I couldn't look up at 35,000 over the Atlantic.
1153 foreach ( $usergroup->user_ids as $key => $usergroup_user_id ) {
1154 if ( (int) $usergroup_user_id === (int) $user_id ) {
1155 unset( $usergroup->user_ids[ $key ] );
1156 }
1157 }
1158 $retval = $this->update_usergroup( $usergroup_id, null, $usergroup->user_ids );
1159 if ( is_wp_error( $retval ) ) {
1160 return $retval;
1161 }
1162 }
1163 return true;
1164 }
1165
1166 /**
1167 * Get all of the Usergroup ids or objects for a given user.
1168 *
1169 * @since 0.7
1170 *
1171 * @param int|string $user_id_or_login User ID or login to search against.
1172 * @param string $ids_or_objects Whether to retrieve an array of IDs or usergroup objects.
1173 * @return array|bool Array of usergroup 'ids' or 'objects', false if none.
1174 */
1175 public function get_usergroups_for_user( $user_id_or_login, $ids_or_objects = 'ids' ) {
1176
1177 if ( ! is_numeric( $user_id_or_login ) ) {
1178 $user_id = get_user_by( 'login', $user_id_or_login )->ID;
1179 } else {
1180 $user_id = (int) $user_id_or_login;
1181 }
1182
1183 // Unfortunately, the easiest way to do this is get all usergroups
1184 // and then loop through each one to see if the user ID is stored.
1185 $all_usergroups = $this->get_usergroups();
1186 if ( ! empty( $all_usergroups ) ) {
1187 $usergroup_objects_or_ids = array();
1188 foreach ( $all_usergroups as $usergroup ) {
1189 // Not in this usergroup, so keep going.
1190 if ( ! in_array( (int) $user_id, array_map( 'intval', (array) $usergroup->user_ids ), true ) ) {
1191 continue;
1192 }
1193 if ( 'ids' == $ids_or_objects ) {
1194 $usergroup_objects_or_ids[] = (int) $usergroup->term_id;
1195 } elseif ( 'objects' == $ids_or_objects ) {
1196 $usergroup_objects_or_ids[] = $usergroup;
1197 }
1198 }
1199 return $usergroup_objects_or_ids;
1200 } else {
1201 return false;
1202 }
1203 }
1204 }
1205
1206 }
1207
1208 // phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
1209
1210 if ( ! class_exists( 'EF_Usergroups_List_Table' ) ) {
1211 /**
1212 * Usergroups uses WordPress' List Table API for generating the Usergroup management table.
1213 *
1214 * @since 0.7
1215 */
1216 class EF_Usergroups_List_Table extends WP_List_Table {
1217
1218 /**
1219 * Callback arguments.
1220 *
1221 * @var array
1222 */
1223 protected $callback_args;
1224
1225 /**
1226 * Constructor.
1227 */
1228 public function __construct() {
1229
1230 parent::__construct( array(
1231 'plural' => 'user groups',
1232 'singular' => 'user group',
1233 'ajax' => true,
1234 ) );
1235 }
1236
1237 /**
1238 * Prepare items for display.
1239 *
1240 * @todo Paginate if we have a lot of usergroups.
1241 *
1242 * @since 0.7
1243 */
1244 public function prepare_items() {
1245 global $edit_flow;
1246
1247 $columns = $this->get_columns();
1248 $hidden = array();
1249 $sortable = array();
1250
1251 $this->_column_headers = array( $columns, $hidden, $sortable );
1252
1253 $this->items = $edit_flow->user_groups->get_usergroups();
1254
1255 if ( ! is_array( $this->items ) ) {
1256 $this->items = array();
1257 }
1258
1259 $this->set_pagination_args( array(
1260 'total_items' => count( $this->items ),
1261 'per_page' => count( $this->items ),
1262 ) );
1263 }
1264
1265 /**
1266 * Message to be displayed when there are no usergroups.
1267 *
1268 * @since 0.7
1269 */
1270 public function no_items() {
1271 esc_html_e( 'No user groups found.', 'edit-flow' );
1272 }
1273
1274 /**
1275 * Columns in our Usergroups table.
1276 *
1277 * @since 0.7
1278 *
1279 * @return array The columns.
1280 */
1281 public function get_columns() {
1282
1283 $columns = array(
1284 'name' => __( 'Name', 'edit-flow' ),
1285 'description' => __( 'Description', 'edit-flow' ),
1286 'users' => __( 'Users in Group', 'edit-flow' ),
1287 );
1288
1289 return $columns;
1290 }
1291
1292 /**
1293 * Process the Usergroup column value for all methods that aren't registered.
1294 *
1295 * @since 0.7
1296 *
1297 * @param object $usergroup The usergroup object.
1298 * @param string $column_name The column name.
1299 */
1300 public function column_default( $usergroup, $column_name ) {
1301 }
1302
1303 /**
1304 * Process the Usergroup name column value.
1305 *
1306 * Displays the name of the Usergroup, and action links.
1307 *
1308 * @since 0.7
1309 *
1310 * @param object $usergroup The usergroup object.
1311 * @return string The column output.
1312 */
1313 public function column_name( $usergroup ) {
1314 global $edit_flow;
1315
1316 // @todo direct edit link.
1317 $output = '<strong><a href="' . esc_url( $edit_flow->user_groups->get_link( array(
1318 'action' => 'edit-usergroup',
1319 'usergroup-id' => $usergroup->term_id,
1320 ) ) ) . '">' . esc_html( $usergroup->name ) . '</a></strong>';
1321
1322 $actions = array();
1323 $actions['edit edit-usergroup'] = sprintf( '<a href="%1$s">' . __( 'Edit', 'edit-flow' ) . '</a>', esc_url( $edit_flow->user_groups->get_link( array(
1324 'action' => 'edit-usergroup',
1325 'usergroup-id' => $usergroup->term_id,
1326 ) ) ) );
1327 $actions['inline hide-if-no-js'] = '<a href="#" class="editinline">' . __( 'Quick&nbsp;Edit', 'edit-flow' ) . '</a>';
1328 $actions['delete delete-usergroup'] = sprintf( '<a href="%1$s">' . __( 'Delete', 'edit-flow' ) . '</a>', esc_url( $edit_flow->user_groups->get_link( array(
1329 'action' => 'delete-usergroup',
1330 'usergroup-id' => $usergroup->term_id,
1331 ) ) ) );
1332
1333 $output .= $this->row_actions( $actions, false );
1334 $output .= '<div class="hidden" id="inline_' . $usergroup->term_id . '">';
1335 $output .= '<div class="name">' . esc_html( $usergroup->name ) . '</div>';
1336 $output .= '<div class="description">' . esc_html( $usergroup->description ) . '</div>';
1337 $output .= '</div>';
1338
1339 return $output;
1340 }
1341
1342 /**
1343 * Handle the 'description' column for the table of Usergroups.
1344 *
1345 * Don't need to unencode this because we already did when the usergroup was loaded.
1346 *
1347 * @since 0.7
1348 *
1349 * @param object $usergroup The usergroup object.
1350 * @return string The column output.
1351 */
1352 public function column_description( $usergroup ) {
1353 return esc_html( $usergroup->description );
1354 }
1355
1356 /**
1357 * Show the "Total Users" in a given usergroup.
1358 *
1359 * @since 0.7
1360 *
1361 * @param object $usergroup The usergroup object.
1362 * @return string The column output.
1363 */
1364 public function column_users( $usergroup ) {
1365 global $edit_flow;
1366 return '<a href="' . esc_url( $edit_flow->user_groups->get_link( array(
1367 'action' => 'edit-usergroup',
1368 'usergroup-id' => $usergroup->term_id,
1369 ) ) ) . '">' . count( $usergroup->user_ids ) . '</a>';
1370 }
1371
1372 /**
1373 * Prepare a single row of information about a usergroup.
1374 *
1375 * @since 0.7
1376 *
1377 * @param object $usergroup The usergroup object.
1378 */
1379 public function single_row( $usergroup ) {
1380 static $is_alternate = false;
1381 $is_alternate = ! $is_alternate;
1382
1383 printf(
1384 '<tr id="usergroup-%d"%s>',
1385 (int) $usergroup->term_id,
1386 $is_alternate ? ' class="alternate"' : ''
1387 );
1388 // single_row_columns() echoes its own output; the column_* callbacks are
1389 // responsible for escaping, as per WP_List_Table's contract.
1390 $this->single_row_columns( $usergroup );
1391 echo '</tr>';
1392 }
1393
1394 /**
1395 * If we use this form, we can have inline editing!
1396 *
1397 * @since 0.7
1398 */
1399 public function inline_edit() {
1400 global $edit_flow;
1401 ?>
1402 <form method="get" action=""><table style="display: none"><tbody id="inlineedit">
1403 <tr id="inline-edit" class="inline-edit-row" style="display: none"><td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>" class="colspanchange">
1404 <fieldset><div class="inline-edit-col">
1405 <h4><?php esc_html_e( 'Quick Edit', 'edit-flow' ); ?></h4>
1406 <label>
1407 <span class="title"><?php _e( 'Name', 'edit-flow' ); ?></span>
1408 <span class="input-text-wrap"><input type="text" name="name" class="ptitle" value="" maxlength="40" /></span>
1409 </label>
1410 <label>
1411 <span class="title"><?php _e( 'Description', 'edit-flow' ); ?></span>
1412 <span class="input-text-wrap"><input type="text" name="description" class="pdescription" value="" /></span>
1413 </label>
1414 </div></fieldset>
1415 <p class="inline-edit-save submit">
1416 <a accesskey="c" href="#inline-edit" title="<?php esc_attr_e( 'Cancel', 'edit-flow' ); ?>" class="cancel button-secondary alignleft"><?php esc_html_e( 'Cancel', 'edit-flow' ); ?></a>
1417 <?php $update_text = __( 'Update User Group', 'edit-flow' ); ?>
1418 <a accesskey="s" href="#inline-edit" title="<?php echo esc_attr( $update_text ); ?>" class="save button-primary alignright"><?php echo esc_html( $update_text ); ?></a>
1419 <img class="waiting" style="display:none;" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
1420 <span class="error" style="display:none;"></span>
1421 <?php wp_nonce_field( 'usergroups-inline-edit-nonce', 'inline_edit', false ); ?>
1422 <br class="clear" />
1423 </p>
1424 </td></tr>
1425 </tbody></table></form>
1426 <?php
1427 }
1428 }
1429 }
1430