PluginProbe
Groups – Memberships and Access Control / trunk
Groups – Memberships and Access Control vtrunk
4.6.0 4.5.0 4.4.0 4.3.0 trunk 1.0.0-beta-1 1.0.0-beta-2 1.0.0-beta-3 1.0.0-beta-3b 1.0.0-beta-3c 1.0.0-beta-3d 1.1.4 1.1.5 1.10.0 1.10.1 1.10.2 1.10.3 1.11.0 1.11.1 1.11.2 1.11.3 1.12.0 1.13.0 1.13.1 1.2.0 All 129 releases
groups / lib / admin / groups-admin-groups.php

groups-admin-groups.php in Groups – Memberships and Access Control trunk, at lib/admin/groups-admin-groups.php

785 lines 28.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * groups-admin-groups.php
4 *
5 * Copyright (c) "kento" Karim Rahimpur www.itthinx.com
6 *
7 * This code is released under the GNU General Public License.
8 * See COPYRIGHT.txt and LICENSE.txt.
9 *
10 * This code is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * This header and all notices must be kept intact.
16 *
17 * @author Karim Rahimpur
18 * @package groups
19 * @since groups 1.0.0
20 */
21
22 if ( !defined( 'ABSPATH' ) ) {
23 exit;
24 }
25
26 // phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
27
28 // admin defines
29 define( 'GROUPS_GROUPS_PER_PAGE', 10 );
30 define( 'GROUPS_ADMIN_GROUPS_ACTION_NONCE', 'groups-action-nonce' );
31 define( 'GROUPS_ADMIN_GROUPS_FILTER_NONCE', 'groups-filter-nonce' );
32
33 require_once GROUPS_CORE_LIB . '/class-groups-pagination.php';
34 require_once GROUPS_ADMIN_LIB . '/groups-admin-groups-add.php';
35 require_once GROUPS_ADMIN_LIB . '/groups-admin-groups-edit.php';
36 require_once GROUPS_ADMIN_LIB . '/groups-admin-groups-remove.php';
37
38 /**
39 * Manage Groups: table of groups and add, edit, remove actions.
40 */
41 function groups_admin_groups() {
42
43 global $wpdb;
44
45 $output = '';
46 // $today = date( 'Y-m-d', time() ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
47
48 if ( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
49 wp_die( esc_html__( 'Access denied.', 'groups' ) );
50 }
51
52 //
53 // handle actions
54 //
55 if ( isset( $_POST['action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
56 // handle action submit - do it
57 switch ( groups_sanitize_post( 'action' ) ) {
58 case 'add' :
59 if ( !( $group_id = groups_admin_groups_add_submit() ) ) {
60 return groups_admin_groups_add();
61 } else {
62 $group = Groups_Group::read( $group_id );
63 Groups_Admin::add_message( sprintf(
64 /* translators: group name */
65 __( 'The <em>%s</em> group has been created.', 'groups' ),
66 $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : ''
67 ) );
68 }
69 break;
70 case 'edit' :
71 if ( !( $group_id = groups_admin_groups_edit_submit() ) ) {
72 return groups_admin_groups_edit( groups_sanitize_post( 'group-id-field' ) );
73 } else {
74 $group = Groups_Group::read( $group_id );
75 Groups_Admin::add_message( sprintf(
76 /* translators: group name */
77 __( 'The <em>%s</em> group has been updated.', 'groups' ),
78 $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : ''
79 ) );
80 }
81 break;
82 case 'remove' :
83 if ( $group_id = groups_admin_groups_remove_submit() ) {
84 Groups_Admin::add_message( __( 'The group has been deleted.', 'groups' ) );
85 }
86 break;
87 // bulk actions on groups: add capabilities, remove capabilities, remove groups
88 case 'groups-action' :
89 if ( groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_ACTION_NONCE, 'admin' ) ) {
90 $group_ids = groups_sanitize_post( 'group_ids' );
91 $bulk_action = groups_sanitize_post( 'bulk-action' );
92 if ( is_array( $group_ids ) && ( $bulk_action !== null ) ) {
93 foreach ( $group_ids as $group_id ) {
94 switch ( $bulk_action ) {
95 case 'add-capability' :
96 $capabilities_id = groups_sanitize_post( 'capability_id' );
97 if ( is_array( $capabilities_id ) ) {
98 foreach ( $capabilities_id as $capability_id ) {
99 Groups_Group_Capability::create( array( 'group_id' => $group_id, 'capability_id' => $capability_id ) );
100 }
101 }
102 break;
103 case 'remove-capability' :
104 $capabilities_id = groups_sanitize_post( 'capability_id' );
105 if ( is_array( $capabilities_id ) ) {
106 foreach ( $capabilities_id as $capability_id ) {
107 Groups_Group_Capability::delete( $group_id, $capability_id );
108 }
109 }
110 break;
111 case 'remove-group' :
112 $bulk_confirm = isset( $_POST['confirm'] ) ? true : false; // phpcs:ignore WordPress.Security.NonceVerification.Missing
113 if ( $bulk_confirm ) {
114 groups_admin_groups_bulk_remove_submit();
115 } else {
116 return groups_admin_groups_bulk_remove();
117 }
118 break;
119 default:
120 if ( has_action( 'groups_admin_groups_handle_bulk_action' ) ) {
121 /**
122 * Handle the requested bulk action.
123 *
124 * @param string $bulk_action the requested bulk action
125 * @param string|int $group_id the requested group ID
126 */
127 do_action( 'groups_admin_groups_handle_bulk_action', $bulk_action, $group_id );
128 }
129 }
130 }
131 }
132 }
133 break;
134 default:
135 if ( has_filter( 'groups_admin_groups_handle_action_submit' ) ) {
136 /**
137 * Handle a requested action after $_POST.
138 *
139 * @since 3.7.0
140 *
141 * @param boolean $handle whether to handle the posted action
142 * @param string $action the requested action
143 *
144 * @return boolean whether the posted data was accepted and action was taken
145 */
146 if ( apply_filters( 'groups_admin_groups_handle_action_submit', false, groups_sanitize_post( 'action' ) ) ) {
147 /**
148 * Fires after the posted data for an action was accepted.
149 *
150 * Should produce output to provide feedback to the user.
151 *
152 * @since 3.7.0
153 *
154 * @param string $action the requested action
155 */
156 do_action( 'groups_admin_groups_handle_action_confirm', groups_sanitize_post( 'action' ) );
157 } else {
158 /**
159 * Fires after the posted data for an action was rejected.
160 *
161 * Should produce output to provide feedback to the user.
162 *
163 * @since 3.7.0
164 *
165 * @param string $action the requested action
166 */
167 do_action( 'groups_admin_groups_handle_action_reject', groups_sanitize_post( 'action' ) );
168 return;
169 }
170 }
171 }
172 } else if ( isset( $_GET['action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
173 // handle action request - show form
174 switch ( groups_sanitize_get( 'action' ) ) {
175 case 'add' :
176 return groups_admin_groups_add();
177 break;
178 case 'edit' :
179 if ( isset( $_GET['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
180 return groups_admin_groups_edit( groups_sanitize_get( 'group_id' ) );
181 }
182 break;
183 case 'remove' :
184 if ( isset( $_GET['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
185 return groups_admin_groups_remove( groups_sanitize_get( 'group_id' ) );
186 }
187 break;
188 default:
189 if ( isset( $_GET['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
190 if ( has_action( 'groups_admin_groups_handle_action' ) ) {
191 /**
192 * Handle the requested action and produce the corresponding output.
193 *
194 * @param string $action the requested action
195 * @param string|int $group_id the requested group ID
196 */
197 do_action( 'groups_admin_groups_handle_action', groups_sanitize_get( 'action' ), groups_sanitize_get( 'group_id' ) );
198 return;
199 }
200 }
201 }
202 }
203
204 //
205 // group table
206 //
207 if (
208 isset( $_POST['clear_filters'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing
209 isset( $_POST['group_id'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing
210 isset( $_POST['group_name'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
211 ) {
212 if ( !groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_FILTER_NONCE, 'admin' ) ) {
213 wp_die( esc_html__( 'Access denied.', 'groups' ) );
214 }
215 }
216
217 // filters
218 $group_id = Groups_Options::get_user_option( 'groups_group_id', null );
219 $group_name = Groups_Options::get_user_option( 'groups_group_name', null );
220
221 if ( isset( $_POST['clear_filters'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
222 Groups_Options::delete_user_option( 'groups_group_id' );
223 Groups_Options::delete_user_option( 'groups_group_name' );
224 $group_id = null;
225 $group_name = null;
226 } else if ( isset( $_POST['submitted'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
227 // filter by name
228 if ( !empty( $_POST['group_name'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
229 $group_name = groups_sanitize_post( 'group_name' );
230 Groups_Options::update_user_option( 'groups_group_name', $group_name );
231 }
232 // filter by group id
233 if ( !empty( $_POST['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
234 $group_id = intval( groups_sanitize_post( 'group_id' ) );
235 Groups_Options::update_user_option( 'groups_group_id', $group_id );
236 } else if ( isset( $_POST['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
237 // empty && isset => '' => all
238 $group_id = null;
239 Groups_Options::delete_user_option( 'groups_group_id' );
240 }
241 }
242
243 if ( isset( $_POST['row_count'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
244 if ( !groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_NONCE, 'admin' ) ) {
245 wp_die( esc_html__( 'Access denied.', 'groups' ) );
246 }
247 }
248
249 if ( isset( $_POST['paged'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
250 if ( !groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_NONCE, 'admin' ) ) {
251 wp_die( esc_html__( 'Access denied.', 'groups' ) );
252 }
253 }
254
255 $current_url = groups_get_current_url();
256 $current_url = remove_query_arg( 'paged', $current_url );
257 $current_url = remove_query_arg( 'action', $current_url );
258 $current_url = remove_query_arg( 'group_id', $current_url );
259
260 $group_table = _groups_get_tablename( 'group' );
261 $user_group_table = _groups_get_tablename( 'user_group' );
262
263 $output .=
264 '<div class="manage-groups wrap">' .
265 '<h1>' .
266 _x( 'Groups', 'page-title', 'groups' ) .
267 sprintf(
268 '<a title="%s" class="add page-title-action" href="%s">',
269 esc_attr__( 'Click to add a new group', 'groups' ),
270 esc_url( $current_url . '&action=add' )
271 ) .
272 sprintf(
273 '<img class="icon" alt="%s" src="%s" />',
274 esc_attr__( 'Add', 'groups' ),
275 esc_url( GROUPS_PLUGIN_URL . 'images/add.png' )
276 ) .
277 sprintf(
278 '<span class="label">%s</span>',
279 esc_html__( 'New Group', 'groups' )
280 ) .
281 '</a>' .
282 '</h1>';
283
284 $output .= Groups_Admin::render_messages();
285
286 $row_count = intval( groups_sanitize_post( 'row_count' ) ?? 0 );
287
288 if ($row_count <= 0) {
289 $row_count = Groups_Options::get_user_option( 'groups_per_page', GROUPS_GROUPS_PER_PAGE );
290 } else {
291 Groups_Options::update_user_option('groups_per_page', $row_count );
292 }
293 $offset = intval( groups_sanitize_get( 'offset' ) ?? 0 );
294 if ( $offset < 0 ) {
295 $offset = 0;
296 }
297 $paged = intval( groups_sanitize_request( 'paged' ) ?? 0 );
298 if ( $paged < 0 ) {
299 $paged = 0;
300 }
301
302 $orderby = groups_sanitize_get( 'orderby' );
303 switch ( $orderby ) {
304 case 'group_id' :
305 case 'name' :
306 case 'description' :
307 case 'members':
308 break;
309 default:
310 $orderby = 'name';
311 }
312
313 $order = groups_sanitize_get( 'order' );
314 switch ( $order ) {
315 case 'asc' :
316 case 'ASC' :
317 $switch_order = 'DESC';
318 break;
319 case 'desc' :
320 case 'DESC' :
321 $switch_order = 'ASC';
322 break;
323 default:
324 $order = 'ASC';
325 $switch_order = 'DESC';
326 }
327
328 $filters = array( " 1=%d " );
329 $filter_params = array( 1 );
330 if ( $group_id ) {
331 $filters[] = " $group_table.group_id = %d ";
332 $filter_params[] = $group_id;
333 }
334 if ( $group_name ) {
335 $filters[] = " $group_table.name LIKE %s ";
336 $filter_params[] = '%' . $wpdb->esc_like( $group_name ) . '%';
337 }
338
339 if ( !empty( $filters ) ) { // @phpstan-ignore empty.variable
340 $filters = " WHERE " . implode( " AND ", $filters );
341 } else {
342 $filters = '';
343 }
344
345 $count_query = $wpdb->prepare( "SELECT COUNT(*) FROM $group_table $filters", $filter_params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
346 $count = $wpdb->get_var( $count_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
347 if ( $count > $row_count ) {
348 $paginate = true;
349 } else {
350 $paginate = false;
351 }
352 $pages = ceil ( $count / $row_count );
353 if ( $paged > $pages ) {
354 $paged = $pages;
355 }
356 if ( $paged != 0 ) {
357 $offset = ( $paged - 1 ) * $row_count;
358 }
359
360 switch ( $orderby ) {
361 case 'members':
362 $query = $wpdb->prepare(
363 // nosemgrep: audit.php.wp.security.sqli.input-in-sinks
364 "SELECT $group_table.*, COUNT($user_group_table.user_id) AS members " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
365 "FROM $group_table LEFT JOIN $user_group_table ON $group_table.group_id = $user_group_table.group_id " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
366 "$filters " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
367 "GROUP BY $group_table.group_id " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
368 "ORDER BY COUNT($user_group_table.user_id) $order " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
369 "LIMIT $row_count OFFSET $offset", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
370 $filter_params
371 );
372 break;
373 default:
374 $query = $wpdb->prepare(
375 // nosemgrep: audit.php.wp.security.sqli.input-in-sinks
376 "SELECT * FROM $group_table $filters ORDER BY $orderby $order LIMIT $row_count OFFSET $offset", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
377 $filter_params
378 );
379 }
380
381 /**
382 * Allows to modify the query for the groups table.
383 *
384 * @since 3.7.0
385 *
386 * @param string $query the query
387 *
388 * @return string
389 */
390 $query = apply_filters( 'groups_admin_groups_query', $query );
391
392 // nosemgrep: audit.php.wp.security.sqli.input-in-sinks
393 $results = $wpdb->get_results( $query, OBJECT ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
394
395 /**
396 * Allows to modify the results for the groups table.
397 *
398 * @since 3.7.0
399 *
400 * @param object[] $results result to show
401 *
402 * @return object[]
403 */
404 $results = apply_filters( 'groups_admin_groups_results', $results );
405
406 $columns = array(
407 'group_id' => array( 'label' => __( 'ID', 'groups' ), 'sortable' => true ),
408 'name' => array( 'label' => __( 'Group', 'groups' ), 'sortable' => true ),
409 'members' => array( 'label' => __( 'Members', 'groups' ), 'sortable' => true ),
410 'description' => array( 'label' => __( 'Description', 'groups' ), 'sortable' => true ),
411 'capabilities' => array( 'label' => __( 'Capabilities', 'groups' ), 'sortable' => false )
412 );
413
414 /**
415 * Allows to modify the columns of the groups table.
416 *
417 * @since 3.7.0
418 *
419 * @param array $columns maps column keys to column details; keys must be alphanumeric allowing also for underscores '_' and dashes '-', columns with invalid keys are removed; 'checkbox' is a reserved column key and must not be used
420 *
421 * @return array
422 */
423 $columns = apply_filters( 'groups_admin_groups_columns', $columns );
424 unset( $columns['checkbox'] );
425 foreach ( $columns as $key => $column ) {
426 if ( preg_replace( '/[^a-zA-Z0-9_-]/', '', $key ) !== $key ) {
427 unset( $columns[$key] );
428 }
429 }
430
431 $column_count = count( $columns ) + 1;
432
433 $output .= '<div class="groups-overview">';
434
435 $filters_html = '<div class="filters">';
436 $filters_html .= '<form id="setfilters" action="" method="post">';
437 $filters_html .= '<fieldset>';
438 $filters_html .= '<legend>' . esc_html__( 'Filters', 'groups' ) . '</legend>';
439 $filters_html .= '<label class="group-id-filter">' . esc_html__( 'Group ID', 'groups' ) . ' ';
440 $filters_html .= '<input class="group-id-filter" name="group_id" type="text" value="' . esc_attr( $group_id ) . '"/>';
441 $filters_html .= '</label>' . ' ';
442 $filters_html .= '<label class="group-name-filter">' . esc_html__( 'Group Name', 'groups' ) . ' ';
443 $filters_html .= '<input class="group-name-filter" name="group_name" type="text" value="' . esc_attr( stripslashes( $group_name !== null ? $group_name : '' ) ) . '"/>';
444 $filters_html .= '</label>' . ' ';
445 /**
446 * Allows to add markup after the standard filter fields of the groups table.
447 *
448 * @since 3.7.0
449 *
450 * @param string $markup additional markup
451 *
452 * @return string
453 */
454 $filters_html .= apply_filters( 'groups_admin_groups_filters_fields_epilogue', '' );
455 $filters_html .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_FILTER_NONCE, true, false );
456 $filters_html .= '<input class="button" type="submit" value="' . esc_attr__( 'Apply', 'groups' ) . '"/>' . ' ';
457 $filters_html .= '<input class="button" type="submit" name="clear_filters" value="' . esc_attr__( 'Clear', 'groups' ) . '"/>';
458 $filters_html .= '<input type="hidden" value="submitted" name="submitted"/>';
459 $filters_html .= '</fieldset>';
460 $filters_html .= '</form>';
461 $filters_html .= '</div>'; // .filters
462
463 /**
464 * Allows to process the HTML of the filters section of the groups table.
465 *
466 * @since 3.7.0
467 *
468 * @param string $filters_html markup
469 *
470 * @return string
471 */
472 $output .= apply_filters( 'groups_admin_groups_filters_html', $filters_html );
473
474 $capability_table = _groups_get_tablename( "capability" );
475 // $group_capability_table = _groups_get_tablename( "group_capability" );
476
477 // capabilities select
478 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
479 $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" );
480 $capabilities_select = sprintf(
481 '<select class="select capability" name="capability_id[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
482 esc_attr__( 'Capabilities &hellip;', 'groups' ),
483 esc_attr__( 'Capabilities &hellip;', 'groups' )
484 );
485 foreach ( $capabilities as $capability ) {
486 $capabilities_select .= sprintf(
487 '<option value="%s">%s</option>',
488 esc_attr( $capability->capability_id ),
489 $capability->capability ? stripslashes( wp_filter_nohtml_kses( $capability->capability ) ) : ''
490 );
491 }
492 $capabilities_select .= '</select>';
493 $capabilities_select .= Groups_UIE::render_select( '.select.capability' );
494
495 $output .= '<form id="groups-action" method="post" action="">';
496
497 $output .= '<div class="tablenav top">';
498
499 $bulk_html = '<div class="groups-bulk-container">';
500 $bulk_html .= '<div class="capabilities-select-container">';
501 $bulk_html .= $capabilities_select;
502 $bulk_html .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false );
503 $bulk_html .= '</div>';
504 $bulk_html .= '<select class="bulk-action" name="bulk-action">';
505 $bulk_html .= '<option selected="selected" value="-1">' . esc_html__( 'Bulk Actions', 'groups' ) . '</option>';
506 $bulk_html .= '<option value="remove-group">' . esc_html__( 'Remove group', 'groups' ) . '</option>';
507 $bulk_html .= '<option value="add-capability">' . esc_html__( 'Add capability', 'groups' ) . '</option>';
508 $bulk_html .= '<option value="remove-capability">' . esc_html__( 'Remove capability', 'groups' ) . '</option>';
509 $bulk_html .= '</select>';
510 /**
511 * Allows to add markup after the standard bulk actions fields of the groups table.
512 *
513 * @since 3.7.0
514 *
515 * @param string $markup additional markup
516 *
517 * @return string
518 */
519 $filters_html .= apply_filters( 'groups_admin_groups_bulk_actions_fields_epilogue', '' );
520 $bulk_html .= sprintf( '<input class="button" type="submit" name="bulk" value="%s" />', esc_attr__( 'Apply', 'groups' ) );
521 $bulk_html .= '<input type="hidden" name="action" value="groups-action"/>';
522 $bulk_html .= '</div>';
523
524 /**
525 * Allows to process the HTML of the bulk actions section of the groups table.
526 *
527 * @since 3.7.0
528 *
529 * @param string $bulk_html markup
530 *
531 * @return string
532 */
533 $output .= apply_filters( 'groups_admin_groups_bulk_actions_html', $bulk_html );
534
535 if ( $paginate ) {
536 require_once GROUPS_CORE_LIB . '/class-groups-pagination.php';
537 $pagination = new Groups_Pagination( $count, null, $row_count );
538 $output .= $pagination->pagination( 'top' );
539 }
540
541 $output .= '<div class="page-options right">';
542 $output .= '<label for="row_count">' . esc_html__( 'Results per page', 'groups' ) . '</label>';
543 $output .= '<input name="row_count" type="text" size="2" value="' . esc_attr( $row_count ) .'" />';
544 $output .= '<input class="button" type="submit" value="' . esc_attr__( 'Apply', 'groups' ) . '"/>';
545 $output .= '</div>'; // .page-options.right
546
547 $output .= '</div>'; // .tablenav.top
548
549 $output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_NONCE, true, false );
550
551 $output .= '<table id="" class="wp-list-table widefat fixed" cellspacing="0">';
552 $output .= '<thead>';
553 $output .= '<tr>';
554
555 $output .= '<th id="cb" class="manage-column column-cb check-column" scope="col"><input type="checkbox"></th>';
556
557 foreach ( $columns as $key => $column ) {
558 $options = array(
559 'orderby' => $key,
560 'order' => $switch_order
561 );
562 $class = $key;
563 if ( isset( $column['sortable'] ) && $column['sortable'] ) {
564 if ( strcmp( $key, $orderby ) == 0 ) {
565 $lorder = strtolower( $order );
566 $class = "$key manage-column sorted $lorder";
567 } else {
568 $class = "$key manage-column sortable";
569 }
570 $heading =
571 sprintf(
572 '<a href="%s"><span>%s</span>'.
573 '<span class="sorting-indicators">' .
574 '<span class="sorting-indicator asc" aria-hidden="true"></span>'.
575 '<span class="sorting-indicator desc" aria-hidden="true"></span>'.
576 '</span>' . // .sorting-indicators
577 '</a>',
578 esc_url( add_query_arg( $options, $current_url ) ),
579 esc_html( $column['label'] )
580 );
581 } else {
582 $heading = esc_html( $column['label'] );
583 }
584 $output .= sprintf(
585 '<th scope="col" class="%s">%s</th>',
586 esc_attr( $class ),
587 $heading
588 );
589 }
590
591 $output .= '</tr>';
592 $output .= '</thead>';
593 $output .= '<tbody>';
594
595 if ( count( $results ) > 0 ) {
596 for ( $i = 0; $i < count( $results ); $i++ ) {
597
598 $result = $results[$i];
599
600 /**
601 * @var Groups_Group
602 */
603 $group = new Groups_Group( $result->group_id );
604
605 // Construct the "edit" URL.
606 $edit_url = add_query_arg(
607 array(
608 'group_id' => intval( $result->group_id ),
609 'action' => 'edit',
610 'paged' => $paged
611 ),
612 $current_url
613 );
614
615 // Construct the "delete" URL.
616 $delete_url = add_query_arg(
617 array(
618 'group_id' => intval( $result->group_id ),
619 'action' => 'remove',
620 'paged' => $paged
621 ),
622 $current_url
623 );
624
625 $users_url = add_query_arg(
626 array( 'filter_group_ids[0]' => intval( $result->group_id ) ),
627 admin_url( 'users.php' )
628 );
629
630 // Construct row actions for this group.
631 $row_actions = array(
632 'edit' => sprintf( '<a href="%s"><img src="%s"/>&nbsp;%s</a>', esc_url( $edit_url ), esc_url( GROUPS_PLUGIN_URL . 'images/edit.png' ), esc_html__( 'Edit', 'groups' ) )
633 );
634 if ( $result->name !== Groups_Registered::REGISTERED_GROUP_NAME ) {
635 $row_actions['remove trash'] = sprintf( '<a href="%s" class="submitdelete"><img src="%s"/>&nbsp;%s</a>', esc_url( $delete_url ), esc_url( GROUPS_PLUGIN_URL . 'images/remove.png' ), esc_html__( 'Remove', 'groups' ) );
636 }
637
638 /**
639 * Allows to alter the row actions for a group in the groups table.
640 *
641 * @since 3.7.0
642 *
643 * @param array $row_actions row actions as HTML
644 * @param int $group_id ID of the group
645 *
646 * @return array
647 */
648 $row_actions = apply_filters( 'groups_admin_groups_row_actions', $row_actions, intval( $result->group_id ) );
649
650 $n = 1;
651 $row_actions_html = '<div class="row-actions">';
652 foreach ( $row_actions as $row_action_key => $row_action ) {
653 $row_actions_html .= sprintf( '<span class="%s">', esc_attr( $row_action_key ) );
654 $row_actions_html .= $row_action;
655 $row_actions_html .= '</span>';
656 if ( $n < count( $row_actions ) ) {
657 $row_actions_html .= '&emsp;|&emsp;';
658 }
659 $n++;
660 }
661 $row_actions_html .= '</div>'; // .row-actions
662
663 /**
664 * Allows to process the HTML of the row actions for a group in the groups table.
665 *
666 * @since 3.7.0
667 *
668 * @param string $row_actions_html markup
669 * @param int $group_id ID of the group
670 *
671 * @return string
672 */
673 $row_actions_html = apply_filters( 'groups_admin_groups_row_actions_html', $row_actions_html, intval( $result->group_id ) );
674
675 $output .= '<tr class="' . ( $i % 2 == 0 ? 'even' : 'odd' ) . '">';
676
677 $columns = array( 'checkbox' => array() ) + $columns;
678 foreach ( $columns as $key => $column ) {
679 switch ( $key ) {
680 case 'checkbox':
681 $output .= '<th class="check-column">';
682 $output .= '<input type="checkbox" value="' . esc_attr( $result->group_id ) . '" name="group_ids[]"/>';
683 $output .= '</th>';
684 break;
685 case 'group_id':
686 $output .= '<td class="group-id">';
687 $output .= $result->group_id;
688 $output .= '</td>';
689 break;
690 case 'name':
691 $output .= '<td class="group-name">';
692 $output .= sprintf(
693 '<a href="%s">%s</a>',
694 esc_url( $edit_url ),
695 $result->name ? stripslashes( wp_filter_nohtml_kses( $result->name ) ) : ''
696 );
697 $output .= $row_actions_html;
698 $output .= '</td>';
699 break;
700 case 'members':
701 $output .= '<td class="group-members">';
702 $user_ids = $group->get_user_ids();
703 $user_count = is_array( $user_ids ) ? count( $user_ids ) : 0; // guard against null when there are no users
704 $output .= sprintf(
705 '<a href="%s">%s</a>',
706 esc_url( $users_url ),
707 $user_count
708 );
709 $output .= '</td>';
710 break;
711 case 'description':
712 $output .= '<td class="group-description">';
713 $output .= $result->description ? stripslashes( wp_filter_nohtml_kses( $result->description ) ) : '';
714 $output .= '</td>';
715 break;
716 case 'capabilities':
717 $output .= '<td class="capabilities">';
718 $group_capabilities = $group->get_capabilities();
719 $group_capabilities_deep = $group->get_capabilities_deep();
720 usort( $group_capabilities_deep, array( 'Groups_Utility', 'cmp' ) );
721 if ( count( $group_capabilities_deep ) > 0 ) {
722 $output .= '<ul>';
723 foreach ( $group_capabilities_deep as $group_capability ) {
724 $output .= '<li>';
725 $class = '';
726 if ( empty( $group_capabilities ) || !in_array( $group_capability, $group_capabilities ) ) {
727 $class = 'inherited';
728 }
729 $output .= sprintf( '<span class="%s">', $class );
730 $output .= stripslashes( wp_filter_nohtml_kses( $group_capability->get_capability() ) );
731 $output .= '</span>';
732 $output .= '</li>';
733 }
734 $output .= '</ul>';
735 } else {
736 $output .= esc_html__( 'This group has no capabilities.', 'groups' );
737 }
738 $output .= '</td>';
739 break;
740 default:
741 $output .= sprintf( '<td class="custom-column %s">', esc_attr( $key ) );
742 /**
743 * Provide the row's output for the column identified by $key for the group given by its ID.
744 *
745 * @param string $content column content
746 * @param string $key the column key
747 * @param int $group_id the group's ID
748 *
749 * @return string content HTML
750 */
751 $output .= apply_filters( 'groups_admin_groups_column_content', '', $key, $group->get_group_id() );
752 $output .= '</td>'; // .custom-column ...
753 }
754 }
755 $output .= '</tr>';
756 }
757 } else {
758 $output .= '<tr>';
759 $output .= sprintf( '<td colspan="%d">', esc_attr( $column_count ) );
760 $output .= esc_html__( 'There are no results.', 'groups' );
761 $output .= '</td>';
762 $output .= '</tr>';
763 }
764
765 $output .= '</tbody>';
766 $output .= '</table>';
767
768 $output .= Groups_UIE::render_add_titles( '.groups-overview table td' );
769
770 $output .= '</form>'; // #groups-action
771
772 if ( $paginate ) {
773 require_once GROUPS_CORE_LIB . '/class-groups-pagination.php';
774 $pagination = new Groups_Pagination($count, null, $row_count);
775 $output .= '<div class="tablenav bottom">';
776 $output .= $pagination->pagination( 'bottom' );
777 $output .= '</div>';
778 }
779
780 $output .= '</div>'; // .groups-overview
781 $output .= '</div>'; // .manage-groups
782
783 echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
784 } // function groups_admin_groups()
785