'ability-guard-log', 'plural' => 'ability-guard-logs', 'ajax' => false, ) ); $this->repository = $repository; } /** * Prepare table data. */ public function prepare_items(): void { $this->process_bulk_action(); $per_page = 20; $page = $this->get_pagenum(); $args = $this->get_query_args(); $args = $this->add_inventory_filters( $args ); $total = $this->repository->count_logs( $args ); $args['per_page'] = $per_page; $args['page'] = $page; $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() ); $this->items = $this->repository->get_logs( $args ); $this->set_pagination_args( array( 'total_items' => $total, 'per_page' => $per_page, 'total_pages' => (int) ceil( $total / $per_page ), ) ); } /** * Get columns. * * @return array */ public function get_columns(): array { return array( 'cb' => '', 'ability_name' => esc_html__( 'Ability', 'ability-guard' ), 'user_id' => esc_html__( 'User', 'ability-guard' ), 'triggered_via' => esc_html__( 'Via', 'ability-guard' ), 'status' => esc_html__( 'Status', 'ability-guard' ), 'execution_ms' => esc_html__( 'Duration', 'ability-guard' ), 'created_at' => esc_html__( 'Time', 'ability-guard' ), ); } /** * Get sortable columns. * * @return array> */ protected function get_sortable_columns(): array { return array( 'ability_name' => array( 'ability_name', false ), 'user_id' => array( 'user_id', false ), 'status' => array( 'status', false ), 'execution_ms' => array( 'execution_ms', false ), 'created_at' => array( 'created_at', true ), ); } /** * Get bulk actions. * * @return array */ protected function get_bulk_actions(): array { return array( 'bulk-delete' => esc_html__( 'Delete', 'ability-guard' ), ); } /** * Render checkbox column. * * @param object $item Current item. * @return string */ protected function column_cb( $item ): string { return sprintf( '', absint( $item->id ) ); } /** * Render ability column with row actions. * * @param object $item Current item. * @return string */ protected function column_ability_name( $item ): string { $log_id = absint( $item->id ); $view_url = add_query_arg( array( 'page' => 'ability-guard-logs', 'log_id' => $log_id, ), admin_url( 'admin.php' ) ); $modal_url = '#TB_inline?TB_inline=true&width=900&height=650&inlineId=ability-guard-log-modal-' . $log_id; $delete_url = wp_nonce_url( add_query_arg( array( 'page' => 'ability-guard-logs', 'action' => 'delete', 'log_id' => $log_id, ), admin_url( 'admin.php' ) ), 'ability_guard_delete_log_' . $log_id ); $actions = array( 'view' => sprintf( '%4$s', esc_url( $view_url ), esc_attr__( 'Log Details', 'ability-guard' ), esc_url( $modal_url ), esc_html__( 'View details', 'ability-guard' ) ), 'delete' => sprintf( '%s', esc_url( $delete_url ), esc_html__( 'Delete', 'ability-guard' ) ), ); return sprintf( '%s%s', esc_html( $item->ability_name ), $this->row_actions( $actions ) ); } /** * Render user column. * * @param object $item Current item. * @return string */ protected function column_user_id( $item ): string { $user_id = absint( $item->user_id ); if ( 0 === $user_id ) { return esc_html__( 'System', 'ability-guard' ); } $user = get_userdata( $user_id ); if ( ! $user ) { return sprintf( /* translators: %d: user ID. */ esc_html__( 'User #%d', 'ability-guard' ), $user_id ); } return esc_html( $user->display_name ); } /** * Render trigger column. * * @param object $item Current item. * @return string */ protected function column_triggered_via( $item ): string { $context = sanitize_key( $item->triggered_via ); return sprintf( '%2$s', esc_attr( sanitize_html_class( $context ) ), esc_html( strtoupper( $context ) ) ); } /** * Render status column. * * @param object $item Current item. * @return string */ protected function column_status( $item ): string { $status = sanitize_key( $item->status ); return sprintf( '%2$s', esc_attr( sanitize_html_class( $status ) ), esc_html( ucfirst( $status ) ) ); } /** * Render duration column. * * @param object $item Current item. * @return string */ protected function column_execution_ms( $item ): string { return sprintf( /* translators: %d: duration in milliseconds. */ esc_html__( '%d ms', 'ability-guard' ), absint( $item->execution_ms ) ); } /** * Render created date column. * * @param object $item Current item. * @return string */ protected function column_created_at( $item ): string { $timestamp = strtotime( (string) $item->created_at ); if ( ! $timestamp ) { return esc_html( $item->created_at ); } return esc_html( wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp ) ); } /** * Render default column. * * @param object $item Current item. * @param string $column_name Column name. * @return string */ protected function column_default( $item, $column_name ): string { if ( isset( $item->{$column_name} ) ) { return esc_html( (string) $item->{$column_name} ); } return ''; } /** * Render extra filters. * * @param string $which Table position. */ protected function extra_tablenav( $which ): void { if ( 'top' !== $which ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $status = isset( $_REQUEST['status'] ) ? sanitize_key( wp_unslash( $_REQUEST['status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $triggered_via = isset( $_REQUEST['triggered_via'] ) ? sanitize_key( wp_unslash( $_REQUEST['triggered_via'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $user_id = isset( $_REQUEST['user_id'] ) && '' !== trim( (string) wp_unslash( $_REQUEST['user_id'] ) ) ? absint( wp_unslash( $_REQUEST['user_id'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $date_from = isset( $_REQUEST['date_from'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['date_from'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $date_to = isset( $_REQUEST['date_to'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['date_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $risk = isset( $_REQUEST['risk'] ) ? sanitize_key( wp_unslash( $_REQUEST['risk'] ) ) : ''; ?>
*/ private function get_query_args(): array { return array( // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table sorting. 'orderby' => isset( $_REQUEST['orderby'] ) ? sanitize_key( wp_unslash( $_REQUEST['orderby'] ) ) : 'created_at', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table sorting. 'order' => isset( $_REQUEST['order'] ) ? sanitize_key( wp_unslash( $_REQUEST['order'] ) ) : 'DESC', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. 'status' => isset( $_REQUEST['status'] ) ? sanitize_key( wp_unslash( $_REQUEST['status'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. 'triggered_via' => isset( $_REQUEST['triggered_via'] ) ? sanitize_key( wp_unslash( $_REQUEST['triggered_via'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. 'user_id' => isset( $_REQUEST['user_id'] ) && '' !== trim( (string) wp_unslash( $_REQUEST['user_id'] ) ) ? absint( wp_unslash( $_REQUEST['user_id'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. 'date_from' => isset( $_REQUEST['date_from'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['date_from'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. 'date_to' => isset( $_REQUEST['date_to'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['date_to'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. 'risk' => isset( $_REQUEST['risk'] ) ? sanitize_key( wp_unslash( $_REQUEST['risk'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table search. 'search' => isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : '', ); } /** * Convert inventory filters into ability name filters. * * @param array $args Query args. * @return array */ private function add_inventory_filters( array $args ): array { if ( empty( $args['risk'] ) ) { return $args; } $scanner = new Ability_Guard_Ability_Scanner(); $ability_names = array(); foreach ( $scanner->get_abilities() as $ability ) { if ( ! empty( $args['risk'] ) && $ability['risk'] !== $args['risk'] ) { continue; } $ability_names[] = $ability['name']; } $args['ability_names'] = $ability_names; return $args; } /** * Redirect after mutating action to avoid resubmission. */ private function redirect_after_action(): void { wp_safe_redirect( admin_url( 'admin.php?page=ability-guard-logs' ) ); exit; } }