> */ private $all_items; /** * Constructor. * * @param array> $items Inventory items. */ public function __construct( array $items ) { parent::__construct( array( 'singular' => 'ability-guard-ability', 'plural' => 'ability-guard-abilities', 'ajax' => false, ) ); $this->all_items = $items; } /** * Prepare table data. */ public function prepare_items(): void { $per_page = 20; $page = $this->get_pagenum(); $items = $this->filter_items( $this->all_items ); $total = count( $items ); $offset = ( $page - 1 ) * $per_page; $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() ); $this->items = array_slice( $items, $offset, $per_page ); $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( 'name' => esc_html__( 'Ability', 'ability-guard' ), 'risk' => esc_html__( 'Risk', 'ability-guard' ), 'annotations' => esc_html__( 'Annotations', 'ability-guard' ), 'category' => esc_html__( 'Category', 'ability-guard' ), 'permission' => esc_html__( 'Current User', 'ability-guard' ), 'rest' => esc_html__( 'REST', 'ability-guard' ), 'schemas' => esc_html__( 'Schemas', 'ability-guard' ), ); } /** * Get sortable columns. * * @return array> */ protected function get_sortable_columns(): array { return array( 'name' => array( 'name', false ), 'risk' => array( 'risk', false ), 'category' => array( 'category', false ), ); } /** * Render filters. * * @param string $which Table nav location. */ protected function extra_tablenav( $which ): void { if ( 'top' !== $which ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $current_risk = isset( $_GET['risk'] ) ? sanitize_key( wp_unslash( $_GET['risk'] ) ) : ''; ?>
$item Current item. * @return string */ protected function column_name( $item ): string { $ability_id = sanitize_html_class( str_replace( '/', '-', (string) $item['name'] ) ); $view_url = add_query_arg( array( 'page' => 'ability-guard', 'ability_name' => rawurlencode( (string) $item['name'] ), ), admin_url( 'admin.php' ) ); $modal_url = '#TB_inline?TB_inline=true&width=900&height=650&inlineId=ability-guard-ability-modal-' . $ability_id; $output = '' . esc_html( (string) $item['name'] ) . ''; $output .= '
' . esc_html( (string) $item['label'] ) . '
'; return $output; } /** * Render risk column. * * @param array $item Current item. * @return string */ protected function column_risk( $item ): string { $risk = sanitize_key( (string) $item['risk'] ); return sprintf( '%2$s', esc_attr( $risk ), esc_html( ucfirst( $risk ) ) ); } /** * Render annotations column. * * @param array $item Current item. * @return string */ protected function column_annotations( $item ): string { $badges = array( $this->annotation_badge( __( 'Readonly', 'ability-guard' ), $item['readonly'] ), $this->annotation_badge( __( 'Destructive', 'ability-guard' ), $item['destructive'] ), $this->annotation_badge( __( 'Idempotent', 'ability-guard' ), $item['idempotent'] ), ); return implode( ' ', $badges ); } /** * Render category column. * * @param array $item Current item. * @return string */ protected function column_category( $item ): string { $label = ! empty( $item['category_label'] ) ? (string) $item['category_label'] : (string) $item['category']; return esc_html( $label ) . '
' . esc_html( (string) $item['category'] ) . '
'; } /** * Render permission column. * * @param array $item Current item. * @return string */ protected function column_permission( $item ): string { $status = isset( $item['permission_status'] ) ? sanitize_key( (string) $item['permission_status'] ) : 'unknown'; if ( 'allowed' === $status ) { $class = 'yes'; $text = __( 'Allowed', 'ability-guard' ); } elseif ( 'denied' === $status ) { $class = 'no'; $text = __( 'Denied', 'ability-guard' ); } else { $class = 'risk-unknown'; $text = __( 'Unknown', 'ability-guard' ); } return sprintf( '%2$s', esc_attr( $class ), esc_html( $text ) ); } /** * Render REST column. * * @param array $item Current item. * @return string */ protected function column_rest( $item ): string { return $this->yes_no_badge( ! empty( $item['show_in_rest'] ) ); } /** * Render schema column. * * @param array $item Current item. * @return string */ protected function column_schemas( $item ): string { $parts = array(); if ( ! empty( $item['has_input_schema'] ) ) { $parts[] = '' . esc_html__( 'Input', 'ability-guard' ) . ''; } if ( ! empty( $item['has_output_schema'] ) ) { $parts[] = '' . esc_html__( 'Output', 'ability-guard' ) . ''; } if ( empty( $parts ) ) { return '' . esc_html__( 'None', 'ability-guard' ) . ''; } return implode( ' ', $parts ); } /** * Default column output. * * @param array $item Current item. * @param string $column_name Column name. * @return string */ protected function column_default( $item, $column_name ): string { return isset( $item[ $column_name ] ) ? esc_html( (string) $item[ $column_name ] ) : ''; } /** * Filter and sort inventory. * * @param array> $items Items. * @return array> */ private function filter_items( array $items ): array { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table search. $search = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table filter. $risk = isset( $_GET['risk'] ) ? sanitize_key( wp_unslash( $_GET['risk'] ) ) : ''; if ( '' !== $search ) { $items = array_filter( $items, static function ( array $item ) use ( $search ): bool { $haystack = strtolower( implode( ' ', array( $item['name'], $item['label'], $item['description'], $item['category'], $item['namespace'], $item['source'], ) ) ); return false !== strpos( $haystack, strtolower( $search ) ); } ); } if ( '' !== $risk ) { $items = array_filter( $items, static function ( array $item ) use ( $risk ): bool { return isset( $item['risk'] ) && $item['risk'] === $risk; } ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table sorting. $orderby = isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'name'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only table sorting. $order = isset( $_GET['order'] ) ? strtolower( sanitize_key( wp_unslash( $_GET['order'] ) ) ) : 'asc'; $allowed = array( 'name', 'risk', 'category' ); if ( ! in_array( $orderby, $allowed, true ) ) { $orderby = 'name'; } usort( $items, static function ( array $a, array $b ) use ( $orderby, $order ): int { $result = strcmp( (string) $a[ $orderby ], (string) $b[ $orderby ] ); return 'desc' === $order ? -$result : $result; } ); return array_values( $items ); } /** * Render annotation badge. * * @param string $label Annotation label. * @param bool|null $value Annotation value. * @return string */ private function annotation_badge( string $label, $value ): string { if ( true === $value ) { $class = 'true'; $text = __( 'Yes', 'ability-guard' ); } elseif ( false === $value ) { $class = 'false'; $text = __( 'No', 'ability-guard' ); } else { $class = 'unknown'; $text = __( 'Unknown', 'ability-guard' ); } return sprintf( '%2$s: %3$s', esc_attr( $class ), esc_html( $label ), esc_html( $text ) ); } /** * Render yes/no badge. * * @param bool $value Value. * @return string */ private function yes_no_badge( bool $value ): string { return sprintf( '%2$s', $value ? 'yes' : 'no', $value ? esc_html__( 'Yes', 'ability-guard' ) : esc_html__( 'No', 'ability-guard' ) ); } }