v1.6.0
This commit is contained in:
parent
83d629568a
commit
98b1cf0f3b
17 changed files with 2968 additions and 1313 deletions
|
|
@ -8,6 +8,7 @@
|
|||
namespace MediaRightsAudit\Admin;
|
||||
|
||||
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
||||
use MediaRightsAudit\External\HostnameFilter;
|
||||
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
|
|
@ -83,7 +84,7 @@ class MediaListTable extends \WP_List_Table {
|
|||
'run_external_scan' => __( 'Run External Scan', 'robotstxt-mediaaudit' ),
|
||||
'purge_external_data' => __( 'Purge External Data', 'robotstxt-mediaaudit' ),
|
||||
'export_csv' => __( 'Export CSV', 'robotstxt-mediaaudit' ),
|
||||
'export_external_csv' => __( 'Export External Results CSV', 'robotstxt-mediaaudit' ),
|
||||
'export_csv_alerts' => __( 'Export CSV (Alerts only)', 'robotstxt-mediaaudit' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -279,6 +280,49 @@ class MediaListTable extends \WP_List_Table {
|
|||
$es_val = $item['external_status'];
|
||||
$status = is_string( $es_val ) ? $es_val : '';
|
||||
|
||||
// Check for alert domains first.
|
||||
$top_domains_raw = isset( $item['top_domains_data'] ) && is_array( $item['top_domains_data'] )
|
||||
? $item['top_domains_data']
|
||||
: array();
|
||||
$top_domains_data = array();
|
||||
foreach ( $top_domains_raw as $td_key => $td_val ) {
|
||||
if ( is_string( $td_key ) && is_int( $td_val ) ) {
|
||||
$top_domains_data[ $td_key ] = $td_val;
|
||||
}
|
||||
}
|
||||
$has_alert = ! empty( $top_domains_data ) && HostnameFilter::has_alert_domains( $top_domains_data );
|
||||
|
||||
if ( $has_alert ) {
|
||||
$out = sprintf(
|
||||
'<span class="mra-badge mra-badge-alert">%s</span>',
|
||||
esc_html__( 'Alert', 'robotstxt-mediaaudit' )
|
||||
);
|
||||
|
||||
// Sum counts for non-ignored domains.
|
||||
$filtered_count = 0;
|
||||
foreach ( $top_domains_data as $domain => $count ) {
|
||||
if ( ! HostnameFilter::is_ignored( $domain ) ) {
|
||||
$filtered_count += $count;
|
||||
}
|
||||
}
|
||||
if ( $filtered_count > 0 ) {
|
||||
$out .= sprintf(
|
||||
' <span class="mra-badge mra-badge-match-count">%s</span>',
|
||||
esc_html( number_format_i18n( $filtered_count ) )
|
||||
);
|
||||
}
|
||||
|
||||
$aid_val = $item['attachment_id'];
|
||||
$aid = is_numeric( $aid_val ) ? (int) $aid_val : 0;
|
||||
$out .= sprintf(
|
||||
' <a href="#" class="mra-view-details" data-id="%d">%s</a>',
|
||||
$aid,
|
||||
esc_html__( 'View Results', 'robotstxt-mediaaudit' )
|
||||
);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
$labels = array(
|
||||
'pending' => __( 'Pending', 'robotstxt-mediaaudit' ),
|
||||
'queued' => __( 'Queued', 'robotstxt-mediaaudit' ),
|
||||
|
|
@ -385,6 +429,11 @@ class MediaListTable extends \WP_List_Table {
|
|||
// External status filter.
|
||||
echo '<select name="mra_external_status">';
|
||||
echo '<option value="">' . esc_html__( 'All statuses', 'robotstxt-mediaaudit' ) . '</option>';
|
||||
printf(
|
||||
'<option value="alert"%s>%s</option>',
|
||||
selected( $current_status, 'alert', false ),
|
||||
esc_html__( 'Alert', 'robotstxt-mediaaudit' )
|
||||
);
|
||||
foreach ( self::EXTERNAL_STATUSES as $s ) {
|
||||
printf(
|
||||
'<option value="%s"%s>%s</option>',
|
||||
|
|
@ -456,6 +505,13 @@ class MediaListTable extends \WP_List_Table {
|
|||
$show_unused = ! empty( $_REQUEST['mra_unused'] );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
// Detect alert filter before normalising external_status.
|
||||
$filter_alert = ( 'alert' === $external_status );
|
||||
if ( $filter_alert ) {
|
||||
// Reuse existing matches WHERE logic to narrow the candidate set.
|
||||
$external_status = 'matches';
|
||||
}
|
||||
|
||||
if ( ! in_array( $external_status, self::EXTERNAL_STATUSES, true ) ) {
|
||||
$external_status = '';
|
||||
}
|
||||
|
|
@ -485,8 +541,6 @@ class MediaListTable extends \WP_List_Table {
|
|||
|
||||
$where = implode( ' AND ', $where_parts );
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
if ( 'usage_count' === $orderby ) {
|
||||
$order_sql = "(SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_usage mu WHERE mu.attachment_id = i.attachment_id) {$order}";
|
||||
} elseif ( 'external_scanned_at' === $orderby ) {
|
||||
|
|
@ -496,30 +550,110 @@ class MediaListTable extends \WP_List_Table {
|
|||
$order_sql = "i.attachment_id {$order}";
|
||||
}
|
||||
|
||||
$count_sql = "SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_index i WHERE {$where}";
|
||||
$items_sql = "SELECT i.*,
|
||||
$select_sql = "SELECT i.*,
|
||||
(SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_usage mu WHERE mu.attachment_id = i.attachment_id) AS usage_count,
|
||||
(SELECT COALESCE(SUM(er.match_count), 0) FROM {$wpdb->prefix}mra_external_results er WHERE er.attachment_id = i.attachment_id) AS total_match_count
|
||||
FROM {$wpdb->prefix}mra_media_index i
|
||||
WHERE {$where}
|
||||
ORDER BY {$order_sql}
|
||||
LIMIT %d OFFSET %d";
|
||||
FROM {$wpdb->prefix}mra_media_index i";
|
||||
|
||||
$count_prepared = empty( $prepare_args )
|
||||
? $count_sql
|
||||
: $wpdb->prepare( $count_sql, ...$prepare_args );
|
||||
$rows = array();
|
||||
$total = 0;
|
||||
|
||||
$items_prepared = $wpdb->prepare(
|
||||
$items_sql,
|
||||
...array_merge( $prepare_args, array( $per_page, $offset ) )
|
||||
);
|
||||
if ( $filter_alert ) {
|
||||
// Fetch ALL matching rows (no LIMIT), then PHP-filter by alert domains.
|
||||
$all_sql = $select_sql . " WHERE {$where} ORDER BY {$order_sql}";
|
||||
|
||||
$total = (int) $wpdb->get_var( $count_prepared );
|
||||
$rows = $wpdb->get_results( $items_prepared, ARRAY_A );
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
||||
$all_prepared = empty( $prepare_args )
|
||||
? $all_sql
|
||||
: $wpdb->prepare( $all_sql, ...$prepare_args );
|
||||
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
||||
$all_rows = $wpdb->get_results( $all_prepared, ARRAY_A );
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
if ( ! $rows ) {
|
||||
if ( ! is_array( $all_rows ) ) {
|
||||
$all_rows = array();
|
||||
}
|
||||
|
||||
// Bulk-fetch top_domains for all candidate rows.
|
||||
$all_ids = array_map( 'intval', array_column( $all_rows, 'attachment_id' ) );
|
||||
$top_domains_by_id = self::fetch_top_domains( $all_ids );
|
||||
|
||||
// PHP-filter: keep only rows that have at least one alert domain.
|
||||
$filtered = array();
|
||||
foreach ( $all_rows as $r ) {
|
||||
if ( ! is_array( $r ) ) {
|
||||
continue;
|
||||
}
|
||||
$aid_val = $r['attachment_id'] ?? null;
|
||||
$aid = is_numeric( $aid_val ) ? (int) $aid_val : 0;
|
||||
if ( HostnameFilter::has_alert_domains( $top_domains_by_id[ $aid ] ?? array() ) ) {
|
||||
$filtered[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
$total = count( $filtered );
|
||||
$rows = array_slice( $filtered, $offset, $per_page );
|
||||
|
||||
// Attach top_domains_data for the page slice.
|
||||
foreach ( $rows as &$row ) {
|
||||
$rid_val = $row['attachment_id'] ?? null;
|
||||
$rid = is_numeric( $rid_val ) ? (int) $rid_val : 0;
|
||||
$row['top_domains_data'] = $top_domains_by_id[ $rid ] ?? array();
|
||||
}
|
||||
unset( $row );
|
||||
} else {
|
||||
$count_sql = "SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_index i WHERE {$where}";
|
||||
$items_sql = $select_sql . " WHERE {$where} ORDER BY {$order_sql} LIMIT %d OFFSET %d";
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
||||
$count_prepared = empty( $prepare_args )
|
||||
? $count_sql
|
||||
: $wpdb->prepare( $count_sql, ...$prepare_args );
|
||||
|
||||
$items_prepared = $wpdb->prepare(
|
||||
$items_sql,
|
||||
...array_merge( $prepare_args, array( $per_page, $offset ) )
|
||||
);
|
||||
|
||||
$total = (int) $wpdb->get_var( $count_prepared );
|
||||
$rows = $wpdb->get_results( $items_prepared, ARRAY_A );
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
if ( ! is_array( $rows ) ) {
|
||||
$rows = array();
|
||||
}
|
||||
|
||||
// Bulk-fetch top_domains only for rows with external_status = 'matches'.
|
||||
$matches_ids = array();
|
||||
foreach ( $rows as $r ) {
|
||||
if ( ! is_array( $r ) ) {
|
||||
continue;
|
||||
}
|
||||
$es_val = $r['external_status'] ?? null;
|
||||
if ( 'matches' === $es_val ) {
|
||||
$aid_val = $r['attachment_id'] ?? null;
|
||||
if ( is_numeric( $aid_val ) ) {
|
||||
$matches_ids[] = (int) $aid_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$top_domains_by_id = empty( $matches_ids )
|
||||
? array()
|
||||
: self::fetch_top_domains( $matches_ids );
|
||||
|
||||
foreach ( $rows as &$row ) {
|
||||
if ( ! is_array( $row ) ) {
|
||||
continue;
|
||||
}
|
||||
$rid_val = $row['attachment_id'] ?? null;
|
||||
$rid = is_numeric( $rid_val ) ? (int) $rid_val : 0;
|
||||
$row['top_domains_data'] = $top_domains_by_id[ $rid ] ?? array();
|
||||
}
|
||||
unset( $row );
|
||||
}
|
||||
|
||||
if ( empty( $rows ) ) {
|
||||
$this->items = array();
|
||||
} else {
|
||||
// Bulk-load usages for this page (avoid N+1).
|
||||
|
|
@ -533,6 +667,9 @@ class MediaListTable extends \WP_List_Table {
|
|||
}
|
||||
|
||||
foreach ( $rows as &$row ) {
|
||||
if ( ! is_array( $row ) ) {
|
||||
continue;
|
||||
}
|
||||
$rid_val = $row['attachment_id'] ?? null;
|
||||
$rid = is_numeric( $rid_val ) ? (int) $rid_val : 0;
|
||||
$row['usages_data'] = $usage_by[ $rid ] ?? array();
|
||||
|
|
@ -562,6 +699,74 @@ class MediaListTable extends \WP_List_Table {
|
|||
// Public query helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fetches and merges top_domains for a set of attachment IDs.
|
||||
* Multiple providers for the same attachment have their domain counts summed.
|
||||
*
|
||||
* @param array<int, int> $attachment_ids Attachment IDs to look up.
|
||||
*
|
||||
* @return array<int, array<string, int>> attachment_id → domain → count
|
||||
*/
|
||||
public static function fetch_top_domains( array $attachment_ids ): array {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $attachment_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$placeholders = implode( ',', array_fill( 0, count( $attachment_ids ), '%d' ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||
$rows = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT attachment_id, top_domains FROM {$wpdb->prefix}mra_external_results WHERE attachment_id IN ({$placeholders})",
|
||||
...$attachment_ids
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||
|
||||
if ( ! is_array( $rows ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ( $rows as $row ) {
|
||||
if ( ! is_array( $row ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aid_val = $row['attachment_id'] ?? null;
|
||||
$aid = is_numeric( $aid_val ) ? (int) $aid_val : 0;
|
||||
if ( $aid <= 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$td_val = $row['top_domains'] ?? null;
|
||||
$td_raw = is_string( $td_val ) ? json_decode( $td_val, true ) : null;
|
||||
$domains = is_array( $td_raw ) ? $td_raw : array();
|
||||
|
||||
if ( ! isset( $result[ $aid ] ) ) {
|
||||
$result[ $aid ] = array();
|
||||
}
|
||||
|
||||
foreach ( $domains as $domain => $count ) {
|
||||
if ( ! is_string( $domain ) ) {
|
||||
continue;
|
||||
}
|
||||
$c = is_numeric( $count ) ? (int) $count : 0;
|
||||
if ( isset( $result[ $aid ][ $domain ] ) ) {
|
||||
$result[ $aid ][ $domain ] += $c;
|
||||
} else {
|
||||
$result[ $aid ][ $domain ] = $c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches usage rows for a set of attachment IDs in one query.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue