'mra-network-attachment', 'plural' => 'mra-network-attachments', 'ajax' => false, ) ); $this->alerts_only = $alerts_only; } /** * Returns the column definitions. * * @return array */ public function get_columns(): array { return array( 'cb' => '', 'site' => __( 'Site', 'robotstxt-mediaaudit' ), 'file_name' => __( 'Filename', 'robotstxt-mediaaudit' ), 'external_status' => __( 'External Status', 'robotstxt-mediaaudit' ), 'has_alert' => __( 'Alert', 'robotstxt-mediaaudit' ), 'external_scanned_at' => __( 'Last Scanned', 'robotstxt-mediaaudit' ), ); } /** * Returns sortable column definitions. * * @return array> */ protected function get_sortable_columns(): array { return array( 'site' => array( 'site_url', false ), 'file_name' => array( 'file_name', false ), 'external_status' => array( 'external_status', false ), 'external_scanned_at' => array( 'external_scanned_at', false ), ); } /** * Returns bulk action definitions. * * @return array */ protected function get_bulk_actions(): array { return array( 'run_external_scan' => __( 'Run External Scan', 'robotstxt-mediaaudit' ), 'purge_external_data' => __( 'Purge External Data', 'robotstxt-mediaaudit' ), ); } /** * Renders the checkbox column. * * @param array $item Row data. * * @return string */ protected function column_cb( $item ): string { $site_id = is_numeric( $item['site_id'] ?? null ) ? (int) $item['site_id'] : 0; $aid = is_numeric( $item['attachment_id'] ?? null ) ? (int) $item['attachment_id'] : 0; return sprintf( '', $site_id, $aid ); } /** * Renders the Site column. * * @param array $item Row data. * * @return string */ protected function column_site( array $item ): string { $site_id = is_numeric( $item['site_id'] ?? null ) ? (int) $item['site_id'] : 0; $site_url = is_string( $item['site_url'] ?? null ) ? $item['site_url'] : ''; $site_details = get_site( $site_id ); $site_name = $site_details instanceof \WP_Site ? esc_html( $site_details->blogname ) : esc_html( $site_url ); if ( $site_url ) { $admin_url = get_admin_url( $site_id, 'admin.php?page=robotstxt-mediaaudit' ); return sprintf( '%s
%s', esc_url( $admin_url ), $site_name, esc_html( $site_url ) ); } return $site_name; } /** * Renders the Filename column with a link to the per-site detail page. * * @param array $item Row data. * * @return string */ protected function column_file_name( array $item ): string { $site_id = is_numeric( $item['site_id'] ?? null ) ? (int) $item['site_id'] : 0; $aid = is_numeric( $item['attachment_id'] ?? null ) ? (int) $item['attachment_id'] : 0; $filename = is_string( $item['file_name'] ?? null ) ? $item['file_name'] : "#{$aid}"; $detail_url = get_admin_url( $site_id, 'admin.php?page=robotstxt-mediaaudit-detail&attachment_id=' . $aid ); return sprintf( '%s', esc_url( $detail_url ), esc_html( $filename ) ); } /** * Renders the External Status column. * * @param array $item Row data. * * @return string */ protected function column_external_status( array $item ): string { $status = is_string( $item['external_status'] ?? null ) ? $item['external_status'] : 'pending'; return '' . esc_html( ucfirst( $status ) ) . ''; } /** * Renders the Alert column. * * @param array $item Row data. * * @return string */ protected function column_has_alert( array $item ): string { $has_alert = ! empty( $item['has_alert'] ); $is_dismissed = ! empty( $item['is_dismissed'] ); if ( $has_alert && $is_dismissed ) { return '' . esc_html__( 'Dismissed', 'robotstxt-mediaaudit' ) . ''; } if ( $has_alert ) { return '' . esc_html__( 'Alert', 'robotstxt-mediaaudit' ) . ''; } return '—'; } /** * Renders the Last Scanned column. * * @param array $item Row data. * * @return string */ protected function column_external_scanned_at( array $item ): string { $val = $item['external_scanned_at'] ?? null; return is_string( $val ) && '' !== $val ? esc_html( $val ) : '—'; } /** * Default column renderer. * * @param array $item Row data. * @param string $column_name Column key. * * @return string */ protected function column_default( $item, $column_name ): string { $val = $item[ $column_name ] ?? ''; return is_scalar( $val ) ? esc_html( (string) $val ) : ''; } /** * Queries the network mirror table and populates $this->items. * * @return void */ public function prepare_items(): void { global $wpdb; $table = NetworkDatabase::table_name(); $per_pg = self::PER_PAGE; $current = $this->get_pagenum(); $offset = ( $current - 1 ) * $per_pg; $where = array( '1=1' ); $args = array(); if ( $this->alerts_only ) { $where[] = 'has_alert = 1 AND is_dismissed = 0'; } // Site filter. // phpcs:ignore WordPress.Security.NonceVerification.Recommended $site_filter = isset( $_GET['mra_site'] ) && is_numeric( $_GET['mra_site'] ) ? (int) $_GET['mra_site'] : 0; if ( $site_filter > 0 ) { $where[] = 'site_id = %d'; $args[] = $site_filter; } // Status filter. $valid_statuses = array( 'pending', 'queued', 'scanned', 'matches', 'error' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $status_filter = isset( $_GET['mra_status'] ) && is_string( $_GET['mra_status'] ) ? sanitize_key( $_GET['mra_status'] ) : ''; if ( $status_filter && in_array( $status_filter, $valid_statuses, true ) ) { $where[] = 'external_status = %s'; $args[] = $status_filter; } $where_sql = implode( ' AND ', $where ); // Sorting. $valid_orderby = array( 'site_url', 'file_name', 'external_status', 'external_scanned_at' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $orderby_raw = isset( $_GET['orderby'] ) && is_string( $_GET['orderby'] ) ? sanitize_key( $_GET['orderby'] ) : 'site_url'; $orderby = in_array( $orderby_raw, $valid_orderby, true ) ? $orderby_raw : 'site_url'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order_raw = isset( $_GET['order'] ) && is_string( $_GET['order'] ) ? strtoupper( sanitize_key( $_GET['order'] ) ) : 'ASC'; $order = in_array( $order_raw, array( 'ASC', 'DESC' ), true ) ? $order_raw : 'ASC'; // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared $count_sql = "SELECT COUNT(*) FROM `{$table}` WHERE {$where_sql}"; $total = empty( $args ) ? (int) $wpdb->get_var( $count_sql ) : (int) $wpdb->get_var( $wpdb->prepare( $count_sql, ...$args ) ); $data_sql = "SELECT * FROM `{$table}` WHERE {$where_sql} ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d"; $query_args = array_merge( $args, array( $per_pg, $offset ) ); $this->items = (array) $wpdb->get_results( $wpdb->prepare( $data_sql, ...$query_args ), ARRAY_A ); // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared $this->set_pagination_args( array( 'total_items' => $total, 'per_page' => $per_pg, ) ); $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), ); } /** * Renders the "no items" message. * * @return void */ public function no_items(): void { esc_html_e( 'No media attachments found across the network.', 'robotstxt-mediaaudit' ); } }