$per_page, 'offset' => max( 0, (int) $atts['offset'] ) + ( ( $page - 1 ) * $per_page ), 'orderby' => airowp_query_sanitize_user_orderby( (string) $atts['orderBy'] ), 'order' => 'ASC' === strtoupper( (string) $atts['order'] ) ? 'ASC' : 'DESC', ); // Search — attribute-bound OR URL-bound via bindSearchTo. $search = (string) $atts['search']; if ( ! empty( $atts['bindSearchTo'] ) && isset( $context['params'][ $atts['bindSearchTo'] ] ) ) { $bound = $context['params'][ $atts['bindSearchTo'] ]; $search = is_scalar( $bound ) ? (string) $bound : ''; } if ( '' !== $search ) { // Wildcard search like WP_User_Query default. $args['search'] = '*' . $search . '*'; $args['search_columns'] = array( 'user_login', 'user_nicename', 'display_name' ); // Match core's users endpoint: email is searchable only by people who // can list users. Anyone else — every visitor to a public directory // with a search box — could otherwise test whether an address belongs // to an account. if ( current_user_can( 'list_users' ) ) { $args['search_columns'][] = 'user_email'; } } /** This filter is documented in src/blocks/query/render-posts.php */ $args = apply_filters( 'airowp_query_args', $args, $atts, $context ); $query_id = isset( $context['query_id'] ) ? (string) $context['query_id'] : ''; if ( '' !== $query_id ) { /** This filter is documented in src/blocks/query/render-posts.php */ $args = apply_filters( 'airowp_query_' . $query_id . '_args', $args, $atts, $context ); } $query = new WP_User_Query( $args ); $users = (array) $query->get_results(); $total_users = (int) $query->get_total(); $items_html = ''; $item_index = 0; foreach ( $users as $user ) { $items_html .= airowp_query_render_item( (string) $context['inner_html'], array( 'airo-wp/currentItemId' => (int) $user->ID, 'airo-wp/currentItemType' => 'user', 'index' => $item_index, 'airo-wp/itemIndex' => $item_index, ), $atts['itemTagName'] ); ++$item_index; } $totals = airowp_query_paginate_totals( $total_users, (int) $atts['offset'], $per_page ); $state = array( 'totalItems' => $totals['totalItems'], 'totalPages' => $totals['totalPages'], 'page' => $page, ); airowp_query_set_last_state( $query_id, $state ); return array( 'html' => airowp_query_wrap( $items_html, $atts, $context, $context['wrapper_attrs'] ?? null ), 'items_html' => $items_html, 'totalPages' => $state['totalPages'], 'totalItems' => $state['totalItems'], ); } /** * Whitelist orderby values acceptable to WP_User_Query. * * The block's `orderBy` attribute defaults to `date` (a post orderby), so * map that to a sensible user equivalent. * * @param string $orderby Requested orderby value. * @return string Safe WP_User_Query orderby value. */ function airowp_query_sanitize_user_orderby( $orderby ) { $allowed = array( 'ID', 'id', 'user_registered', 'registered', 'display_name', 'name', 'login', 'user_login', 'nicename', 'user_nicename', 'email', 'user_email' ); if ( 'date' === $orderby ) { return 'user_registered'; } if ( 'title' === $orderby ) { return 'display_name'; } // Match core's users endpoint: sorting by email needs list_users, or the // order leaks addresses to anyone who can register and see where they land. if ( in_array( $orderby, array( 'email', 'user_email' ), true ) && ! current_user_can( 'list_users' ) ) { return 'display_name'; } return in_array( $orderby, $allowed, true ) ? $orderby : 'user_registered'; } endif;