%s group has been created.', 'groups' ), $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '' ) ); } break; case 'edit' : if ( !( $group_id = groups_admin_groups_edit_submit() ) ) { return groups_admin_groups_edit( groups_sanitize_post( 'group-id-field' ) ); } else { $group = Groups_Group::read( $group_id ); Groups_Admin::add_message( sprintf( /* translators: group name */ __( 'The %s group has been updated.', 'groups' ), $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '' ) ); } break; case 'remove' : if ( $group_id = groups_admin_groups_remove_submit() ) { Groups_Admin::add_message( __( 'The group has been deleted.', 'groups' ) ); } break; // bulk actions on groups: add capabilities, remove capabilities, remove groups case 'groups-action' : if ( groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_ACTION_NONCE, 'admin' ) ) { $group_ids = groups_sanitize_post( 'group_ids' ); $bulk_action = groups_sanitize_post( 'bulk-action' ); if ( is_array( $group_ids ) && ( $bulk_action !== null ) ) { foreach ( $group_ids as $group_id ) { switch ( $bulk_action ) { case 'add-capability' : $capabilities_id = groups_sanitize_post( 'capability_id' ); if ( is_array( $capabilities_id ) ) { foreach ( $capabilities_id as $capability_id ) { Groups_Group_Capability::create( array( 'group_id' => $group_id, 'capability_id' => $capability_id ) ); } } break; case 'remove-capability' : $capabilities_id = groups_sanitize_post( 'capability_id' ); if ( is_array( $capabilities_id ) ) { foreach ( $capabilities_id as $capability_id ) { Groups_Group_Capability::delete( $group_id, $capability_id ); } } break; case 'remove-group' : $bulk_confirm = isset( $_POST['confirm'] ) ? true : false; // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( $bulk_confirm ) { groups_admin_groups_bulk_remove_submit(); } else { return groups_admin_groups_bulk_remove(); } break; default: if ( has_action( 'groups_admin_groups_handle_bulk_action' ) ) { /** * Handle the requested bulk action. * * @param string $bulk_action the requested bulk action * @param string|int $group_id the requested group ID */ do_action( 'groups_admin_groups_handle_bulk_action', $bulk_action, $group_id ); } } } } } break; default: if ( has_filter( 'groups_admin_groups_handle_action_submit' ) ) { /** * Handle a requested action after $_POST. * * @since 3.7.0 * * @param boolean $handle whether to handle the posted action * @param string $action the requested action * * @return boolean whether the posted data was accepted and action was taken */ if ( apply_filters( 'groups_admin_groups_handle_action_submit', false, groups_sanitize_post( 'action' ) ) ) { /** * Fires after the posted data for an action was accepted. * * Should produce output to provide feedback to the user. * * @since 3.7.0 * * @param string $action the requested action */ do_action( 'groups_admin_groups_handle_action_confirm', groups_sanitize_post( 'action' ) ); } else { /** * Fires after the posted data for an action was rejected. * * Should produce output to provide feedback to the user. * * @since 3.7.0 * * @param string $action the requested action */ do_action( 'groups_admin_groups_handle_action_reject', groups_sanitize_post( 'action' ) ); return; } } } } else if ( isset( $_GET['action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended // handle action request - show form switch ( groups_sanitize_get( 'action' ) ) { case 'add' : return groups_admin_groups_add(); break; case 'edit' : if ( isset( $_GET['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended return groups_admin_groups_edit( groups_sanitize_get( 'group_id' ) ); } break; case 'remove' : if ( isset( $_GET['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended return groups_admin_groups_remove( groups_sanitize_get( 'group_id' ) ); } break; default: if ( isset( $_GET['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended if ( has_action( 'groups_admin_groups_handle_action' ) ) { /** * Handle the requested action and produce the corresponding output. * * @param string $action the requested action * @param string|int $group_id the requested group ID */ do_action( 'groups_admin_groups_handle_action', groups_sanitize_get( 'action' ), groups_sanitize_get( 'group_id' ) ); return; } } } } // // group table // if ( isset( $_POST['clear_filters'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing isset( $_POST['group_id'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing isset( $_POST['group_name'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing ) { if ( !groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_FILTER_NONCE, 'admin' ) ) { wp_die( esc_html__( 'Access denied.', 'groups' ) ); } } // filters $group_id = Groups_Options::get_user_option( 'groups_group_id', null ); $group_name = Groups_Options::get_user_option( 'groups_group_name', null ); if ( isset( $_POST['clear_filters'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing Groups_Options::delete_user_option( 'groups_group_id' ); Groups_Options::delete_user_option( 'groups_group_name' ); $group_id = null; $group_name = null; } else if ( isset( $_POST['submitted'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing // filter by name if ( !empty( $_POST['group_name'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing $group_name = groups_sanitize_post( 'group_name' ); Groups_Options::update_user_option( 'groups_group_name', $group_name ); } // filter by group id if ( !empty( $_POST['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing $group_id = intval( groups_sanitize_post( 'group_id' ) ); Groups_Options::update_user_option( 'groups_group_id', $group_id ); } else if ( isset( $_POST['group_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing // empty && isset => '' => all $group_id = null; Groups_Options::delete_user_option( 'groups_group_id' ); } } if ( isset( $_POST['row_count'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( !groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_NONCE, 'admin' ) ) { wp_die( esc_html__( 'Access denied.', 'groups' ) ); } } if ( isset( $_POST['paged'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( !groups_verify_post_nonce( GROUPS_ADMIN_GROUPS_NONCE, 'admin' ) ) { wp_die( esc_html__( 'Access denied.', 'groups' ) ); } } $current_url = groups_get_current_url(); $current_url = remove_query_arg( 'paged', $current_url ); $current_url = remove_query_arg( 'action', $current_url ); $current_url = remove_query_arg( 'group_id', $current_url ); $group_table = _groups_get_tablename( 'group' ); $user_group_table = _groups_get_tablename( 'user_group' ); $output .= '
' . '

' . _x( 'Groups', 'page-title', 'groups' ) . sprintf( '', esc_attr__( 'Click to add a new group', 'groups' ), esc_url( $current_url . '&action=add' ) ) . sprintf( '%s', esc_attr__( 'Add', 'groups' ), esc_url( GROUPS_PLUGIN_URL . 'images/add.png' ) ) . sprintf( '%s', esc_html__( 'New Group', 'groups' ) ) . '' . '

'; $output .= Groups_Admin::render_messages(); $row_count = intval( groups_sanitize_post( 'row_count' ) ?? 0 ); if ($row_count <= 0) { $row_count = Groups_Options::get_user_option( 'groups_per_page', GROUPS_GROUPS_PER_PAGE ); } else { Groups_Options::update_user_option('groups_per_page', $row_count ); } $offset = intval( groups_sanitize_get( 'offset' ) ?? 0 ); if ( $offset < 0 ) { $offset = 0; } $paged = intval( groups_sanitize_request( 'paged' ) ?? 0 ); if ( $paged < 0 ) { $paged = 0; } $orderby = groups_sanitize_get( 'orderby' ); switch ( $orderby ) { case 'group_id' : case 'name' : case 'description' : case 'members': break; default: $orderby = 'name'; } $order = groups_sanitize_get( 'order' ); switch ( $order ) { case 'asc' : case 'ASC' : $switch_order = 'DESC'; break; case 'desc' : case 'DESC' : $switch_order = 'ASC'; break; default: $order = 'ASC'; $switch_order = 'DESC'; } $filters = array( " 1=%d " ); $filter_params = array( 1 ); if ( $group_id ) { $filters[] = " $group_table.group_id = %d "; $filter_params[] = $group_id; } if ( $group_name ) { $filters[] = " $group_table.name LIKE %s "; $filter_params[] = '%' . $wpdb->esc_like( $group_name ) . '%'; } if ( !empty( $filters ) ) { // @phpstan-ignore empty.variable $filters = " WHERE " . implode( " AND ", $filters ); } else { $filters = ''; } $count_query = $wpdb->prepare( "SELECT COUNT(*) FROM $group_table $filters", $filter_params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare $count = $wpdb->get_var( $count_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared if ( $count > $row_count ) { $paginate = true; } else { $paginate = false; } $pages = ceil ( $count / $row_count ); if ( $paged > $pages ) { $paged = $pages; } if ( $paged != 0 ) { $offset = ( $paged - 1 ) * $row_count; } switch ( $orderby ) { case 'members': $query = $wpdb->prepare( // nosemgrep: audit.php.wp.security.sqli.input-in-sinks "SELECT $group_table.*, COUNT($user_group_table.user_id) AS members " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare "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 "$filters " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare "GROUP BY $group_table.group_id " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare "ORDER BY COUNT($user_group_table.user_id) $order " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare "LIMIT $row_count OFFSET $offset", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare $filter_params ); break; default: $query = $wpdb->prepare( // nosemgrep: audit.php.wp.security.sqli.input-in-sinks "SELECT * FROM $group_table $filters ORDER BY $orderby $order LIMIT $row_count OFFSET $offset", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare $filter_params ); } /** * Allows to modify the query for the groups table. * * @since 3.7.0 * * @param string $query the query * * @return string */ $query = apply_filters( 'groups_admin_groups_query', $query ); // nosemgrep: audit.php.wp.security.sqli.input-in-sinks $results = $wpdb->get_results( $query, OBJECT ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared /** * Allows to modify the results for the groups table. * * @since 3.7.0 * * @param object[] $results result to show * * @return object[] */ $results = apply_filters( 'groups_admin_groups_results', $results ); $columns = array( 'group_id' => array( 'label' => __( 'ID', 'groups' ), 'sortable' => true ), 'name' => array( 'label' => __( 'Group', 'groups' ), 'sortable' => true ), 'members' => array( 'label' => __( 'Members', 'groups' ), 'sortable' => true ), 'description' => array( 'label' => __( 'Description', 'groups' ), 'sortable' => true ), 'capabilities' => array( 'label' => __( 'Capabilities', 'groups' ), 'sortable' => false ) ); /** * Allows to modify the columns of the groups table. * * @since 3.7.0 * * @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 * * @return array */ $columns = apply_filters( 'groups_admin_groups_columns', $columns ); unset( $columns['checkbox'] ); foreach ( $columns as $key => $column ) { if ( preg_replace( '/[^a-zA-Z0-9_-]/', '', $key ) !== $key ) { unset( $columns[$key] ); } } $column_count = count( $columns ) + 1; $output .= '
'; $filters_html = '
'; $filters_html .= '
'; $filters_html .= '
'; $filters_html .= '' . esc_html__( 'Filters', 'groups' ) . ''; $filters_html .= '' . ' '; $filters_html .= '' . ' '; /** * Allows to add markup after the standard filter fields of the groups table. * * @since 3.7.0 * * @param string $markup additional markup * * @return string */ $filters_html .= apply_filters( 'groups_admin_groups_filters_fields_epilogue', '' ); $filters_html .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_FILTER_NONCE, true, false ); $filters_html .= '' . ' '; $filters_html .= ''; $filters_html .= ''; $filters_html .= '
'; $filters_html .= '
'; $filters_html .= '
'; // .filters /** * Allows to process the HTML of the filters section of the groups table. * * @since 3.7.0 * * @param string $filters_html markup * * @return string */ $output .= apply_filters( 'groups_admin_groups_filters_html', $filters_html ); $capability_table = _groups_get_tablename( "capability" ); // $group_capability_table = _groups_get_tablename( "group_capability" ); // capabilities select // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" ); $capabilities_select = sprintf( ''; $capabilities_select .= Groups_UIE::render_select( '.select.capability' ); $output .= '
'; $output .= '
'; $bulk_html = '
'; $bulk_html .= '
'; $bulk_html .= $capabilities_select; $bulk_html .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false ); $bulk_html .= '
'; $bulk_html .= ''; /** * Allows to add markup after the standard bulk actions fields of the groups table. * * @since 3.7.0 * * @param string $markup additional markup * * @return string */ $filters_html .= apply_filters( 'groups_admin_groups_bulk_actions_fields_epilogue', '' ); $bulk_html .= sprintf( '', esc_attr__( 'Apply', 'groups' ) ); $bulk_html .= ''; $bulk_html .= '
'; /** * Allows to process the HTML of the bulk actions section of the groups table. * * @since 3.7.0 * * @param string $bulk_html markup * * @return string */ $output .= apply_filters( 'groups_admin_groups_bulk_actions_html', $bulk_html ); if ( $paginate ) { require_once GROUPS_CORE_LIB . '/class-groups-pagination.php'; $pagination = new Groups_Pagination( $count, null, $row_count ); $output .= $pagination->pagination( 'top' ); } $output .= '
'; $output .= ''; $output .= ''; $output .= ''; $output .= '
'; // .page-options.right $output .= '
'; // .tablenav.top $output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_NONCE, true, false ); $output .= ''; $output .= ''; $output .= ''; $output .= ''; foreach ( $columns as $key => $column ) { $options = array( 'orderby' => $key, 'order' => $switch_order ); $class = $key; if ( isset( $column['sortable'] ) && $column['sortable'] ) { if ( strcmp( $key, $orderby ) == 0 ) { $lorder = strtolower( $order ); $class = "$key manage-column sorted $lorder"; } else { $class = "$key manage-column sortable"; } $heading = sprintf( '%s'. '' . ''. ''. '' . // .sorting-indicators '', esc_url( add_query_arg( $options, $current_url ) ), esc_html( $column['label'] ) ); } else { $heading = esc_html( $column['label'] ); } $output .= sprintf( '', esc_attr( $class ), $heading ); } $output .= ''; $output .= ''; $output .= ''; if ( count( $results ) > 0 ) { for ( $i = 0; $i < count( $results ); $i++ ) { $result = $results[$i]; /** * @var Groups_Group */ $group = new Groups_Group( $result->group_id ); // Construct the "edit" URL. $edit_url = add_query_arg( array( 'group_id' => intval( $result->group_id ), 'action' => 'edit', 'paged' => $paged ), $current_url ); // Construct the "delete" URL. $delete_url = add_query_arg( array( 'group_id' => intval( $result->group_id ), 'action' => 'remove', 'paged' => $paged ), $current_url ); $users_url = add_query_arg( array( 'filter_group_ids[0]' => intval( $result->group_id ) ), admin_url( 'users.php' ) ); // Construct row actions for this group. $row_actions = array( 'edit' => sprintf( ' %s', esc_url( $edit_url ), esc_url( GROUPS_PLUGIN_URL . 'images/edit.png' ), esc_html__( 'Edit', 'groups' ) ) ); if ( $result->name !== Groups_Registered::REGISTERED_GROUP_NAME ) { $row_actions['remove trash'] = sprintf( ' %s', esc_url( $delete_url ), esc_url( GROUPS_PLUGIN_URL . 'images/remove.png' ), esc_html__( 'Remove', 'groups' ) ); } /** * Allows to alter the row actions for a group in the groups table. * * @since 3.7.0 * * @param array $row_actions row actions as HTML * @param int $group_id ID of the group * * @return array */ $row_actions = apply_filters( 'groups_admin_groups_row_actions', $row_actions, intval( $result->group_id ) ); $n = 1; $row_actions_html = '
'; foreach ( $row_actions as $row_action_key => $row_action ) { $row_actions_html .= sprintf( '', esc_attr( $row_action_key ) ); $row_actions_html .= $row_action; $row_actions_html .= ''; if ( $n < count( $row_actions ) ) { $row_actions_html .= ' | '; } $n++; } $row_actions_html .= '
'; // .row-actions /** * Allows to process the HTML of the row actions for a group in the groups table. * * @since 3.7.0 * * @param string $row_actions_html markup * @param int $group_id ID of the group * * @return string */ $row_actions_html = apply_filters( 'groups_admin_groups_row_actions_html', $row_actions_html, intval( $result->group_id ) ); $output .= ''; $columns = array( 'checkbox' => array() ) + $columns; foreach ( $columns as $key => $column ) { switch ( $key ) { case 'checkbox': $output .= ''; break; case 'group_id': $output .= ''; break; case 'name': $output .= ''; break; case 'members': $output .= ''; break; case 'description': $output .= ''; break; case 'capabilities': $output .= ''; break; default: $output .= sprintf( ''; // .custom-column ... } } $output .= ''; } } else { $output .= ''; $output .= sprintf( ''; $output .= ''; } $output .= ''; $output .= '
%s
'; $output .= ''; $output .= ''; $output .= $result->group_id; $output .= ''; $output .= sprintf( '%s', esc_url( $edit_url ), $result->name ? stripslashes( wp_filter_nohtml_kses( $result->name ) ) : '' ); $output .= $row_actions_html; $output .= ''; $user_ids = $group->get_user_ids(); $user_count = is_array( $user_ids ) ? count( $user_ids ) : 0; // guard against null when there are no users $output .= sprintf( '%s', esc_url( $users_url ), $user_count ); $output .= ''; $output .= $result->description ? stripslashes( wp_filter_nohtml_kses( $result->description ) ) : ''; $output .= ''; $group_capabilities = $group->get_capabilities(); $group_capabilities_deep = $group->get_capabilities_deep(); usort( $group_capabilities_deep, array( 'Groups_Utility', 'cmp' ) ); if ( count( $group_capabilities_deep ) > 0 ) { $output .= '
    '; foreach ( $group_capabilities_deep as $group_capability ) { $output .= '
  • '; $class = ''; if ( empty( $group_capabilities ) || !in_array( $group_capability, $group_capabilities ) ) { $class = 'inherited'; } $output .= sprintf( '', $class ); $output .= stripslashes( wp_filter_nohtml_kses( $group_capability->get_capability() ) ); $output .= ''; $output .= '
  • '; } $output .= '
'; } else { $output .= esc_html__( 'This group has no capabilities.', 'groups' ); } $output .= '
', esc_attr( $key ) ); /** * Provide the row's output for the column identified by $key for the group given by its ID. * * @param string $content column content * @param string $key the column key * @param int $group_id the group's ID * * @return string content HTML */ $output .= apply_filters( 'groups_admin_groups_column_content', '', $key, $group->get_group_id() ); $output .= '
', esc_attr( $column_count ) ); $output .= esc_html__( 'There are no results.', 'groups' ); $output .= '
'; $output .= Groups_UIE::render_add_titles( '.groups-overview table td' ); $output .= '
'; // #groups-action if ( $paginate ) { require_once GROUPS_CORE_LIB . '/class-groups-pagination.php'; $pagination = new Groups_Pagination($count, null, $row_count); $output .= '
'; $output .= $pagination->pagination( 'bottom' ); $output .= '
'; } $output .= '
'; // .groups-overview $output .= '
'; // .manage-groups echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } // function groups_admin_groups()