v1.2.0
This commit is contained in:
parent
6708bbb67f
commit
650e69349f
21 changed files with 2919 additions and 268 deletions
|
|
@ -65,6 +65,8 @@ class AuditPage {
|
|||
|
||||
if ( 'export_csv' === $action ) {
|
||||
$this->handle_export_csv();
|
||||
} elseif ( 'export_external_csv' === $action ) {
|
||||
$this->handle_export_external_csv();
|
||||
} elseif ( 'run_external_scan' === $action ) {
|
||||
$this->handle_run_external_scan();
|
||||
} elseif ( 'purge_external_data' === $action ) {
|
||||
|
|
@ -498,6 +500,142 @@ class AuditPage {
|
|||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an external-results CSV for selected attachment IDs and terminates.
|
||||
*
|
||||
* One row per attachment × provider pair. Attachments with no results still
|
||||
* appear with empty provider columns.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handle_export_external_csv(): void {
|
||||
check_admin_referer( 'bulk-mra-attachments' );
|
||||
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_die( esc_html__( 'Insufficient permissions.', 'robotstxt-mediaaudit' ) );
|
||||
}
|
||||
|
||||
$ids = $this->collect_attachment_ids();
|
||||
$rows = $this->build_external_csv_rows( $ids );
|
||||
|
||||
$filename = 'media-audit-external-' . gmdate( 'Y-m-d' ) . '.csv';
|
||||
|
||||
header( 'Content-Type: text/csv; charset=utf-8' );
|
||||
header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
|
||||
header( 'Pragma: no-cache' );
|
||||
|
||||
$out = fopen( 'php://output', 'w' );
|
||||
if ( false === $out ) {
|
||||
wp_die( esc_html__( 'Could not open output stream.', 'robotstxt-mediaaudit' ) );
|
||||
}
|
||||
|
||||
fwrite( $out, "\xEF\xBB\xBF" ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
|
||||
|
||||
fputcsv(
|
||||
$out,
|
||||
array(
|
||||
__( 'Attachment ID', 'robotstxt-mediaaudit' ),
|
||||
__( 'Filename', 'robotstxt-mediaaudit' ),
|
||||
__( 'File URL', 'robotstxt-mediaaudit' ),
|
||||
__( 'External Status', 'robotstxt-mediaaudit' ),
|
||||
__( 'Last Scanned', 'robotstxt-mediaaudit' ),
|
||||
__( 'Provider', 'robotstxt-mediaaudit' ),
|
||||
__( 'Match Count', 'robotstxt-mediaaudit' ),
|
||||
__( 'Top Domains', 'robotstxt-mediaaudit' ),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $rows as $row ) {
|
||||
fputcsv( $out, $row );
|
||||
}
|
||||
|
||||
fclose( $out ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds external-results CSV rows for the given attachment IDs.
|
||||
*
|
||||
* Each row represents one attachment × provider combination. Attachments with
|
||||
* no provider results are included with empty provider columns. If $ids is
|
||||
* empty, all indexed attachments are exported.
|
||||
*
|
||||
* @param array<int, int> $ids Attachment IDs to export (empty = all).
|
||||
*
|
||||
* @return array<int, array<int, string>>
|
||||
*/
|
||||
private function build_external_csv_rows( array $ids ): array {
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||
|
||||
if ( empty( $ids ) ) {
|
||||
$rows = $wpdb->get_results(
|
||||
"SELECT i.attachment_id, i.file_name, i.file_url, i.external_status,
|
||||
i.external_scanned_at, er.provider, er.match_count, er.top_domains
|
||||
FROM {$wpdb->prefix}mra_media_index i
|
||||
LEFT JOIN {$wpdb->prefix}mra_external_results er ON er.attachment_id = i.attachment_id
|
||||
ORDER BY i.attachment_id ASC, er.provider ASC",
|
||||
ARRAY_A
|
||||
);
|
||||
} else {
|
||||
$placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
|
||||
$rows = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT i.attachment_id, i.file_name, i.file_url, i.external_status,
|
||||
i.external_scanned_at, er.provider, er.match_count, er.top_domains
|
||||
FROM {$wpdb->prefix}mra_media_index i
|
||||
LEFT JOIN {$wpdb->prefix}mra_external_results er ON er.attachment_id = i.attachment_id
|
||||
WHERE i.attachment_id IN ({$placeholders})
|
||||
ORDER BY i.attachment_id ASC, er.provider ASC",
|
||||
...$ids
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
}
|
||||
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||
|
||||
if ( ! $rows ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$output = array();
|
||||
|
||||
foreach ( $rows as $r ) {
|
||||
$aid_raw = $r['attachment_id'] ?? '';
|
||||
$scanned_raw = $r['external_scanned_at'] ?? '';
|
||||
$domains_raw = $r['top_domains'] ?? '';
|
||||
$mc_raw = $r['match_count'] ?? '';
|
||||
|
||||
$domains_decoded = is_string( $domains_raw ) && '' !== $domains_raw
|
||||
? json_decode( $domains_raw, true )
|
||||
: array();
|
||||
$domain_parts = array();
|
||||
if ( is_array( $domains_decoded ) ) {
|
||||
foreach ( $domains_decoded as $d ) {
|
||||
if ( is_string( $d ) ) {
|
||||
$domain_parts[] = $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
$domains_str = implode( '; ', $domain_parts );
|
||||
|
||||
$output[] = array(
|
||||
is_numeric( $aid_raw ) ? (string) (int) $aid_raw : '',
|
||||
is_string( $r['file_name'] ?? null ) ? (string) $r['file_name'] : '',
|
||||
is_string( $r['file_url'] ?? null ) ? (string) $r['file_url'] : '',
|
||||
is_string( $r['external_status'] ?? null ) ? (string) $r['external_status'] : '',
|
||||
is_string( $scanned_raw ) ? $scanned_raw : '',
|
||||
is_string( $r['provider'] ?? null ) ? (string) $r['provider'] : '',
|
||||
is_numeric( $mc_raw ) ? (string) (int) $mc_raw : '',
|
||||
$domains_str,
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues selected attachments for external scanning and redirects.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue