'user', 'plural' => 'users', 'ajax' => false, ) ); } /** * Render dashboard tab content. */ public static function render_page() { $table = new self(); $table->prepare_items(); self::enqueue_assets( $table ); $stats = Activitysignature_Helpers::get_dashboard_stats(); $filters = self::get_filters(); $roles = wp_roles()->get_names(); $activity_url = Activitysignature_Helpers::get_admin_url( 'activity' ); $can_manage = self::can_manage_users(); ?>

display(); ?>
display(); ?> get_columns(); $hidden = array(); $sortable = $this->get_sortable_columns(); $this->_column_headers = array( $columns, $hidden, $sortable ); $settings = Activitysignature_Helpers::get_settings(); $per_page = max( 5, (int) $settings['items_per_page'] ); $current_page = $this->get_pagenum(); $offset = ( $current_page - 1 ) * $per_page; $this->items = $this->get_users_with_activity( $offset, $per_page ); $total = $this->get_total_users_count(); $this->set_pagination_args( array( 'total_items' => $total, 'per_page' => $per_page, 'total_pages' => (int) ceil( $total / $per_page ), ) ); } /** * Handle a dashboard delete before the screen renders. */ public static function handle_actions() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Routing only; process_actions() checks the nonce. $tab = isset( $_REQUEST['tab'] ) ? sanitize_key( wp_unslash( $_REQUEST['tab'] ) ) : 'dashboard'; if ( 'dashboard' !== $tab || ! self::can_manage_users() ) { return; } $action = self::requested_action(); if ( 'delete' !== $action ) { return; } $table = new self(); $table->process_actions(); } /** * Read the list-table bulk action. * * @return string */ private static function requested_action() { // Filter submits are not bulk actions. process_actions() verifies the nonce before any delete. $filter_action = isset( $_REQUEST['filter_action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['filter_action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Routing only; process_actions() verifies the nonce. if ( '' !== $filter_action ) { return ''; } $action = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Routing only; process_actions() verifies the nonce. if ( '' === $action || '-1' === $action ) { $action = isset( $_REQUEST['action2'] ) ? sanitize_key( wp_unslash( $_REQUEST['action2'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Routing only; process_actions() verifies the nonce. } return $action; } /** * Handle single-row and bulk delete actions. */ private function process_actions() { if ( ! self::can_manage_users() ) { return; } $action = $this->current_action(); if ( 'delete' !== $action ) { return; } $keep_paged = true; if ( ! empty( $_POST['activitysignature_delete_all_filtered'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['activitysignature_delete_all_filtered'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified on the next line. check_admin_referer( 'activitysignature_users_bulk', 'activitysignature_users_nonce' ); $result = $this->delete_filtered_accounts(); $keep_paged = false; } elseif ( ! empty( $_POST['users'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified on the next line. check_admin_referer( 'activitysignature_users_bulk', 'activitysignature_users_nonce' ); $user_ids = array_map( 'absint', (array) wp_unslash( $_POST['users'] ) ); $result = $this->delete_user_accounts( $user_ids ); $result['more'] = false; } elseif ( ! empty( $_GET['user'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified on the next line. $user_id = absint( wp_unslash( $_GET['user'] ) ); check_admin_referer( 'activitysignature_delete_user_' . $user_id ); $result = $this->delete_user_accounts( array( $user_id ) ); $result['more'] = false; } else { return; } $redirect = Activitysignature_Helpers::get_admin_url( 'dashboard' ); $filters = self::get_filters(); $map = array( 's' => 'search', 'user_role' => 'role', 'login_status' => 'login_status', 'order_status' => 'order_status', 'registered_from' => 'registered_from', 'registered_to' => 'registered_to', 'updated_from' => 'updated_from', 'updated_to' => 'updated_to', 'login_from' => 'login_from', 'login_to' => 'login_to', ); foreach ( $map as $name => $key ) { if ( ! empty( $filters[ $key ] ) ) { $redirect = add_query_arg( $name, $filters[ $key ], $redirect ); } } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['orderby'] ) ) { $orderby = sanitize_key( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( in_array( $orderby, array( 'user_login', 'user_email', 'created_date', 'last_updated', 'last_login' ), true ) ) { $redirect = add_query_arg( 'orderby', $orderby, $redirect ); } } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['order'] ) ) { $order = strtolower( sanitize_key( wp_unslash( $_GET['order'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( in_array( $order, array( 'asc', 'desc' ), true ) ) { $redirect = add_query_arg( 'order', $order, $redirect ); } } if ( $keep_paged ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['paged'] ) ) { $paged = absint( wp_unslash( $_GET['paged'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( $paged > 1 ) { $redirect = add_query_arg( 'paged', $paged, $redirect ); } } } $args = array( 'activitysignature_deleted' => (int) $result['deleted'], 'activitysignature_skipped' => (int) $result['skipped'], ); if ( ! empty( $result['more'] ) ) { $args['activitysignature_more'] = '1'; } wp_safe_redirect( add_query_arg( $args, $redirect ) ); exit; } /** * Whether the current user may delete/remove users. * * @return bool */ public static function can_manage_users() { if ( is_multisite() ) { return current_user_can( 'remove_users' ); } return current_user_can( 'delete_users' ); } /** * User IDs matching the current filters, capped to one batch. * * @param int $limit Maximum IDs to return. * @param int[] $exclude User IDs to leave out of this batch. * * @return int[] */ private function get_filtered_user_ids( $limit, array $exclude = array() ) { $args = $this->get_query_args(); $args['fields'] = 'ID'; $args['number'] = max( 1, (int) $limit ); unset( $args['offset'] ); $exclude = array_values( array_unique( array_filter( array_map( 'absint', $exclude ) ) ) ); if ( $exclude ) { // Keep exclude inside get_users() so it stays a WP_User_Query argument. $ids = get_users( array_merge( $args, array( 'exclude' => $exclude, ) ) ); } else { $ids = get_users( $args ); } return array_map( 'absint', (array) $ids ); } /** * Transient storing accounts this delete pass could not remove. * * @return string */ private static function delete_skip_transient_key() { return Activitysignature_Helpers::TRANSIENT_DELETE_SKIP . get_current_user_id(); } /** * Delete filtered accounts in bounded batches and report whether more remain. * * @return array{deleted:int,skipped:int,more:bool} */ private function delete_filtered_accounts() { $batch_size = 50; $max_batches = 4; $key = self::delete_skip_transient_key(); $continue = isset( $_POST['activitysignature_delete_continue'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['activitysignature_delete_continue'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Caller verified activitysignature_users_bulk. $exclude = array( get_current_user_id() ); if ( $continue ) { $saved = get_transient( $key ); if ( is_array( $saved ) ) { $exclude = array_merge( $exclude, array_map( 'absint', $saved ) ); } } else { delete_transient( $key ); } $deleted = 0; $skipped = 0; $started = microtime( true ); $rounds = 0; while ( $rounds < $max_batches && ( microtime( true ) - $started ) < 8 ) { $ids = $this->get_filtered_user_ids( $batch_size, $exclude ); if ( empty( $ids ) ) { break; } $result = $this->delete_user_accounts( $ids ); $deleted += (int) $result['deleted']; $skipped += (int) $result['skipped']; if ( ! empty( $result['skipped_ids'] ) ) { $exclude = array_merge( $exclude, $result['skipped_ids'] ); } ++$rounds; if ( 0 === (int) $result['deleted'] ) { break; } } $remember = array_values( array_unique( array_filter( array_map( 'absint', array_diff( $exclude, array( get_current_user_id() ) ) ) ) ) ); $more = ! empty( $this->get_filtered_user_ids( 1, array_merge( array( get_current_user_id() ), $remember ) ) ); if ( $more && $remember && count( $remember ) <= 500 ) { set_transient( $key, $remember, 15 * MINUTE_IN_SECONDS ); } else { if ( count( $remember ) > 500 ) { $more = false; } delete_transient( $key ); } return array( 'deleted' => $deleted, 'skipped' => $skipped, 'more' => $more, ); } /** * Build shared user query args. * * @return array */ private function get_query_args() { $orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : 'user_login'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order = isset( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : 'asc'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $allowed = array( 'user_login', 'user_email', 'created_date', 'last_updated', 'last_login' ); if ( ! in_array( $orderby, $allowed, true ) ) { $orderby = 'user_login'; } $order = 'DESC' === strtoupper( $order ) ? 'DESC' : 'ASC'; $filters = self::get_filters(); $query_orderby = $orderby; $meta_key = ''; if ( 'created_date' === $orderby ) { $query_orderby = 'registered'; } elseif ( 'last_login' === $orderby ) { $query_orderby = 'meta_value'; $meta_key = Activitysignature_Helpers::get_user_meta_key( 'last_login' ); } elseif ( 'last_updated' === $orderby ) { $query_orderby = 'meta_value'; $meta_key = Activitysignature_Helpers::get_user_meta_key( 'last_update' ); } $args = array( 'orderby' => $query_orderby, 'order' => $order, ); if ( 'never' === $filters['login_status'] ) { // Filter to users with no last-login meta. Meta-based sort is meaningless here. $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query array( 'key' => Activitysignature_Helpers::get_user_meta_key( 'last_login' ), 'compare' => 'NOT EXISTS', ), ); if ( in_array( $orderby, array( 'last_login', 'last_updated' ), true ) ) { $args['orderby'] = 'login'; } } elseif ( 'logged_in' === $filters['login_status'] ) { $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query array( 'key' => Activitysignature_Helpers::get_user_meta_key( 'last_login' ), 'compare' => 'EXISTS', ), ); if ( 'last_login' === $orderby ) { $args['meta_key'] = Activitysignature_Helpers::get_user_meta_key( 'last_login' ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key } elseif ( 'last_updated' === $orderby ) { $args['orderby'] = 'login'; } } elseif ( $meta_key ) { $args['meta_key'] = $meta_key; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key // Include users missing the meta so sort does not drop them. $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'relation' => 'OR', array( 'key' => $meta_key, 'compare' => 'EXISTS', ), array( 'key' => $meta_key, 'compare' => 'NOT EXISTS', ), ); } if ( $filters['search'] ) { $args['search'] = '*' . $filters['search'] . '*'; $args['search_columns'] = array( 'user_login', 'user_email' ); } if ( $filters['role'] ) { $args['role'] = $filters['role']; } if ( $filters['registered_from'] || $filters['registered_to'] ) { // user_registered is GMT; convert local calendar bounds to GMT datetimes. $date_query = array( 'inclusive' => true, 'column' => 'user_registered' ); if ( $filters['registered_from'] ) { $date_query['after'] = get_gmt_from_date( $filters['registered_from'] . ' 00:00:00' ); } if ( $filters['registered_to'] ) { $date_query['before'] = get_gmt_from_date( $filters['registered_to'] . ' 23:59:59' ); } $args['date_query'] = array( $date_query ); } if ( $filters['updated_from'] || $filters['updated_to'] ) { $updated_query = array( 'key' => Activitysignature_Helpers::get_user_meta_key( 'last_update' ), 'type' => 'DATETIME', ); if ( $filters['updated_from'] && $filters['updated_to'] ) { $updated_query['value'] = array( $filters['updated_from'] . ' 00:00:00', $filters['updated_to'] . ' 23:59:59', ); $updated_query['compare'] = 'BETWEEN'; } elseif ( $filters['updated_from'] ) { $updated_query['value'] = $filters['updated_from'] . ' 00:00:00'; $updated_query['compare'] = '>='; } else { $updated_query['value'] = $filters['updated_to'] . ' 23:59:59'; $updated_query['compare'] = '<='; } if ( isset( $args['meta_query'] ) ) { $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'relation' => 'AND', $args['meta_query'], $updated_query, ); } else { $args['meta_query'] = array( $updated_query ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query } } if ( $filters['login_from'] || $filters['login_to'] ) { $login_query = self::get_meta_date_query( Activitysignature_Helpers::get_user_meta_key( 'last_login' ), $filters['login_from'], $filters['login_to'] ); if ( isset( $args['meta_query'] ) ) { $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- User meta date filter via WP_User_Query API. 'relation' => 'AND', $args['meta_query'], $login_query, ); } else { $args['meta_query'] = array( $login_query ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- User meta date filter via WP_User_Query API. } } if ( Activitysignature_WooCommerce::is_orders_column_enabled() && $filters['order_status'] ) { $args['activitysignature_order_status'] = $filters['order_status']; } return $args; } /** * Read and validate dashboard filters. * * @return array */ private static function get_filters() { $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $role = isset( $_GET['user_role'] ) ? sanitize_key( wp_unslash( $_GET['user_role'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $login_status = isset( $_GET['login_status'] ) ? sanitize_key( wp_unslash( $_GET['login_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order_status = isset( $_GET['order_status'] ) ? sanitize_key( wp_unslash( $_GET['order_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $registered_from = isset( $_GET['registered_from'] ) ? sanitize_text_field( wp_unslash( $_GET['registered_from'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $registered_to = isset( $_GET['registered_to'] ) ? sanitize_text_field( wp_unslash( $_GET['registered_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $updated_from = isset( $_GET['updated_from'] ) ? sanitize_text_field( wp_unslash( $_GET['updated_from'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $updated_to = isset( $_GET['updated_to'] ) ? sanitize_text_field( wp_unslash( $_GET['updated_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $login_from = isset( $_GET['login_from'] ) ? sanitize_text_field( wp_unslash( $_GET['login_from'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $login_to = isset( $_GET['login_to'] ) ? sanitize_text_field( wp_unslash( $_GET['login_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $allowed_roles = array_keys( wp_roles()->get_names() ); if ( ! in_array( $role, $allowed_roles, true ) ) { $role = ''; } if ( ! in_array( $login_status, array( 'logged_in', 'never' ), true ) ) { $login_status = ''; } if ( ! Activitysignature_WooCommerce::is_orders_column_enabled() || ! in_array( $order_status, array( 'has_orders', 'no_orders' ), true ) ) { $order_status = ''; } if ( ! self::is_valid_date( $registered_from ) ) { $registered_from = ''; } if ( ! self::is_valid_date( $registered_to ) ) { $registered_to = ''; } if ( ! self::is_valid_date( $updated_from ) ) { $updated_from = ''; } if ( ! self::is_valid_date( $updated_to ) ) { $updated_to = ''; } if ( ! self::is_valid_date( $login_from ) ) { $login_from = ''; } if ( ! self::is_valid_date( $login_to ) ) { $login_to = ''; } return array( 'search' => $search, 'role' => $role, 'login_status' => $login_status, 'order_status' => $order_status, 'registered_from' => $registered_from, 'registered_to' => $registered_to, 'updated_from' => $updated_from, 'updated_to' => $updated_to, 'login_from' => $login_from, 'login_to' => $login_to, ); } /** * Validate an optional YYYY-MM-DD filter value. * * @param string $date Date value. * * @return bool */ private static function is_valid_date( $date ) { return Activitysignature_Helpers::is_valid_date( $date ); } /** Build a datetime user-meta filter. */ private static function get_meta_date_query( $key, $from, $to ) { $query = array( 'key' => $key, 'type' => 'DATETIME' ); if ( $from && $to ) { $query['value'] = array( $from . ' 00:00:00', $to . ' 23:59:59' ); $query['compare'] = 'BETWEEN'; } elseif ( $from ) { $query['value'] = $from . ' 00:00:00'; $query['compare'] = '>='; } else { $query['value'] = $to . ' 23:59:59'; $query['compare'] = '<='; } return $query; } /** * Delete WordPress user accounts and assign their content to the acting user. * * @param int[] $user_ids User IDs. * * @return array{deleted:int,skipped:int,skipped_ids:int[]} */ private function delete_user_accounts( $user_ids ) { $deleted = 0; $skipped = 0; $skipped_ids = array(); $current = get_current_user_id(); if ( ! function_exists( 'wp_delete_user' ) ) { require_once ABSPATH . 'wp-admin/includes/user.php'; } foreach ( $user_ids as $user_id ) { $user_id = (int) $user_id; if ( $user_id <= 0 ) { $skipped ++; continue; } if ( ! get_userdata( $user_id ) ) { $skipped ++; $skipped_ids[] = $user_id; continue; } if ( $user_id === $current || $current <= 0 ) { $skipped ++; $skipped_ids[] = $user_id; continue; } $ok = false; if ( is_multisite() ) { if ( current_user_can( 'remove_user', $user_id ) ) { $removed = remove_user_from_blog( $user_id, get_current_blog_id(), $current ); $ok = $removed && ! is_wp_error( $removed ); } } elseif ( current_user_can( 'delete_user', $user_id ) ) { $ok = (bool) wp_delete_user( $user_id, $current ); } if ( $ok ) { $deleted ++; } else { $skipped ++; $skipped_ids[] = $user_id; } } return array( 'deleted' => $deleted, 'skipped' => $skipped, 'skipped_ids' => $skipped_ids, ); } /** * Columns. * * @return array */ public function get_columns() { $columns = array(); if ( self::can_manage_users() ) { $columns['cb'] = ''; } $columns['user_login'] = __( 'Username', 'activitysignature' ); $columns['user_email'] = __( 'Email', 'activitysignature' ); $columns['user_roles'] = __( 'Role', 'activitysignature' ); $columns['created_date'] = __( 'Created', 'activitysignature' ); $columns['last_updated'] = __( 'Last Updated', 'activitysignature' ); $columns['last_login'] = __( 'Last Login', 'activitysignature' ); if ( Activitysignature_WooCommerce::is_orders_column_enabled() ) { $columns['wc_orders'] = __( 'Orders', 'activitysignature' ); } if ( self::can_manage_users() ) { $columns['actions'] = __( 'Actions', 'activitysignature' ); } return $columns; } /** * Sortable columns. * * @return array */ public function get_sortable_columns() { return array( 'user_login' => array( 'user_login', false ), 'user_email' => array( 'user_email', false ), 'created_date' => array( 'created_date', false ), 'last_updated' => array( 'last_updated', false ), 'last_login' => array( 'last_login', false ), ); } /** * Fetch users for the current page. * * @param int $offset Offset. * @param int $per_page Per page. * * @return array */ private function get_users_with_activity( $offset, $per_page ) { $args = $this->get_query_args(); $args['number'] = $per_page; $args['offset'] = $offset; $users = get_users( $args ); $user_ids = wp_list_pluck( $users, 'ID' ); $order_counts = array(); if ( ! empty( $user_ids ) ) { update_meta_cache( 'user', $user_ids ); Activitysignature_User_Tracking::prime_last_logins( $user_ids ); if ( Activitysignature_WooCommerce::is_orders_column_enabled() ) { $order_counts = Activitysignature_WooCommerce::get_order_counts( $user_ids ); } } $users_data = array(); foreach ( $users as $user ) { $row = array( 'ID' => $user->ID, 'user_login' => $user->user_login, 'user_email' => $user->user_email, 'user_roles' => $user->roles, 'created_date' => Activitysignature_User_Tracking::get_user_created_date( $user->ID ), 'last_updated' => Activitysignature_User_Tracking::get_last_update( $user->ID ), 'last_login' => Activitysignature_User_Tracking::get_last_login( $user->ID ), ); if ( Activitysignature_WooCommerce::is_orders_column_enabled() ) { $row['wc_orders'] = isset( $order_counts[ $user->ID ] ) ? (int) $order_counts[ $user->ID ] : 0; } $users_data[] = $row; } return $users_data; } /** * Total users for pagination. * * @return int */ private function get_total_users_count() { $args = $this->get_query_args(); $args['count_total'] = true; $args['fields'] = 'ID'; $args['number'] = 1; $query = new WP_User_Query( $args ); return (int) $query->get_total(); } /** * Enqueue dashboard user-management script. * * @param Activitysignature_User_Table $table Prepared table. */ private static function enqueue_assets( $table ) { if ( ! self::can_manage_users() ) { return; } $page_count = is_array( $table->items ) ? count( $table->items ) : 0; $total_items = (int) $table->get_pagination_arg( 'total_items' ); if ( $total_items < 1 ) { $total_items = $page_count; } wp_enqueue_script( 'activitysignature-admin-users', ACTIVITYSIGNATURE_PLUGIN_URL . 'assets/js/admin-users.js', array(), ACTIVITYSIGNATURE_PLUGIN_VERSION, true ); if ( is_multisite() ) { $confirm_delete = __( 'Remove the selected account(s) from this site? Their content will be assigned to you. This cannot be undone.', 'activitysignature' ); /* translators: %d: number of users */ $confirm_delete_all = __( 'Remove all %d accounts matching this filter from this site? Their content will be assigned to you. Large selections are removed in batches. This cannot be undone.', 'activitysignature' ); $confirm_delete_one = __( 'Remove this account from this site? Their content will be assigned to you. This cannot be undone.', 'activitysignature' ); } else { $confirm_delete = __( 'Delete the selected account(s)? Their content will be assigned to you. This cannot be undone.', 'activitysignature' ); /* translators: %d: number of users */ $confirm_delete_all = __( 'Delete all %d accounts matching this filter? Their content will be assigned to you. Large selections are deleted in batches. This cannot be undone.', 'activitysignature' ); $confirm_delete_one = __( 'Delete this account? Their content will be assigned to you. This cannot be undone.', 'activitysignature' ); } wp_localize_script( 'activitysignature-admin-users', 'activitysignatureUsers', array( 'pageCount' => $page_count, 'totalCount' => $total_items, 'confirmDelete' => $confirm_delete, 'confirmDeleteAll' => $confirm_delete_all, 'confirmDeleteOne' => $confirm_delete_one, ) ); } /** * Show success/error notices after delete actions. */ private static function render_notices() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only result of a redirect. $reported = isset( $_GET['activitysignature_deleted'] ) || isset( $_GET['activitysignature_skipped'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $deleted = isset( $_GET['activitysignature_deleted'] ) ? absint( wp_unslash( $_GET['activitysignature_deleted'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $skipped = isset( $_GET['activitysignature_skipped'] ) ? absint( wp_unslash( $_GET['activitysignature_skipped'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $more = isset( $_GET['activitysignature_more'] ) && '1' === sanitize_text_field( wp_unslash( $_GET['activitysignature_more'] ) ); if ( $deleted > 0 ) { if ( is_multisite() ) { $message = sprintf( /* translators: %d: number of removed users */ _n( '%d user was removed from this site. Their content was assigned to you.', '%d users were removed from this site. Their content was assigned to you.', $deleted, 'activitysignature' ), $deleted ); } else { $message = sprintf( /* translators: %d: number of deleted users */ _n( '%d user deleted. Their content was assigned to you.', '%d users deleted. Their content was assigned to you.', $deleted, 'activitysignature' ), $deleted ); } echo '

' . esc_html( $message ) . '

'; } elseif ( $reported && 0 === $skipped && ! $more ) { echo '

' . esc_html__( 'No accounts were deleted.', 'activitysignature' ) . '

'; } if ( $skipped > 0 ) { echo '

'; echo esc_html( sprintf( /* translators: %d: number of skipped users */ _n( '%d user was skipped (your own account or insufficient permissions).', '%d users were skipped (your own account or insufficient permissions).', $skipped, 'activitysignature' ), $skipped ) ); echo '

'; } if ( $more && self::can_manage_users() ) { $continue_url = remove_query_arg( array( 'activitysignature_deleted', 'activitysignature_skipped', 'activitysignature_more', 'paged' ) ); $confirm = is_multisite() ? __( 'Remove the next batch of matching accounts from this site? Their content will be assigned to you.', 'activitysignature' ) : __( 'Delete the next batch of matching accounts? Their content will be assigned to you.', 'activitysignature' ); ?>

$filters Filters. */ private static function preserve_filter_fields( $filters ) { $map = array( 's' => 'search', 'user_role' => 'role', 'login_status' => 'login_status', 'order_status' => 'order_status', 'registered_from' => 'registered_from', 'registered_to' => 'registered_to', 'updated_from' => 'updated_from', 'updated_to' => 'updated_to', 'login_from' => 'login_from', 'login_to' => 'login_to', ); foreach ( $map as $name => $key ) { if ( empty( $filters[ $key ] ) ) { continue; } printf( '' . "\n", esc_attr( $name ), esc_attr( $filters[ $key ] ) ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['orderby'] ) ) { printf( '' . "\n", esc_attr( sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['order'] ) ) { printf( '' . "\n", esc_attr( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['paged'] ) ) { printf( '' . "\n", esc_attr( absint( $_GET['paged'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } } /** * Checkbox column. * * @param array $item Row. * * @return string */ public function column_cb( $item ) { $user_id = (int) $item['ID']; if ( $user_id === get_current_user_id() ) { return '—'; } return sprintf( '', $user_id ); } /** * Actions column (right-side delete). * * @param array $item Row. * * @return string */ public function column_actions( $item ) { $user_id = (int) $item['ID']; if ( $user_id === get_current_user_id() ) { return '' . esc_html__( '—', 'activitysignature' ) . ''; } $url = wp_nonce_url( add_query_arg( array( 'page' => Activitysignature_Helpers::get_admin_page_slug(), 'tab' => 'dashboard', 'action' => 'delete', 'user' => $user_id, ), admin_url( 'options-general.php' ) ), 'activitysignature_delete_user_' . $user_id ); // Preserve filters on single delete. $filters = self::get_filters(); $map = array( 's' => 'search', 'user_role' => 'role', 'login_status' => 'login_status', 'order_status' => 'order_status', 'registered_from' => 'registered_from', 'registered_to' => 'registered_to', 'updated_from' => 'updated_from', 'updated_to' => 'updated_to', 'login_from' => 'login_from', 'login_to' => 'login_to', ); $query = array(); foreach ( $map as $name => $key ) { if ( ! empty( $filters[ $key ] ) ) { $query[ $name ] = $filters[ $key ]; } } if ( $query ) { $url = add_query_arg( $query, $url ); } return sprintf( '%s', esc_url( $url ), esc_html__( 'Delete', 'activitysignature' ) ); } /** * Column output. * * @param array $item Row. * @param string $column_name Column. * * @return string */ public function column_default( $item, $column_name ) { switch ( $column_name ) { case 'user_login': $edit_url = get_edit_user_link( $item['ID'] ); return '' . esc_html( $item['user_login'] ) . ''; case 'user_email': return esc_html( $item['user_email'] ); case 'user_roles': $role_names = array(); $roles = wp_roles()->get_names(); foreach ( (array) $item['user_roles'] as $role ) { if ( isset( $roles[ $role ] ) ) { $role_names[] = translate_user_role( $roles[ $role ] ); } } return $role_names ? esc_html( implode( ', ', $role_names ) ) : '—'; case 'created_date': $created = isset( $item['created_date'] ) ? $item['created_date'] : ''; return $created ? esc_html( Activitysignature_Helpers::format_datetime( $created ) ) : '—'; case 'last_updated': return esc_html( Activitysignature_Helpers::format_datetime( isset( $item['last_updated'] ) ? $item['last_updated'] : '' ) ); case 'last_login': return esc_html( Activitysignature_Helpers::format_datetime( isset( $item['last_login'] ) ? $item['last_login'] : '' ) ); case 'wc_orders': $count = isset( $item['wc_orders'] ) ? (int) $item['wc_orders'] : 0; $label = esc_html( (string) $count ); if ( $count > 0 && current_user_can( 'edit_shop_orders' ) ) { $url = Activitysignature_WooCommerce::get_customer_orders_url( (int) $item['ID'] ); if ( $url ) { return '' . $label . ''; } } return $label; default: return '—'; } } /** * Empty state. */ public function no_items() { esc_html_e( 'No users found.', 'activitysignature' ); } /** * Bulk actions. * * @return array */ protected function get_bulk_actions() { if ( ! self::can_manage_users() ) { return array(); } return array( 'delete' => __( 'Delete', 'activitysignature' ), ); } }