From 650e69349fb4359b09ba4d4ad1331b1f0769c2f3 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Wed, 3 Jun 2026 06:25:27 +0000 Subject: [PATCH] v1.2.0 --- assets/css/media-audit-admin.css | 131 ++++ assets/js/media-audit-admin.js | 86 ++- changelog.txt | 45 ++ includes/Admin/AuditPage.php | 138 +++++ includes/Admin/MediaListTable.php | 67 +- includes/Admin/ToolsPage.php | 776 ++++++++++++++++++++++++ includes/Core/Database.php | 17 + includes/Core/Plugin.php | 23 + includes/External/ExternalScanner.php | 89 +++ includes/Internal/AttachmentIndexer.php | 17 + includes/Internal/UsageScanner.php | 22 + languages/robotstxt-mediaaudit-ca.mo | Bin 8366 -> 18274 bytes languages/robotstxt-mediaaudit-ca.po | 612 ++++++++++++++++--- languages/robotstxt-mediaaudit-es_ES.mo | Bin 8215 -> 18072 bytes languages/robotstxt-mediaaudit-es_ES.po | 607 +++++++++++++++--- languages/robotstxt-mediaaudit.pot | 539 +++++++++++++--- messages.mo | Bin 7894 -> 0 bytes readme.txt | 15 +- robotstxt-mediaaudit.php | 11 +- vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_static.php | 1 + 21 files changed, 2924 insertions(+), 273 deletions(-) create mode 100644 includes/Admin/ToolsPage.php delete mode 100644 messages.mo diff --git a/assets/css/media-audit-admin.css b/assets/css/media-audit-admin.css index 23de874..0c4fa33 100644 --- a/assets/css/media-audit-admin.css +++ b/assets/css/media-audit-admin.css @@ -218,6 +218,99 @@ font-weight: 600; } +/* ----------------------------------------------------------------------- + Tools & Status page + ----------------------------------------------------------------------- */ + +/* Status table */ +.mra-status-table td.mra-status-dot-col { + width: 24px; + padding-right: 4px; +} + +.mra-status-table th { + white-space: nowrap; + width: 180px; + font-weight: 600; +} + +.mra-status-table td.mra-status-hint { + color: #50575e; + font-size: 12px; +} + +/* Status indicator dots */ +.mra-dot { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + vertical-align: middle; +} + +.mra-dot-ok { background: #00a32a; } +.mra-dot-warn { background: #dba617; } +.mra-dot-error { background: #d63638; } + +/* Job status labels in the scheduled-jobs row */ +.mra-job-label { + display: inline-block; + margin-right: 18px; +} + +/* WP-CLI hint below status table */ +.mra-cli-hint { + margin-top: 8px; +} + +.mra-cli-hint code { + margin-right: 8px; +} + +/* Operations section */ +.mra-ops-heading { + margin-top: 2em; +} + +.mra-destructive-heading { + margin-top: 1.5em; + color: #b32d2e; + font-size: 14px; + font-weight: 600; +} + +.mra-ops-grid { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin: 12px 0 20px; +} + +.mra-op-card { + background: #fff; + border: 1px solid #dcdcde; + border-radius: 4px; + padding: 16px 20px; + width: 280px; + max-width: 100%; + display: flex; + flex-direction: column; +} + +.mra-op-card-title { + margin: 0 0 8px; + font-size: 13px; + font-weight: 600; + color: #1d2327; +} + +.mra-op-card-desc { + flex: 1; + margin-bottom: 12px; + font-size: 12px; + line-height: 1.5; +} + /* Pricing reference table (settings page) */ .mra-pricing-table { margin-top: 8px; @@ -238,6 +331,44 @@ border-bottom: 1px solid #dcdcde; } +/* ----------------------------------------------------------------------- + Direct scan runner + ----------------------------------------------------------------------- */ + +.mra-runner-progress { + margin: 8px 0 10px; +} + +.mra-runner-bar { + height: 8px; + background: #dcdcde; + border-radius: 4px; + overflow: hidden; + margin-bottom: 4px; +} + +.mra-runner-bar-fill { + height: 100%; + width: 0; + background: #2271b1; + border-radius: 4px; + transition: width 0.25s ease; +} + +.mra-runner-label { + display: block; + font-size: 12px; + color: #50575e; + min-height: 1.4em; +} + +.mra-runner-actions { + display: flex; + gap: 8px; + align-items: center; + margin-top: 4px; +} + /* Consensus block */ .mra-consensus { background: #f0f8e8; diff --git a/assets/js/media-audit-admin.js b/assets/js/media-audit-admin.js index 3669edc..094d587 100644 --- a/assets/js/media-audit-admin.js +++ b/assets/js/media-audit-admin.js @@ -1,7 +1,86 @@ -/* global mraAdmin */ +/* global mraAdmin, mraTools */ ( function ( $ ) { 'use strict'; + // ------------------------------------------------------------------------- + // Batch runner — Tools page + // ------------------------------------------------------------------------- + + function initRunner( type ) { + var running = false; + var stopReq = false; + + var $card = $( '#mra-runner-' + type ); + var $start = $card.find( '.mra-runner-start' ); + var $stop = $card.find( '.mra-runner-stop' ); + var $prog = $card.find( '.mra-runner-progress' ); + var $fill = $card.find( '.mra-runner-bar-fill' ); + var $label = $card.find( '.mra-runner-label' ); + + $start.on( 'click', function () { + if ( running ) { return; } + running = true; + stopReq = false; + $start.prop( 'disabled', true ); + $stop.prop( 'hidden', false ); + $prog.prop( 'hidden', false ); + $fill.css( 'width', '0%' ); + $label.text( '' ); + runBatch(); + } ); + + $stop.on( 'click', function () { + stopReq = true; + $( this ).prop( 'disabled', true ); + } ); + + function runBatch() { + if ( stopReq ) { + finish( mraTools.i18n.stopped ); + return; + } + $.ajax( { + url: mraTools.ajaxUrl, + type: 'POST', + data: { + action: 'mra_run_batch', + type: type, + nonce: mraTools.nonce + }, + success: function ( res ) { + if ( ! res.success ) { + finish( mraTools.i18n.error ); + return; + } + var d = res.data; + var pct = d.total > 0 ? Math.round( ( d.done / d.total ) * 100 ) : 100; + $fill.css( 'width', pct + '%' ); + $label.text( d.done + ' / ' + d.total ); + if ( d.remaining > 0 ) { + setTimeout( runBatch, 300 ); + } else { + $fill.css( 'width', '100%' ); + finish( mraTools.i18n.done ); + } + }, + error: function () { + finish( mraTools.i18n.error ); + } + } ); + } + + function finish( msg ) { + running = false; + $start.prop( 'disabled', false ); + $stop.prop( 'hidden', true ).prop( 'disabled', false ); + $label.text( msg ); + } + } + + // ------------------------------------------------------------------------- + // Modal — Audit page + // ------------------------------------------------------------------------- + var $overlay, $modalTitle, $modalBody; function openModal( attachmentId ) { @@ -37,6 +116,11 @@ } $( function () { + // Initialize batch runners if mraTools is available (Tools page only). + if ( typeof mraTools !== 'undefined' ) { + [ 'index', 'usage', 'external' ].forEach( initRunner ); + } + $overlay = $( '#mra-modal-overlay' ); $modalTitle = $overlay.find( '.mra-modal-title' ); $modalBody = $overlay.find( '.mra-modal-body' ); diff --git a/changelog.txt b/changelog.txt index 91fceb2..bb2f71c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,50 @@ == Changelog == += 1.2.0 = + +_Release date: 2026-05-02_ + +**Added** + +* Browser-based AJAX scan runner in the Tools page: run index, usage, and external scan batches directly from the browser with a live progress bar. Useful when WP-Cron is disabled or Action Scheduler is not running. +* "Requeue scan errors" operation: re-queues only attachments that failed the last external scan run (status `error`), without touching already-scanned items. +* "Last Scanned" column in the audit list, showing a human-readable relative time with a precise date tooltip. Sortable with NULLs always listed last. +* Bulk action "Export External Results CSV": downloads a CSV with attachment metadata and all external scan results, including top matching domains per provider. + +**Compatibility** + +* WordPress: 6.8 - 7.0 +* PHP: 8.2 - 8.4 +* WP-CLI: 2.x + +**Tests** + +* PHP Coding Standards: WPCS 3.x / PHPCS 3.x +* PHPStan: level 9 +* PHPCompatibility: PHP 8.2 - 8.4 + += 1.1.0 = + +_Release date: 2026-05-02_ + +**Changed** + +* Plugin URI updated to `https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit`. +* Author changed to `ROBOTSTXT` with URI `https://www.robotstxt.es/`. +* Contributors updated to `javiercasares, robotstxt`. + +**Compatibility** + +* WordPress: 6.8 - 7.0 +* PHP: 8.2 - 8.4 +* WP-CLI: 2.x + +**Tests** + +* PHP Coding Standards: WPCS 3.x / PHPCS 3.x +* PHPStan: level 9 +* PHPCompatibility: PHP 8.2 - 8.4 + = 1.0.0 = _Release date: 2026-05-01_ diff --git a/includes/Admin/AuditPage.php b/includes/Admin/AuditPage.php index c4f82a9..b4cb720 100644 --- a/includes/Admin/AuditPage.php +++ b/includes/Admin/AuditPage.php @@ -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 $ids Attachment IDs to export (empty = all). + * + * @return array> + */ + 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. * diff --git a/includes/Admin/MediaListTable.php b/includes/Admin/MediaListTable.php index 2e32fc7..58b3a40 100644 --- a/includes/Admin/MediaListTable.php +++ b/includes/Admin/MediaListTable.php @@ -47,13 +47,14 @@ class MediaListTable extends \WP_List_Table { */ public function get_columns() { return array( - 'cb' => '', - 'thumbnail' => __( 'Thumbnail', 'robotstxt-mediaaudit' ), - 'filename' => __( 'Filename', 'robotstxt-mediaaudit' ), - 'attachment_id' => __( 'ID', 'robotstxt-mediaaudit' ), - 'usage_count' => __( 'Usage Count', 'robotstxt-mediaaudit' ), - 'usages' => __( 'Usages', 'robotstxt-mediaaudit' ), - 'external_status' => __( 'External Status', 'robotstxt-mediaaudit' ), + 'cb' => '', + 'thumbnail' => __( 'Thumbnail', 'robotstxt-mediaaudit' ), + 'filename' => __( 'Filename', 'robotstxt-mediaaudit' ), + 'attachment_id' => __( 'ID', 'robotstxt-mediaaudit' ), + 'usage_count' => __( 'Usage Count', 'robotstxt-mediaaudit' ), + 'usages' => __( 'Usages', 'robotstxt-mediaaudit' ), + 'external_status' => __( 'External Status', 'robotstxt-mediaaudit' ), + 'external_scanned_at' => __( 'Last Scanned', 'robotstxt-mediaaudit' ), ); } @@ -64,8 +65,9 @@ class MediaListTable extends \WP_List_Table { */ protected function get_sortable_columns() { return array( - 'attachment_id' => array( 'attachment_id', true ), - 'usage_count' => array( 'usage_count', false ), + 'attachment_id' => array( 'attachment_id', true ), + 'usage_count' => array( 'usage_count', false ), + 'external_scanned_at' => array( 'external_scanned_at', false ), ); } @@ -79,6 +81,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' ), ); } @@ -309,6 +312,47 @@ class MediaListTable extends \WP_List_Table { return $out; } + /** + * Renders the external scan date column. + * + * Shows a human-readable time difference (e.g. "3 days ago") with the full + * date/time as a tooltip. Displays "—" when the attachment has never been scanned. + * + * @param array $item Row data. + * + * @return string + */ + protected function column_external_scanned_at( $item ) { + $raw = $item['external_scanned_at']; + + if ( ! is_string( $raw ) || '' === $raw ) { + return '—'; + } + + $ts = strtotime( $raw ); + if ( false === $ts ) { + return '—'; + } + + $date_fmt = get_option( 'date_format' ); + $time_fmt = get_option( 'time_format' ); + $format = ( is_string( $date_fmt ) ? $date_fmt : 'Y-m-d' ) + . ' ' + . ( is_string( $time_fmt ) ? $time_fmt : 'H:i' ); + $full_val = wp_date( $format, $ts ); + $full = is_string( $full_val ) ? $full_val : $raw; + + return sprintf( + '%s', + esc_attr( $full ), + sprintf( + /* translators: %s: human-readable time difference, e.g. "3 days" */ + esc_html__( '%s ago', 'robotstxt-mediaaudit' ), + esc_html( human_time_diff( $ts ) ) + ) + ); + } + /** * Renders filter controls above the table. * @@ -391,7 +435,7 @@ class MediaListTable extends \WP_List_Table { $offset = ( $paged - 1 ) * $per_page; // Sorting. - $allowed_orderby = array( 'attachment_id', 'usage_count' ); + $allowed_orderby = array( 'attachment_id', 'usage_count', 'external_scanned_at' ); // phpcs:disable WordPress.Security.NonceVerification.Recommended $orderby_raw = isset( $_REQUEST['orderby'] ) && is_string( $_REQUEST['orderby'] ) ? sanitize_key( wp_unslash( $_REQUEST['orderby'] ) ) : ''; $orderby = in_array( $orderby_raw, $allowed_orderby, true ) ? $orderby_raw : 'attachment_id'; @@ -438,6 +482,9 @@ class MediaListTable extends \WP_List_Table { 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 ) { + // NULLs always last, regardless of sort direction. + $order_sql = "i.external_scanned_at IS NULL ASC, i.external_scanned_at {$order}"; } else { $order_sql = "i.attachment_id {$order}"; } diff --git a/includes/Admin/ToolsPage.php b/includes/Admin/ToolsPage.php new file mode 100644 index 0000000..a3e00d8 --- /dev/null +++ b/includes/Admin/ToolsPage.php @@ -0,0 +1,776 @@ +hook_suffix = $suffix; + add_action( 'load-' . $suffix, array( $this, 'handle_load' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); + } + + /** + * Enqueues CSS and JS assets for the Tools page. + * + * @param string $hook Current admin page hook suffix. + * + * @return void + */ + public function enqueue_assets( string $hook ): void { + if ( $hook !== $this->hook_suffix ) { + return; + } + + $base = ROBOTSTXT_MEDIAAUDIT_PLUGIN_URL . 'assets/'; + $ver = ROBOTSTXT_MEDIAAUDIT_VERSION; + + wp_enqueue_style( 'mra-admin', $base . 'css/media-audit-admin.css', array(), $ver ); + wp_enqueue_script( 'mra-admin', $base . 'js/media-audit-admin.js', array( 'jquery' ), $ver, true ); + + wp_localize_script( + 'mra-admin', + 'mraTools', + array( + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( self::NONCE_BATCH ), + 'i18n' => array( + 'done' => __( 'Done.', 'robotstxt-mediaaudit' ), + 'stopped' => __( 'Stopped.', 'robotstxt-mediaaudit' ), + 'error' => __( 'An error occurred.', 'robotstxt-mediaaudit' ), + ), + ) + ); + } + + /** + * AJAX handler for the batch runner. Processes one batch and returns progress data. + * + * Expected POST fields: type (index|usage|external), nonce. + * Returns JSON: {done: int, total: int, remaining: int}. + * + * @return void + */ + public function ajax_run_batch(): void { + check_ajax_referer( self::NONCE_BATCH, 'nonce' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'robotstxt-mediaaudit' ) ), 403 ); + } + + $type = sanitize_key( wp_unslash( isset( $_POST['type'] ) && is_string( $_POST['type'] ) ? $_POST['type'] : '' ) ); + + $remaining = 0; + $done = 0; + $total = 0; + + switch ( $type ) { + case 'index': + AttachmentIndexer::index_batch(); + $remaining = AttachmentIndexer::get_pending_count(); + $done = AttachmentIndexer::get_indexed_count(); + $total = $done + $remaining; + break; + + case 'usage': + UsageScanner::scan_batch(); + $remaining = UsageScanner::get_pending_count(); + $total = AttachmentIndexer::get_indexed_count(); + $done = max( 0, $total - $remaining ); + break; + + case 'external': + ExternalScanner::queue_all_pending(); + ExternalScanner::scan_batch(); + $counts = ExternalScanner::get_status_counts(); + $remaining = $counts['queued'] ?? 0; + $done = ( $counts['scanned'] ?? 0 ) + ( $counts['matches'] ?? 0 ) + ( $counts['error'] ?? 0 ); + $total = $done + $remaining; + break; + + default: + wp_send_json_error( array( 'message' => __( 'Invalid batch type.', 'robotstxt-mediaaudit' ) ), 400 ); + } + + wp_send_json_success( + array( + 'done' => $done, + 'total' => $total, + 'remaining' => $remaining, + ) + ); + } + + /** + * Fires before page HTML is output; processes POST operations and redirects. + * + * @return void + */ + public function handle_load(): void { + if ( ! isset( $_POST['mra_tools_op'] ) ) { + return; + } + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'Insufficient permissions.', 'robotstxt-mediaaudit' ) ); + } + check_admin_referer( self::NONCE_ACTION, self::NONCE_FIELD ); + + $op = sanitize_key( wp_unslash( is_string( $_POST['mra_tools_op'] ) ? $_POST['mra_tools_op'] : '' ) ); + $result = $this->dispatch_op( $op ); + + wp_safe_redirect( + add_query_arg( + array( + 'page' => 'robotstxt-mediaaudit-tools', + 'mra_notice' => $result['notice'], + 'mra_count' => $result['count'], + 'mra_op' => $op, + ), + admin_url( 'admin.php' ) + ) + ); + exit; + } + + /** + * Routes an operation key to the appropriate handler and returns a result array. + * + * @param string $op Operation key from the POST body. + * + * @return array{notice: string, count: int} + */ + private function dispatch_op( string $op ): array { + $count = 0; + + switch ( $op ) { + case 'schedule_internal': + AttachmentIndexer::schedule(); + UsageScanner::schedule(); + break; + + case 'schedule_external': + ExternalScanner::schedule(); + break; + + case 'requeue_errors': + $count = ExternalScanner::requeue_errors(); + break; + + case 'rescan_usage': + $count = UsageScanner::reset_all(); + UsageScanner::schedule(); + break; + + case 'reindex_all': + $count = AttachmentIndexer::reset_all(); + AttachmentIndexer::schedule(); + break; + + case 'purge_external': + $count = ExternalScanner::purge_all(); + break; + + case 'full_reset': + Database::truncate_all(); + AttachmentIndexer::schedule(); + break; + + default: + return array( + 'notice' => 'error', + 'count' => 0, + ); + } + + return array( + 'notice' => 'success', + 'count' => $count, + ); + } + + /** + * Renders the Tools & Status page. + * + * @return void + */ + public function render(): void { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'You do not have permission to access this page.', 'robotstxt-mediaaudit' ) ); + } + + $status = $this->get_system_status(); + $notice = $this->get_notice_data(); + ?> +
+

+ + render_notice( $notice ); ?> + +

+ render_status( $status ); ?> + +

+ render_operations(); ?> + +

+

+ +

+ render_runner(); ?> +
+ , jobs: array{index: bool, usage: bool, external: bool}} + */ + private function get_system_status(): array { + global $wpdb; + + $raw_ver = get_option( 'robotstxt_mediaaudit_db_version', '' ); + $stored_ver = is_string( $raw_ver ) ? $raw_ver : ''; + $as_ok = Scheduler::is_available(); + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $total_images = (int) $wpdb->get_var( + "SELECT COUNT(ID) FROM {$wpdb->posts} + WHERE post_type = 'attachment' + AND post_mime_type LIKE 'image/%' + AND post_status != 'trash'" + ); + + $indexed = AttachmentIndexer::get_indexed_count(); + $pending_idx = AttachmentIndexer::get_pending_count(); + $pending_use = UsageScanner::get_pending_count(); + $ext_counts = ExternalScanner::get_status_counts(); + + $fallback_ver = '' !== $stored_ver ? $stored_ver : __( 'not installed', 'robotstxt-mediaaudit' ); + + return array( + 'db_version_ok' => ROBOTSTXT_MEDIAAUDIT_DB_VERSION === $stored_ver, + 'stored_version' => $fallback_ver, + 'as_available' => $as_ok, + 'cron_disabled' => defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON, + 'total_images' => $total_images, + 'indexed' => $indexed, + 'pending_index' => $pending_idx, + 'pending_usage' => $pending_use, + 'external' => $ext_counts, + 'jobs' => array( + 'index' => $as_ok && Scheduler::has_pending( AttachmentIndexer::AS_HOOK ), + 'usage' => $as_ok && Scheduler::has_pending( UsageScanner::AS_HOOK ), + 'external' => $as_ok && Scheduler::has_pending( ExternalScanner::AS_HOOK ), + ), + ); + } + + /** + * Reads PRG redirect query parameters and returns structured notice data. + * + * @return array{type: string, op: string, count: int}|null + */ + private function get_notice_data(): ?array { + // phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( ! isset( $_GET['mra_notice'] ) ) { + return null; + } + $type = sanitize_key( wp_unslash( is_string( $_GET['mra_notice'] ) ? $_GET['mra_notice'] : '' ) ); + $op2 = sanitize_key( wp_unslash( isset( $_GET['mra_op'] ) && is_string( $_GET['mra_op'] ) ? $_GET['mra_op'] : '' ) ); + $count = isset( $_GET['mra_count'] ) && is_numeric( $_GET['mra_count'] ) ? (int) $_GET['mra_count'] : 0; + return array( + 'type' => $type, + 'op' => $op2, + 'count' => abs( $count ), + ); + // phpcs:enable WordPress.Security.NonceVerification.Recommended + } + + // ------------------------------------------------------------------------- + // Rendering helpers + // ------------------------------------------------------------------------- + + /** + * Renders a WP admin notice for a completed operation. + * + * @param array{type: string, op: string, count: int}|null $notice Notice data. + * + * @return void + */ + private function render_notice( ?array $notice ): void { + if ( null === $notice || 'success' !== $notice['type'] ) { + return; + } + + $messages = array( + 'schedule_internal' => __( 'Internal scan scheduled.', 'robotstxt-mediaaudit' ), + 'schedule_external' => __( 'External scan scheduled.', 'robotstxt-mediaaudit' ), + 'requeue_errors' => __( 'Failed scans re-queued. Background scan scheduled.', 'robotstxt-mediaaudit' ), + 'rescan_usage' => __( 'Usage data cleared. Background scan scheduled.', 'robotstxt-mediaaudit' ), + 'reindex_all' => __( 'Index cleared. Fresh indexing scheduled.', 'robotstxt-mediaaudit' ), + 'purge_external' => __( 'External scan data purged.', 'robotstxt-mediaaudit' ), + 'full_reset' => __( 'All data reset. Fresh indexing scheduled.', 'robotstxt-mediaaudit' ), + ); + + $msg = $messages[ $notice['op'] ] ?? __( 'Operation completed.', 'robotstxt-mediaaudit' ); + ?> +
+

+
+ , jobs: array{index: bool, usage: bool, external: bool}} $status Status data. + * + * @return void + */ + private function render_status( array $status ): void { + $db_ok = $status['db_version_ok']; + $as_ok = $status['as_available']; + $cron = $status['cron_disabled']; + $tot = $status['total_images']; + $idx = $status['indexed']; + $pi = $status['pending_index']; + $pu = $status['pending_usage']; + + $ext = $status['external']; + $ep = $ext['pending'] ?? 0; + $eq = $ext['queued'] ?? 0; + $es = $ext['scanned'] ?? 0; + $em = $ext['matches'] ?? 0; + $ee = $ext['error'] ?? 0; + + $jobs = $status['jobs']; + $ji = $jobs['index']; + $ju = $jobs['usage']; + $je = $jobs['external']; + + $needs_work = $pi > 0 || $pu > 0 || $ep > 0 || $eq > 0; + $any_job = $ji || $ju || $je; + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dot( $db_ok ? 'ok' : 'error' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + + + + +
dot( $as_ok ? 'ok' : 'error' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + + + + + + + + +
dot( $cron ? 'warn' : 'ok' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + + + + + + + + wp cron event run --due-now + +
dot( $pi > 0 ? 'warn' : 'ok' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + + 0 ) : ?> + + +
dot( $pu > 0 ? 'warn' : 'ok' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + + 0 ) : ?> + + +
+ 0 ) { + $ext_dot = 'error'; + } elseif ( $ep > 0 || $eq > 0 ) { + $ext_dot = 'warn'; + } else { + $ext_dot = 'ok'; + } + echo $this->dot( $ext_dot ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + + + + 0 ) : ?> + + 0 ) : ?> + + +
+ dot( $jobs_dot ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + + + + + + + + + + + + + + +
+ + +

+ + wp mra scan-internal + wp mra scan-external +

+ + 'schedule_internal', + 'label' => __( 'Schedule internal scan', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Queue a background job to index all unindexed media files and scan their usage across posts, pages, and custom fields. Safe to run at any time — will not re-process completed work.', 'robotstxt-mediaaudit' ), + 'class' => 'button button-primary', + 'confirm' => '', + ), + array( + 'op' => 'schedule_external', + 'label' => __( 'Schedule external scan', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Mark all pending attachments as queued and launch a background reverse-image-search via the configured providers. Requires API keys in Settings.', 'robotstxt-mediaaudit' ), + 'class' => 'button button-primary', + 'confirm' => '', + ), + array( + 'op' => 'requeue_errors', + 'label' => __( 'Requeue scan errors', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Re-queues only the attachments that failed with an error on the last external scan run. Does not affect already-scanned items. Useful to retry after fixing API key issues.', 'robotstxt-mediaaudit' ), + 'class' => 'button', + 'confirm' => '', + ), + ); + + $destructive = array( + array( + 'op' => 'rescan_usage', + 'label' => __( 'Reset usage data', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Clears all usage rows and resets the usage-scan flag for every indexed attachment. File metadata in the index is preserved. A background usage scan is scheduled immediately after.', 'robotstxt-mediaaudit' ), + 'class' => 'button', + 'confirm' => __( 'This will delete all usage data. Continue?', 'robotstxt-mediaaudit' ), + ), + array( + 'op' => 'reindex_all', + 'label' => __( 'Re-index media library', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Deletes all index and usage entries, then schedules a full fresh re-index. External scan results are not affected. Useful if many media files have been added or removed.', 'robotstxt-mediaaudit' ), + 'class' => 'button', + 'confirm' => __( 'This will delete all index and usage data. Continue?', 'robotstxt-mediaaudit' ), + ), + array( + 'op' => 'purge_external', + 'label' => __( 'Purge external data', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Deletes all external scan results and resets every attachment\'s external status to pending. Use this before switching API providers or to force a complete re-scan.', 'robotstxt-mediaaudit' ), + 'class' => 'button', + 'confirm' => __( 'This will delete all external scan results. Continue?', 'robotstxt-mediaaudit' ), + ), + array( + 'op' => 'full_reset', + 'label' => __( 'Reset all data', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Truncates all three plugin tables (index, usage, external results) and schedules a fresh indexing run. Use this to start completely from scratch.', 'robotstxt-mediaaudit' ), + 'class' => 'button button-link-delete', + 'confirm' => __( 'This will permanently delete ALL plugin data and cannot be undone. Are you absolutely sure?', 'robotstxt-mediaaudit' ), + ), + ); + ?> +
+ + render_op_card( $op ); ?> + +
+ +

+ +

+

+ +

+
+ + render_op_card( $op ); ?> + +
+ +
+

+

+
+ + + +
+
+ 'index', + 'label' => __( 'Index media files', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Discovers unindexed images and registers them in the plugin index. Run this first.', 'robotstxt-mediaaudit' ), + ), + array( + 'type' => 'usage', + 'label' => __( 'Scan usage', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Checks every indexed image for usage across posts, pages, and custom fields.', 'robotstxt-mediaaudit' ), + ), + array( + 'type' => 'external', + 'label' => __( 'External scan', 'robotstxt-mediaaudit' ), + 'desc' => __( 'Sends images to external providers for reverse-image search. Requires API keys in Settings.', 'robotstxt-mediaaudit' ), + ), + ); + ?> +
+ +
+

+

+ +
+ + +
+
+ +
+ '; + } +} diff --git a/includes/Core/Database.php b/includes/Core/Database.php index 8fa5a18..7f74413 100644 --- a/includes/Core/Database.php +++ b/includes/Core/Database.php @@ -57,6 +57,23 @@ class Database { $wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->prefix}mra_media_index`" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery } + /** + * Truncates all plugin tables, removing all data while preserving the table structure. + * + * Used by the Tools page "Reset all data" operation. + * + * @return void + */ + public static function truncate_all(): void { + global $wpdb; + + // phpcs:disable WordPress.DB.DirectDatabaseQuery + $wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_external_results`" ); + $wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_usage`" ); + $wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_index`" ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery + } + // ------------------------------------------------------------------------- // Migrations // ------------------------------------------------------------------------- diff --git a/includes/Core/Plugin.php b/includes/Core/Plugin.php index 7266f8e..8a5da87 100644 --- a/includes/Core/Plugin.php +++ b/includes/Core/Plugin.php @@ -9,6 +9,7 @@ namespace MediaRightsAudit\Core; use MediaRightsAudit\Admin\AuditPage; use MediaRightsAudit\Admin\Settings; +use MediaRightsAudit\Admin\ToolsPage; use MediaRightsAudit\CLI\Command; use MediaRightsAudit\External\AbstractProvider; use MediaRightsAudit\External\ExternalScanner; @@ -38,12 +39,20 @@ class Plugin { */ private AuditPage $audit_page; + /** + * Tools & Status page controller. + * + * @var ToolsPage + */ + private ToolsPage $tools_page; + /** * Initialises dependencies. */ public function __construct() { $this->settings = new Settings(); $this->audit_page = new AuditPage(); + $this->tools_page = new ToolsPage(); } /** @@ -59,6 +68,7 @@ class Plugin { add_action( AttachmentIndexer::AS_HOOK, array( AttachmentIndexer::class, 'process_scheduled_batch' ) ); add_action( UsageScanner::AS_HOOK, array( UsageScanner::class, 'process_scheduled_batch' ) ); add_action( ExternalScanner::AS_HOOK, array( ExternalScanner::class, 'process_scheduled_batch' ) ); + add_action( 'wp_ajax_mra_run_batch', array( $this->tools_page, 'ajax_run_batch' ) ); add_filter( 'mra/external/providers', array( $this, 'register_providers' ) ); add_filter( 'wp_privacy_personal_data_exporters', array( DataExporter::class, 'register' ) ); add_filter( 'wp_privacy_personal_data_erasers', array( DataEraser::class, 'register' ) ); @@ -107,6 +117,19 @@ class Plugin { array( $this->audit_page, 'render' ) ); + $tools_suffix = add_submenu_page( + 'robotstxt-mediaaudit', + __( 'Tools', 'robotstxt-mediaaudit' ), + __( 'Tools', 'robotstxt-mediaaudit' ), + 'manage_options', + 'robotstxt-mediaaudit-tools', + array( $this->tools_page, 'render' ) + ); + + if ( is_string( $tools_suffix ) ) { + $this->tools_page->set_hook_suffix( $tools_suffix ); + } + add_submenu_page( 'robotstxt-mediaaudit', __( 'Settings', 'robotstxt-mediaaudit' ), diff --git a/includes/External/ExternalScanner.php b/includes/External/ExternalScanner.php index 806c16d..3d7600c 100644 --- a/includes/External/ExternalScanner.php +++ b/includes/External/ExternalScanner.php @@ -184,6 +184,39 @@ class ExternalScanner { } } + /** + * Re-queues all attachments that previously failed with external_status = 'error'. + * + * Does not touch pending, queued, scanned, or matched attachments. + * Schedules a background scan batch if any items were re-queued and + * no batch is already pending. + * + * @return int Number of attachments re-queued. + */ + public static function requeue_errors(): int { + global $wpdb; + + // phpcs:disable WordPress.DB.DirectDatabaseQuery + $count = (int) $wpdb->get_var( + "SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_index WHERE external_status = 'error'" + ); + + if ( $count > 0 ) { + $wpdb->query( + "UPDATE {$wpdb->prefix}mra_media_index + SET external_status = 'queued', external_scanned_at = NULL + WHERE external_status = 'error'" + ); + + if ( ! Scheduler::has_pending( self::AS_HOOK ) ) { + Scheduler::schedule_single( self::AS_HOOK ); + } + } + // phpcs:enable WordPress.DB.DirectDatabaseQuery + + return $count; + } + /** * Deletes external scan results and resets status to pending for given IDs. * @@ -221,6 +254,62 @@ class ExternalScanner { // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare } + /** + * Deletes all external scan results and resets every attachment's external_status to pending. + * + * @return int Number of result rows deleted. + */ + public static function purge_all(): int { + global $wpdb; + + // phpcs:disable WordPress.DB.DirectDatabaseQuery + $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}mra_external_results" ); + $wpdb->query( "DELETE FROM {$wpdb->prefix}mra_external_results" ); + $wpdb->query( + "UPDATE {$wpdb->prefix}mra_media_index + SET external_status = 'pending', external_scanned_at = NULL" + ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery + + return $count; + } + + /** + * Returns a count of attachments grouped by external_status. + * + * @return array + */ + public static function get_status_counts(): array { + global $wpdb; + + $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery + "SELECT external_status, COUNT(*) AS cnt + FROM {$wpdb->prefix}mra_media_index + GROUP BY external_status", + ARRAY_A + ); + + $counts = array( + 'pending' => 0, + 'queued' => 0, + 'scanned' => 0, + 'matches' => 0, + 'error' => 0, + ); + + if ( is_array( $rows ) ) { + foreach ( $rows as $row ) { + $s = is_array( $row ) && is_string( $row['external_status'] ) ? $row['external_status'] : ''; + if ( isset( $counts[ $s ] ) ) { + $cnt = isset( $row['cnt'] ) && is_numeric( $row['cnt'] ) ? (int) $row['cnt'] : 0; + $counts[ $s ] = $cnt; + } + } + } + + return $counts; + } + // ------------------------------------------------------------------------- // Private helpers // ------------------------------------------------------------------------- diff --git a/includes/Internal/AttachmentIndexer.php b/includes/Internal/AttachmentIndexer.php index 0ba069f..4f213b0 100644 --- a/includes/Internal/AttachmentIndexer.php +++ b/includes/Internal/AttachmentIndexer.php @@ -172,4 +172,21 @@ class AttachmentIndexer { Scheduler::schedule_single( self::AS_HOOK ); } } + + /** + * Deletes all rows from mra_media_index and mra_media_usage, forcing a full re-index on the next run. + * + * @return int Number of index rows removed. + */ + public static function reset_all(): int { + global $wpdb; + + // phpcs:disable WordPress.DB.DirectDatabaseQuery + $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_index" ); + $wpdb->query( "DELETE FROM {$wpdb->prefix}mra_media_usage" ); + $wpdb->query( "DELETE FROM {$wpdb->prefix}mra_media_index" ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery + + return $count; + } } diff --git a/includes/Internal/UsageScanner.php b/includes/Internal/UsageScanner.php index 17558cc..470d859 100644 --- a/includes/Internal/UsageScanner.php +++ b/includes/Internal/UsageScanner.php @@ -461,4 +461,26 @@ class UsageScanner { Scheduler::schedule_single( self::AS_HOOK ); } } + + /** + * Resets usage scan state for all indexed attachments. + * + * Clears internal_scanned_at so every attachment is re-queued for scanning, and + * deletes all mra_media_usage rows. File metadata in mra_media_index is preserved. + * + * @return int Number of attachments reset. + */ + public static function reset_all(): int { + global $wpdb; + + // phpcs:disable WordPress.DB.DirectDatabaseQuery + $count = (int) $wpdb->get_var( + "SELECT COUNT(*) FROM {$wpdb->prefix}mra_media_index WHERE internal_scanned_at IS NOT NULL" + ); + $wpdb->query( "UPDATE {$wpdb->prefix}mra_media_index SET internal_scanned_at = NULL" ); + $wpdb->query( "DELETE FROM {$wpdb->prefix}mra_media_usage" ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery + + return $count; + } } diff --git a/languages/robotstxt-mediaaudit-ca.mo b/languages/robotstxt-mediaaudit-ca.mo index f18b88b7722dcfbb38a9873664c286cfed7eb8dc..90b83b0f1337a21aaf9ee5b0389661a2a62e261f 100644 GIT binary patch literal 18274 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 Jcv=*}{{iUDr!fEk delta 3085 zcmbu@S4vS4MHCe*>)o{+T(|UY57CGh zBM?Q!=&1M-o2Ud8A}Wmu2L35ECdNQ~At2ExF)>jzK}b|gOz`_V7NY*^KI2^yn zJUorp;CUQ@*Km`rk4HUMiTtu=I>#$%n2#&54X?nRn2)yTu@gvN2gc{gs)POFh zH@#;94#9HNd#bPyL&&YB6-VMy97F%cp)#6=`%xV}iJCzl*5h8(3{LvjPvcdb2N;DM zhU&Ngxn#zp22g<-cnxZx4OonAsOO?Mk^W7RN)c|u8hjnc;Sp5FKVmojftpF2{HgsR z)N@bxzJZ$I0VM0@W7L2?Lw@EP4lQ`fZx8X3?06a)sZ`-|oPrOdQoR$EszZ1)eu?U! zl=MWf7S%y7YNne|Gu?v9z)Q%OO&@A0_8>p=zTZBONB)(HBQ)s2*WQhFLmg836Q!>jlt^ulXVnHcRGM5Vmezut(tzZu8iLR4l}X8lUScLRpG@F;3# z{Wuj5T7B9&$Fxj8`h&9 z*n<28m}l`pd;zs~`SeELrU;dxYOKU*e*4|X&vbK8#x|oe^A_s)cYQxVP4EEHU)KCg zMZ5SMYGxxjnS>3fJ#iaqV4XM@)2Ny5Lw;tEgJ%3SD&?n89iQ{t&!d)j1k={Qi%{<^ z!O8mm8>ncLtUzs=C~9Vp;BHu}0|Ts&mgXhY00vRdf8qNpD)l4y#PnP_ z@~8S6}3m+M7?jn@5iW_A4N^QzhBgDZA6yStVJ!wX5?$k zm317op_FnJeI>eg0ntdPD9b7u^jspU6Utcn&y~p>tB5*69jqo);>1iMLUa%JK^MN|2{0?bPA!()=V@JDwh#=60?X_LM2Aj>-*Qb>$6kQTIUhPI-w+q^@KK! z${3<6S2Jb~-b~!&pNkseI{&;IwK?x1?j=?cR}dA%9mMs-rHy1Y7gXB4fi}Jx~4nsdcpRHjg9KH#|k>i^u5_V2SUNu(cT&+cW?7DSnu_No+%77fQlW9ThX| zv5JV@Tv^w$#`D&v8^htXxdBBlj3*TB={6UyqgZDguWY;DH%?4sJI?68n$ zPln2elgWge$Ys*8-SrXsW&L)WnO19OHS8MJ986XYzLU#l)AgHd`}8vV!1Nbw{fz1n z|4H7~8LRqVnRzN;?W|>XXyaD<^z5)5n6t_@H4WOjxfAW&c}e@>yv4;W@l@0?9D6gQ utEI>7Omt;ZPBbytZ%;HA+vP2_b|6w_pJ{EfWeYoPURzz{Vix{Ke$3xJPN${- diff --git a/languages/robotstxt-mediaaudit-ca.po b/languages/robotstxt-mediaaudit-ca.po index 4adb207..07b72d0 100644 --- a/languages/robotstxt-mediaaudit-ca.po +++ b/languages/robotstxt-mediaaudit-ca.po @@ -2,10 +2,10 @@ # This file is distributed under the GPL-3.0-or-later. msgid "" msgstr "" -"Project-Id-Version: Media Audit (by ROBOTSTXT) 1.0.0\n" +"Project-Id-Version: Media Audit (by ROBOTSTXT) 1.2.0\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/robotstxt-" "mediaaudit\n" -"POT-Creation-Date: 2026-05-02T06:06:40+00:00\n" +"POT-Creation-Date: 2026-05-02T07:36:54+00:00\n" "PO-Revision-Date: 2026-05-01 07:41+0000\n" "Last-Translator: Javier Casares \n" "Language-Team: Catalan \n" @@ -23,8 +23,8 @@ msgstr "Auditoria de mitjans (by ROBOTSTXT)" #. Plugin URI of the plugin #: robotstxt-mediaaudit.php -msgid "https://robotstxt.es/plugins/media-audit/" -msgstr "https://robotstxt.es/plugins/media-audit/" +msgid "https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit" +msgstr "" #. Description of the plugin #: robotstxt-mediaaudit.php @@ -37,279 +37,311 @@ msgstr "" #. Author of the plugin #: robotstxt-mediaaudit.php -msgid "Javier Casares" -msgstr "Javier Casares" +msgid "ROBOTSTXT" +msgstr "" #. Author URI of the plugin #: robotstxt-mediaaudit.php -msgid "https://javiercasares.com" -msgstr "https://javiercasares.com" +msgid "https://www.robotstxt.es/" +msgstr "" -#: includes/Admin/AuditPage.php:112 +#: includes/Admin/AuditPage.php:114 msgid "Loading…" msgstr "S'està carregant…" -#: includes/Admin/AuditPage.php:113 +#: includes/Admin/AuditPage.php:115 msgid "Error loading details." msgstr "S'ha produït un error en carregar els detalls." -#: includes/Admin/AuditPage.php:114 includes/Admin/AuditPage.php:190 +#: includes/Admin/AuditPage.php:116 includes/Admin/AuditPage.php:192 msgid "This image is not used in any post." msgstr "Aquesta imatge no s'utilitza en cap entrada." -#: includes/Admin/AuditPage.php:115 includes/Admin/AuditPage.php:152 +#: includes/Admin/AuditPage.php:117 includes/Admin/AuditPage.php:154 msgid "Usage Details" msgstr "Detalls d'ús" -#: includes/Admin/AuditPage.php:131 includes/Admin/Settings.php:325 +#: includes/Admin/AuditPage.php:133 includes/Admin/Settings.php:325 +#: includes/Admin/ToolsPage.php:245 msgid "You do not have permission to access this page." msgstr "No teniu permís per accedir a aquesta pàgina." -#: includes/Admin/AuditPage.php:138 includes/Core/Plugin.php:89 -#: includes/Core/Plugin.php:90 includes/Core/Plugin.php:103 -#: includes/Core/Plugin.php:104 includes/Privacy/DataEraser.php:33 +#: includes/Admin/AuditPage.php:140 includes/Core/Plugin.php:99 +#: includes/Core/Plugin.php:100 includes/Core/Plugin.php:113 +#: includes/Core/Plugin.php:114 includes/Privacy/DataEraser.php:33 #: includes/Privacy/DataExporter.php:45 msgid "Media Audit" msgstr "Auditoria de mitjans" -#: includes/Admin/AuditPage.php:144 +#: includes/Admin/AuditPage.php:146 msgid "Search" msgstr "Cerca" -#: includes/Admin/AuditPage.php:153 +#: includes/Admin/AuditPage.php:155 msgid "Close" msgstr "Tanca" -#: includes/Admin/AuditPage.php:171 includes/Admin/AuditPage.php:459 -#: includes/Admin/AuditPage.php:510 includes/Admin/AuditPage.php:542 +#: includes/Admin/AuditPage.php:173 includes/Admin/AuditPage.php:461 +#: includes/Admin/AuditPage.php:515 includes/Admin/AuditPage.php:648 +#: includes/Admin/AuditPage.php:680 includes/Admin/ToolsPage.php:105 +#: includes/Admin/ToolsPage.php:161 msgid "Insufficient permissions." msgstr "Permisos insuficients." -#: includes/Admin/AuditPage.php:176 +#: includes/Admin/AuditPage.php:178 msgid "Invalid attachment ID." msgstr "Identificador de fitxer adjunt no vàlid." -#: includes/Admin/AuditPage.php:184 +#: includes/Admin/AuditPage.php:186 msgid "Featured Image" msgstr "Imatge destacada" -#: includes/Admin/AuditPage.php:185 +#: includes/Admin/AuditPage.php:187 msgid "Post Content" msgstr "Contingut de l'entrada" -#: includes/Admin/AuditPage.php:186 +#: includes/Admin/AuditPage.php:188 msgid "Custom Field" msgstr "Camp personalitzat" -#: includes/Admin/AuditPage.php:194 +#: includes/Admin/AuditPage.php:196 msgid "Post" msgstr "Entrada" -#: includes/Admin/AuditPage.php:195 +#: includes/Admin/AuditPage.php:197 msgid "Type" msgstr "Tipus" -#: includes/Admin/AuditPage.php:196 +#: includes/Admin/AuditPage.php:198 msgid "Context" msgstr "Context" -#: includes/Admin/AuditPage.php:197 +#: includes/Admin/AuditPage.php:199 msgid "Status" msgstr "Estat" -#: includes/Admin/AuditPage.php:240 +#: includes/Admin/AuditPage.php:242 msgid "Internal Usage" msgstr "Ús intern" -#: includes/Admin/AuditPage.php:288 +#: includes/Admin/AuditPage.php:290 msgid "External Scan Results" msgstr "Resultats de l'exploració externa" #. translators: %d: number of external matches -#: includes/Admin/AuditPage.php:312 +#: includes/Admin/AuditPage.php:314 #, php-format msgid "%d match" msgid_plural "%d matches" msgstr[0] "%d coincidència" msgstr[1] "%d coincidències" -#: includes/Admin/AuditPage.php:317 +#: includes/Admin/AuditPage.php:319 msgid "No matches" msgstr "Sense coincidències" -#: includes/Admin/AuditPage.php:328 +#: includes/Admin/AuditPage.php:330 msgid "Domain" msgstr "Domini" -#: includes/Admin/AuditPage.php:329 +#: includes/Admin/AuditPage.php:331 msgid "Occurrences" msgstr "Aparicions" -#: includes/Admin/AuditPage.php:350 +#: includes/Admin/AuditPage.php:352 msgid "Consensus Domains" msgstr "Dominis de consens" #. translators: %d: number of providers that agree -#: includes/Admin/AuditPage.php:356 +#: includes/Admin/AuditPage.php:358 #, php-format msgid "%d provider" msgid_plural "%d providers" msgstr[0] "%d proveïdor" msgstr[1] "%d proveïdors" -#: includes/Admin/AuditPage.php:388 +#: includes/Admin/AuditPage.php:390 msgid "In Index" msgstr "A l'índex" #. translators: %d: count of images not yet indexed -#: includes/Admin/AuditPage.php:392 +#: includes/Admin/AuditPage.php:394 #, php-format msgid "%d not yet indexed" msgstr "%d encara no indexades" -#: includes/Admin/AuditPage.php:395 +#: includes/Admin/AuditPage.php:397 msgid "All indexed" msgstr "Totes indexades" -#: includes/Admin/AuditPage.php:400 includes/Admin/MediaListTable.php:275 +#: includes/Admin/AuditPage.php:402 includes/Admin/MediaListTable.php:278 msgid "Scanned" msgstr "Explorades" #. translators: %d: percentage -#: includes/Admin/AuditPage.php:403 +#: includes/Admin/AuditPage.php:405 #, php-format msgid "%d%% of index" msgstr "%d%% de l'índex" -#: includes/Admin/AuditPage.php:410 +#: includes/Admin/AuditPage.php:412 msgid "Used" msgstr "Usades" -#: includes/Admin/AuditPage.php:416 includes/Admin/MediaListTable.php:200 +#: includes/Admin/AuditPage.php:418 includes/Admin/MediaListTable.php:203 msgid "Unused" msgstr "No usades" -#: includes/Admin/AuditPage.php:423 +#: includes/Admin/AuditPage.php:425 msgid "External Matches" msgstr "Coincidències externes" -#: includes/Admin/AuditPage.php:473 +#: includes/Admin/AuditPage.php:475 includes/Admin/AuditPage.php:529 msgid "Could not open output stream." msgstr "No s'ha pogut obrir el flux de sortida." -#: includes/Admin/AuditPage.php:482 includes/Privacy/DataExporter.php:257 -#: includes/Privacy/DataExporter.php:299 +#: includes/Admin/AuditPage.php:484 includes/Admin/AuditPage.php:537 +#: includes/Privacy/DataExporter.php:257 includes/Privacy/DataExporter.php:299 msgid "Attachment ID" msgstr "ID del fitxer adjunt" -#: includes/Admin/AuditPage.php:483 includes/Admin/MediaListTable.php:52 -#: includes/Privacy/DataExporter.php:261 +#: includes/Admin/AuditPage.php:485 includes/Admin/AuditPage.php:538 +#: includes/Admin/MediaListTable.php:52 includes/Privacy/DataExporter.php:261 msgid "Filename" msgstr "Nom de fitxer" -#: includes/Admin/AuditPage.php:484 includes/Privacy/DataExporter.php:265 +#: includes/Admin/AuditPage.php:486 includes/Admin/AuditPage.php:539 +#: includes/Privacy/DataExporter.php:265 msgid "File URL" msgstr "URL del fitxer" -#: includes/Admin/AuditPage.php:485 +#: includes/Admin/AuditPage.php:487 msgid "MIME Type" msgstr "Tipus MIME" -#: includes/Admin/AuditPage.php:486 +#: includes/Admin/AuditPage.php:488 msgid "File Size (bytes)" msgstr "Mida del fitxer (bytes)" -#: includes/Admin/AuditPage.php:487 includes/Admin/MediaListTable.php:56 +#: includes/Admin/AuditPage.php:489 includes/Admin/AuditPage.php:540 +#: includes/Admin/MediaListTable.php:56 msgid "External Status" msgstr "Estat extern" -#: includes/Admin/AuditPage.php:488 includes/Admin/MediaListTable.php:54 +#: includes/Admin/AuditPage.php:490 includes/Admin/MediaListTable.php:54 msgid "Usage Count" msgstr "Nombre d'usos" -#: includes/Admin/AuditPage.php:489 +#: includes/Admin/AuditPage.php:491 msgid "Used In (post titles)" msgstr "Usat a (títols d'entrades)" +#: includes/Admin/AuditPage.php:541 includes/Admin/MediaListTable.php:57 +msgid "Last Scanned" +msgstr "Darrera exploració" + +#: includes/Admin/AuditPage.php:542 includes/Privacy/DataExporter.php:303 +msgid "Provider" +msgstr "Proveïdor" + +#: includes/Admin/AuditPage.php:543 includes/Privacy/DataExporter.php:307 +msgid "Match Count" +msgstr "Nombre de coincidències" + +#: includes/Admin/AuditPage.php:544 +msgid "Top Domains" +msgstr "Dominis principals" + #: includes/Admin/MediaListTable.php:51 msgid "Thumbnail" msgstr "Miniatura" #: includes/Admin/MediaListTable.php:53 msgid "ID" -msgstr "" +msgstr "ID" #: includes/Admin/MediaListTable.php:55 msgid "Usages" msgstr "Usos" -#: includes/Admin/MediaListTable.php:79 +#: includes/Admin/MediaListTable.php:81 msgid "Run External Scan" msgstr "Executar exploració externa" -#: includes/Admin/MediaListTable.php:80 +#: includes/Admin/MediaListTable.php:82 msgid "Purge External Data" msgstr "Eliminar dades externes" -#: includes/Admin/MediaListTable.php:81 +#: includes/Admin/MediaListTable.php:83 msgid "Export CSV" msgstr "Exportar CSV" -#: includes/Admin/MediaListTable.php:165 +#: includes/Admin/MediaListTable.php:84 +msgid "Export External Results CSV" +msgstr "Exportar CSV de resultats externs" + +#: includes/Admin/MediaListTable.php:168 msgid "Edit" msgstr "Edita" -#: includes/Admin/MediaListTable.php:170 +#: includes/Admin/MediaListTable.php:173 msgid "View Details" msgstr "Visualitza els detalls" -#: includes/Admin/MediaListTable.php:236 +#: includes/Admin/MediaListTable.php:239 msgid "(no title)" msgstr "(sense títol)" #. translators: %d: number of additional usages -#: includes/Admin/MediaListTable.php:251 +#: includes/Admin/MediaListTable.php:254 #, php-format msgid "+ %d more…" msgstr "+ %d més…" -#: includes/Admin/MediaListTable.php:273 +#: includes/Admin/MediaListTable.php:276 includes/Admin/ToolsPage.php:580 +#: includes/Admin/ToolsPage.php:581 includes/Admin/ToolsPage.php:582 msgid "Pending" msgstr "Pendent" -#: includes/Admin/MediaListTable.php:274 +#: includes/Admin/MediaListTable.php:277 msgid "Queued" msgstr "En cua" -#: includes/Admin/MediaListTable.php:276 +#: includes/Admin/MediaListTable.php:279 msgid "Matches Found" msgstr "Coincidències trobades" -#: includes/Admin/MediaListTable.php:277 +#: includes/Admin/MediaListTable.php:280 msgid "Error" msgstr "Error" -#: includes/Admin/MediaListTable.php:305 +#: includes/Admin/MediaListTable.php:308 msgid "View Results" msgstr "Visualitza els resultats" -#: includes/Admin/MediaListTable.php:336 +#. translators: %s: human-readable time difference, e.g. "3 days" +#: includes/Admin/MediaListTable.php:350 +#, php-format +msgid "%s ago" +msgstr "fa %s" + +#: includes/Admin/MediaListTable.php:380 msgid "All statuses" msgstr "Tots els estats" -#: includes/Admin/MediaListTable.php:350 +#: includes/Admin/MediaListTable.php:394 msgid "All post types" msgstr "Tots els tipus d'entrada" -#: includes/Admin/MediaListTable.php:365 +#: includes/Admin/MediaListTable.php:409 msgid "Unused only" msgstr "Només no usades" -#: includes/Admin/MediaListTable.php:368 +#: includes/Admin/MediaListTable.php:412 msgid "Filter" msgstr "Filtra" -#: includes/Admin/MediaListTable.php:378 +#: includes/Admin/MediaListTable.php:422 msgid "" "No indexed attachments found. Run wp mra scan-internal to populate the index." msgstr "" @@ -434,6 +466,417 @@ msgstr "" "Nombre màxim de sol·licituds d'API per minut per proveïdor (1–60). Per " "defecte: 10." +#: includes/Admin/ToolsPage.php:85 +msgid "Done." +msgstr "Fet." + +#: includes/Admin/ToolsPage.php:86 +msgid "Stopped." +msgstr "Aturat." + +#: includes/Admin/ToolsPage.php:87 +msgid "An error occurred." +msgstr "S'ha produït un error." + +#: includes/Admin/ToolsPage.php:139 +msgid "Invalid batch type." +msgstr "Tipus de lot no vàlid." + +#: includes/Admin/ToolsPage.php:256 +msgid "System Status" +msgstr "Estat del sistema" + +#: includes/Admin/ToolsPage.php:259 +msgid "Operations" +msgstr "Operacions" + +#: includes/Admin/ToolsPage.php:262 +msgid "Direct scan runner" +msgstr "Executor d'exploració directa" + +#: includes/Admin/ToolsPage.php:264 +msgid "" +"Run scan batches directly from your browser without relying on background " +"jobs. Useful when WP-Cron is disabled or Action Scheduler is not running. " +"Keep this tab open while processing." +msgstr "" +"Executeu lots d'exploració directament des del navegador sense dependre de " +"treballs en segon pla. Útil quan WP-Cron està desactivat o Action Scheduler " +"no s'està executant. Manteniu aquesta pestanya oberta mentre es processa." + +#: includes/Admin/ToolsPage.php:300 +msgid "not installed" +msgstr "no instal·lat" + +#: includes/Admin/ToolsPage.php:358 +msgid "Internal scan scheduled." +msgstr "Exploració interna programada." + +#: includes/Admin/ToolsPage.php:359 +msgid "External scan scheduled." +msgstr "Exploració externa programada." + +#: includes/Admin/ToolsPage.php:360 +msgid "Failed scans re-queued. Background scan scheduled." +msgstr "" +"Exploracions fallides reencolades. Exploració en segon pla " +"programada." + +#: includes/Admin/ToolsPage.php:361 +msgid "Usage data cleared. Background scan scheduled." +msgstr "Dades d'ús eliminades. Exploració en segon pla programada." + +#: includes/Admin/ToolsPage.php:362 +msgid "Index cleared. Fresh indexing scheduled." +msgstr "Índex eliminat. Nova indexació programada." + +#: includes/Admin/ToolsPage.php:363 +msgid "External scan data purged." +msgstr "Dades d'exploració externa eliminades." + +#: includes/Admin/ToolsPage.php:364 +msgid "All data reset. Fresh indexing scheduled." +msgstr "Totes les dades restablertes. Nova indexació programada." + +#: includes/Admin/ToolsPage.php:367 +msgid "Operation completed." +msgstr "Operació completada." + +#: includes/Admin/ToolsPage.php:411 +msgid "Database schema" +msgstr "Esquema de base de dades" + +#. translators: %s: version number +#: includes/Admin/ToolsPage.php:417 +#, php-format +msgid "v%s – up to date" +msgstr "v%s – actualitzat" + +#. translators: 1: stored version, 2: expected version +#: includes/Admin/ToolsPage.php:423 +#, php-format +msgid "Installed: %1$s — Required: %2$s" +msgstr "Instal·lat: %1$s — Requerit: %2$s" + +#: includes/Admin/ToolsPage.php:432 +msgid "Deactivate and reactivate the plugin to apply pending migrations." +msgstr "" +"Desactiveu i reactiveu el connector per aplicar les migracions " +"pendents." + +#: includes/Admin/ToolsPage.php:439 +msgid "Action Scheduler" +msgstr "Action Scheduler" + +#: includes/Admin/ToolsPage.php:442 +msgid "Available" +msgstr "Disponible" + +#: includes/Admin/ToolsPage.php:444 +msgid "Not available" +msgstr "No disponible" + +#: includes/Admin/ToolsPage.php:449 +msgid "" +"Install and activate the Action Scheduler plugin. Background scanning will " +"not work without it." +msgstr "" +"Instal·leu i activeu el connector Action Scheduler. L'exploració en segon " +"pla no funcionarà sense ell." + +#: includes/Admin/ToolsPage.php:456 +msgid "WP-Cron" +msgstr "WP-Cron" + +#: includes/Admin/ToolsPage.php:459 +msgid "Disabled (DISABLE_WP_CRON)" +msgstr "Desactivat (DISABLE_WP_CRON)" + +#: includes/Admin/ToolsPage.php:461 +msgid "Enabled" +msgstr "Activat" + +#: includes/Admin/ToolsPage.php:466 +msgid "" +"Background jobs will not fire automatically. Trigger the cron externally or " +"use WP-CLI:" +msgstr "" +"Els treballs en segon pla no s'executaran automàticament. Activeu el cron " +"externament o useu WP-CLI:" + +#: includes/Admin/ToolsPage.php:474 +msgid "Internal index" +msgstr "Índex intern" + +#. translators: 1: indexed count, 2: total images +#: includes/Admin/ToolsPage.php:479 +#, php-format +msgid "%1$s / %2$s images indexed" +msgstr "%1$s / %2$s imatges indexades" + +#. translators: %s: formatted count +#: includes/Admin/ToolsPage.php:490 +#, php-format +msgid "%s image not yet indexed." +msgid_plural "%s images not yet indexed." +msgstr[0] "%s imatge encara no indexada." +msgstr[1] "%s imatges encara no indexades." + +#: includes/Admin/ToolsPage.php:494 +msgid "Schedule an internal scan below." +msgstr "Programeu una exploració interna a continuació." + +#: includes/Admin/ToolsPage.php:502 +msgid "Usage scan" +msgstr "Exploració d'ús" + +#. translators: 1: scanned count, 2: total indexed +#: includes/Admin/ToolsPage.php:507 +#, php-format +msgid "%1$s / %2$s scanned for usage" +msgstr "%1$s / %2$s explorades per a ús" + +#. translators: %s: formatted count +#: includes/Admin/ToolsPage.php:518 +#, php-format +msgid "%s pending." +msgid_plural "%s pending." +msgstr[0] "%s pendent." +msgstr[1] "%s pendents." + +#: includes/Admin/ToolsPage.php:539 includes/Admin/ToolsPage.php:736 +msgid "External scan" +msgstr "Exploració externa" + +#. translators: 1: scanned 2: matches 3: queued 4: pending 5: errors +#: includes/Admin/ToolsPage.php:544 +#, php-format +msgid "%1$s scanned · %2$s matches · %3$s queued · %4$s pending · %5$s errors" +msgstr "" +"%1$s explorades · %2$s coincidències · %3$s en cua · %4$s pendents · " +"%5$s errors" + +#: includes/Admin/ToolsPage.php:555 +msgid "" +"Some scans failed. Check that API keys are configured correctly in Settings." +msgstr "" +"Algunes exploracions han fallat. Comproveu que les claus d'API estan " +"configurades correctament a la Configuració." + +#: includes/Admin/ToolsPage.php:557 +msgid "Schedule an external scan below to process pending items." +msgstr "" +"Programeu una exploració externa a continuació per processar els elements " +"pendents." + +#: includes/Admin/ToolsPage.php:575 +msgid "Scheduled jobs" +msgstr "Treballs programats" + +#: includes/Admin/ToolsPage.php:578 +msgid "Action Scheduler not available." +msgstr "Action Scheduler no disponible." + +#: includes/Admin/ToolsPage.php:580 +msgid "Index:" +msgstr "Índex:" + +#: includes/Admin/ToolsPage.php:581 +msgid "Usage:" +msgstr "Ús:" + +#: includes/Admin/ToolsPage.php:582 +msgid "External:" +msgstr "Extern:" + +#: includes/Admin/ToolsPage.php:587 +msgid "" +"There is work to do but no background job is scheduled. Use the operations " +"below." +msgstr "" +"Hi ha feina pendent però no hi ha cap treball en segon pla programat. " +"Useu les operacions de sota." + +#: includes/Admin/ToolsPage.php:589 +msgid "WP-Cron is disabled. Jobs will not fire automatically." +msgstr "" +"WP-Cron està desactivat. Els treballs no s'executaran " +"automàticament." + +#: includes/Admin/ToolsPage.php:599 +msgid "WP-CLI commands to process scans manually:" +msgstr "Comandes WP-CLI per processar exploracions manualment:" + +#: includes/Admin/ToolsPage.php:616 +msgid "Schedule internal scan" +msgstr "Programar exploració interna" + +#: includes/Admin/ToolsPage.php:617 +msgid "" +"Queue a background job to index all unindexed media files and scan their " +"usage across posts, pages, and custom fields. Safe to run at any time — will " +"not re-process completed work." +msgstr "" +"Posa en cua un treball en segon pla per indexar tots els fitxers multimèdia " +"no indexats i explorar el seu ús en entrades, pàgines i camps personalitzats. " +"Es pot executar en qualsevol moment: no reprocessarà el treball ja completat." + +#: includes/Admin/ToolsPage.php:623 +msgid "Schedule external scan" +msgstr "Programar exploració externa" + +#: includes/Admin/ToolsPage.php:624 +msgid "" +"Mark all pending attachments as queued and launch a background reverse-image-" +"search via the configured providers. Requires API keys in Settings." +msgstr "" +"Marca tots els fitxers adjunts pendents com en cua i llança una cerca inversa " +"d'imatges en segon pla a través dels proveïdors configurats. Requereix claus " +"d'API a la Configuració." + +#: includes/Admin/ToolsPage.php:630 +msgid "Requeue scan errors" +msgstr "Reencolar errors d'exploració" + +#: includes/Admin/ToolsPage.php:631 +msgid "" +"Re-queues only the attachments that failed with an error on the last " +"external scan run. Does not affect already-scanned items. Useful to retry " +"after fixing API key issues." +msgstr "" +"Reencola només els fitxers adjunts que van fallar amb un error en l'última " +"execució de l'exploració externa. No afecta els elements ja explorats. " +"Útil per reintentar després de corregir problemes amb les claus d'API." + +#: includes/Admin/ToolsPage.php:640 +msgid "Reset usage data" +msgstr "Restablir dades d'ús" + +#: includes/Admin/ToolsPage.php:641 +msgid "" +"Clears all usage rows and resets the usage-scan flag for every indexed " +"attachment. File metadata in the index is preserved. A background usage scan " +"is scheduled immediately after." +msgstr "" +"Elimina totes les files d'ús i restableix l'indicador d'exploració d'ús per " +"a cada fitxer adjunt indexat. Els metadades del fitxer a l'índex es " +"conserven. Es programa una exploració en segon pla immediatament després." + +#: includes/Admin/ToolsPage.php:643 +msgid "This will delete all usage data. Continue?" +msgstr "Això eliminarà totes les dades d'ús. Voleu continuar?" + +#: includes/Admin/ToolsPage.php:647 +msgid "Re-index media library" +msgstr "Re-indexar la biblioteca de mitjans" + +#: includes/Admin/ToolsPage.php:648 +msgid "" +"Deletes all index and usage entries, then schedules a full fresh re-index. " +"External scan results are not affected. Useful if many media files have been " +"added or removed." +msgstr "" +"Elimina totes les entrades de l'índex i d'ús, i després programa una " +"re-indexació completa. Els resultats de l'exploració externa no es veuen " +"afectats. Útil si s'han afegit o eliminat molts fitxers multimèdia." + +#: includes/Admin/ToolsPage.php:650 +msgid "This will delete all index and usage data. Continue?" +msgstr "Això eliminarà totes les dades de l'índex i d'ús. Voleu continuar?" + +#: includes/Admin/ToolsPage.php:654 +msgid "Purge external data" +msgstr "Purgar dades externes" + +#: includes/Admin/ToolsPage.php:655 +msgid "" +"Deletes all external scan results and resets every attachment's external " +"status to pending. Use this before switching API providers or to force a " +"complete re-scan." +msgstr "" +"Elimina tots els resultats de l'exploració externa i restableix l'estat " +"extern de cada fitxer adjunt a pendent. Useu-ho abans de canviar de " +"proveïdors d'API o per forçar una exploració completa." + +#: includes/Admin/ToolsPage.php:657 +msgid "This will delete all external scan results. Continue?" +msgstr "" +"Això eliminarà tots els resultats de l'exploració externa. Voleu " +"continuar?" + +#: includes/Admin/ToolsPage.php:661 +msgid "Reset all data" +msgstr "Restablir totes les dades" + +#: includes/Admin/ToolsPage.php:662 +msgid "" +"Truncates all three plugin tables (index, usage, external results) and " +"schedules a fresh indexing run. Use this to start completely from scratch." +msgstr "" +"Buida les tres taules del connector (índex, ús, resultats externs) i " +"programa una nova indexació. Useu-ho per començar completament des de zero." + +#: includes/Admin/ToolsPage.php:664 +msgid "" +"This will permanently delete ALL plugin data and cannot be undone. Are you " +"absolutely sure?" +msgstr "" +"Això eliminarà TOTES les dades del connector de forma permanent i no es pot " +"desfer. Esteu absolutament segurs?" + +#: includes/Admin/ToolsPage.php:675 +msgid "Destructive operations" +msgstr "Operacions destructives" + +#: includes/Admin/ToolsPage.php:678 +msgid "" +"These operations permanently delete plugin data. Intended for forced " +"rescans, troubleshooting, or starting fresh. A confirmation prompt will " +"appear before any action is taken." +msgstr "" +"Aquestes operacions eliminen les dades del connector de forma permanent. " +"Pensades per forçar re-exploracions, solucionar problemes o començar des de " +"zero. Apareixerà una confirmació abans d'executar qualsevol acció." + +#: includes/Admin/ToolsPage.php:726 +msgid "Index media files" +msgstr "Indexar fitxers multimèdia" + +#: includes/Admin/ToolsPage.php:727 +msgid "" +"Discovers unindexed images and registers them in the plugin index. Run this " +"first." +msgstr "" +"Descobreix imatges no indexades i les registra a l'índex del connector. " +"Executeu-ho primer." + +#: includes/Admin/ToolsPage.php:731 +msgid "Scan usage" +msgstr "Explorar l'ús" + +#: includes/Admin/ToolsPage.php:732 +msgid "" +"Checks every indexed image for usage across posts, pages, and custom fields." +msgstr "" +"Comprova l'ús de cada imatge indexada en entrades, pàgines i camps " +"personalitzats." + +#: includes/Admin/ToolsPage.php:737 +msgid "" +"Sends images to external providers for reverse-image search. Requires API " +"keys in Settings." +msgstr "" +"Envia imatges a proveïdors externs per a la cerca inversa d'imatges. " +"Requereix claus d'API a la Configuració." + +#: includes/Admin/ToolsPage.php:754 +msgid "Run now" +msgstr "Executa ara" + +#: includes/Admin/ToolsPage.php:757 +msgid "Stop" +msgstr "Atura" + #. translators: %s: DB version number #: includes/CLI/Command.php:49 #, php-format @@ -494,7 +937,11 @@ msgstr "S'estan executant les exploracions externes…" msgid "External scan complete. %d attachments processed." msgstr "Exploració externa completada. %d fitxers adjunts processats." -#: includes/Core/Plugin.php:112 includes/Core/Plugin.php:113 +#: includes/Core/Plugin.php:122 includes/Core/Plugin.php:123 +msgid "Tools" +msgstr "Eines" + +#: includes/Core/Plugin.php:135 includes/Core/Plugin.php:136 msgid "Settings" msgstr "Configuració" @@ -522,14 +969,15 @@ msgstr "Última exploració (interna)" msgid "Last Scanned (external)" msgstr "Última exploració (externa)" -#: includes/Privacy/DataExporter.php:303 -msgid "Provider" -msgstr "Proveïdor" - -#: includes/Privacy/DataExporter.php:307 -msgid "Match Count" -msgstr "Nombre de coincidències" - #: includes/Privacy/DataExporter.php:311 msgid "Scanned At" msgstr "Explorat el" + +#~ msgid "https://robotstxt.es/plugins/media-audit/" +#~ msgstr "https://robotstxt.es/plugins/media-audit/" + +#~ msgid "Javier Casares" +#~ msgstr "Javier Casares" + +#~ msgid "https://javiercasares.com" +#~ msgstr "https://javiercasares.com" diff --git a/languages/robotstxt-mediaaudit-es_ES.mo b/languages/robotstxt-mediaaudit-es_ES.mo index aea4c2bebaf3f799a8fdd8b0887d9722861d8e98..bd685f0dcfcc823fd19375d84aca8ab53d58f6a0 100644 GIT binary patch literal 18072 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\n" "Language-Team: Spanish (Spain) \n" @@ -23,8 +23,8 @@ msgstr "Auditoría de medios (by ROBOTSTXT)" #. Plugin URI of the plugin #: robotstxt-mediaaudit.php -msgid "https://robotstxt.es/plugins/media-audit/" -msgstr "https://robotstxt.es/plugins/media-audit/" +msgid "https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit" +msgstr "" #. Description of the plugin #: robotstxt-mediaaudit.php @@ -37,279 +37,311 @@ msgstr "" #. Author of the plugin #: robotstxt-mediaaudit.php -msgid "Javier Casares" -msgstr "Javier Casares" +msgid "ROBOTSTXT" +msgstr "" #. Author URI of the plugin #: robotstxt-mediaaudit.php -msgid "https://javiercasares.com" -msgstr "https://javiercasares.com" +msgid "https://www.robotstxt.es/" +msgstr "" -#: includes/Admin/AuditPage.php:112 +#: includes/Admin/AuditPage.php:114 msgid "Loading…" msgstr "Cargando…" -#: includes/Admin/AuditPage.php:113 +#: includes/Admin/AuditPage.php:115 msgid "Error loading details." msgstr "Error al cargar los detalles." -#: includes/Admin/AuditPage.php:114 includes/Admin/AuditPage.php:190 +#: includes/Admin/AuditPage.php:116 includes/Admin/AuditPage.php:192 msgid "This image is not used in any post." msgstr "Esta imagen no se usa en ninguna entrada." -#: includes/Admin/AuditPage.php:115 includes/Admin/AuditPage.php:152 +#: includes/Admin/AuditPage.php:117 includes/Admin/AuditPage.php:154 msgid "Usage Details" msgstr "Detalles de uso" -#: includes/Admin/AuditPage.php:131 includes/Admin/Settings.php:325 +#: includes/Admin/AuditPage.php:133 includes/Admin/Settings.php:325 +#: includes/Admin/ToolsPage.php:245 msgid "You do not have permission to access this page." msgstr "No tienes permiso para acceder a esta página." -#: includes/Admin/AuditPage.php:138 includes/Core/Plugin.php:89 -#: includes/Core/Plugin.php:90 includes/Core/Plugin.php:103 -#: includes/Core/Plugin.php:104 includes/Privacy/DataEraser.php:33 +#: includes/Admin/AuditPage.php:140 includes/Core/Plugin.php:99 +#: includes/Core/Plugin.php:100 includes/Core/Plugin.php:113 +#: includes/Core/Plugin.php:114 includes/Privacy/DataEraser.php:33 #: includes/Privacy/DataExporter.php:45 msgid "Media Audit" msgstr "Auditoría de medios" -#: includes/Admin/AuditPage.php:144 +#: includes/Admin/AuditPage.php:146 msgid "Search" msgstr "Buscar" -#: includes/Admin/AuditPage.php:153 +#: includes/Admin/AuditPage.php:155 msgid "Close" msgstr "Cerrar" -#: includes/Admin/AuditPage.php:171 includes/Admin/AuditPage.php:459 -#: includes/Admin/AuditPage.php:510 includes/Admin/AuditPage.php:542 +#: includes/Admin/AuditPage.php:173 includes/Admin/AuditPage.php:461 +#: includes/Admin/AuditPage.php:515 includes/Admin/AuditPage.php:648 +#: includes/Admin/AuditPage.php:680 includes/Admin/ToolsPage.php:105 +#: includes/Admin/ToolsPage.php:161 msgid "Insufficient permissions." msgstr "Permisos insuficientes." -#: includes/Admin/AuditPage.php:176 +#: includes/Admin/AuditPage.php:178 msgid "Invalid attachment ID." msgstr "ID de adjunto no válido." -#: includes/Admin/AuditPage.php:184 +#: includes/Admin/AuditPage.php:186 msgid "Featured Image" msgstr "Imagen destacada" -#: includes/Admin/AuditPage.php:185 +#: includes/Admin/AuditPage.php:187 msgid "Post Content" msgstr "Contenido de la entrada" -#: includes/Admin/AuditPage.php:186 +#: includes/Admin/AuditPage.php:188 msgid "Custom Field" msgstr "Campo personalizado" -#: includes/Admin/AuditPage.php:194 +#: includes/Admin/AuditPage.php:196 msgid "Post" msgstr "Entrada" -#: includes/Admin/AuditPage.php:195 +#: includes/Admin/AuditPage.php:197 msgid "Type" msgstr "Tipo" -#: includes/Admin/AuditPage.php:196 +#: includes/Admin/AuditPage.php:198 msgid "Context" msgstr "Contexto" -#: includes/Admin/AuditPage.php:197 +#: includes/Admin/AuditPage.php:199 msgid "Status" msgstr "Estado" -#: includes/Admin/AuditPage.php:240 +#: includes/Admin/AuditPage.php:242 msgid "Internal Usage" msgstr "Uso interno" -#: includes/Admin/AuditPage.php:288 +#: includes/Admin/AuditPage.php:290 msgid "External Scan Results" msgstr "Resultados del escaneo externo" #. translators: %d: number of external matches -#: includes/Admin/AuditPage.php:312 +#: includes/Admin/AuditPage.php:314 #, php-format msgid "%d match" msgid_plural "%d matches" msgstr[0] "%d coincidencia" msgstr[1] "%d coincidencias" -#: includes/Admin/AuditPage.php:317 +#: includes/Admin/AuditPage.php:319 msgid "No matches" msgstr "Sin coincidencias" -#: includes/Admin/AuditPage.php:328 +#: includes/Admin/AuditPage.php:330 msgid "Domain" msgstr "Dominio" -#: includes/Admin/AuditPage.php:329 +#: includes/Admin/AuditPage.php:331 msgid "Occurrences" msgstr "Apariciones" -#: includes/Admin/AuditPage.php:350 +#: includes/Admin/AuditPage.php:352 msgid "Consensus Domains" msgstr "Dominios de consenso" #. translators: %d: number of providers that agree -#: includes/Admin/AuditPage.php:356 +#: includes/Admin/AuditPage.php:358 #, php-format msgid "%d provider" msgid_plural "%d providers" msgstr[0] "%d proveedor" msgstr[1] "%d proveedores" -#: includes/Admin/AuditPage.php:388 +#: includes/Admin/AuditPage.php:390 msgid "In Index" msgstr "En el índice" #. translators: %d: count of images not yet indexed -#: includes/Admin/AuditPage.php:392 +#: includes/Admin/AuditPage.php:394 #, php-format msgid "%d not yet indexed" msgstr "%d aún no indexadas" -#: includes/Admin/AuditPage.php:395 +#: includes/Admin/AuditPage.php:397 msgid "All indexed" msgstr "Todas indexadas" -#: includes/Admin/AuditPage.php:400 includes/Admin/MediaListTable.php:275 +#: includes/Admin/AuditPage.php:402 includes/Admin/MediaListTable.php:278 msgid "Scanned" msgstr "Escaneadas" #. translators: %d: percentage -#: includes/Admin/AuditPage.php:403 +#: includes/Admin/AuditPage.php:405 #, php-format msgid "%d%% of index" msgstr "%d%% del índice" -#: includes/Admin/AuditPage.php:410 +#: includes/Admin/AuditPage.php:412 msgid "Used" msgstr "Usadas" -#: includes/Admin/AuditPage.php:416 includes/Admin/MediaListTable.php:200 +#: includes/Admin/AuditPage.php:418 includes/Admin/MediaListTable.php:203 msgid "Unused" msgstr "Sin usar" -#: includes/Admin/AuditPage.php:423 +#: includes/Admin/AuditPage.php:425 msgid "External Matches" msgstr "Coincidencias externas" -#: includes/Admin/AuditPage.php:473 +#: includes/Admin/AuditPage.php:475 includes/Admin/AuditPage.php:529 msgid "Could not open output stream." msgstr "No se pudo abrir el flujo de salida." -#: includes/Admin/AuditPage.php:482 includes/Privacy/DataExporter.php:257 -#: includes/Privacy/DataExporter.php:299 +#: includes/Admin/AuditPage.php:484 includes/Admin/AuditPage.php:537 +#: includes/Privacy/DataExporter.php:257 includes/Privacy/DataExporter.php:299 msgid "Attachment ID" msgstr "ID del adjunto" -#: includes/Admin/AuditPage.php:483 includes/Admin/MediaListTable.php:52 -#: includes/Privacy/DataExporter.php:261 +#: includes/Admin/AuditPage.php:485 includes/Admin/AuditPage.php:538 +#: includes/Admin/MediaListTable.php:52 includes/Privacy/DataExporter.php:261 msgid "Filename" msgstr "Nombre de archivo" -#: includes/Admin/AuditPage.php:484 includes/Privacy/DataExporter.php:265 +#: includes/Admin/AuditPage.php:486 includes/Admin/AuditPage.php:539 +#: includes/Privacy/DataExporter.php:265 msgid "File URL" msgstr "URL del archivo" -#: includes/Admin/AuditPage.php:485 +#: includes/Admin/AuditPage.php:487 msgid "MIME Type" msgstr "Tipo MIME" -#: includes/Admin/AuditPage.php:486 +#: includes/Admin/AuditPage.php:488 msgid "File Size (bytes)" msgstr "Tamaño del archivo (bytes)" -#: includes/Admin/AuditPage.php:487 includes/Admin/MediaListTable.php:56 +#: includes/Admin/AuditPage.php:489 includes/Admin/AuditPage.php:540 +#: includes/Admin/MediaListTable.php:56 msgid "External Status" msgstr "Estado externo" -#: includes/Admin/AuditPage.php:488 includes/Admin/MediaListTable.php:54 +#: includes/Admin/AuditPage.php:490 includes/Admin/MediaListTable.php:54 msgid "Usage Count" msgstr "Número de usos" -#: includes/Admin/AuditPage.php:489 +#: includes/Admin/AuditPage.php:491 msgid "Used In (post titles)" msgstr "Usado en (títulos de entradas)" +#: includes/Admin/AuditPage.php:541 includes/Admin/MediaListTable.php:57 +msgid "Last Scanned" +msgstr "Último escaneo" + +#: includes/Admin/AuditPage.php:542 includes/Privacy/DataExporter.php:303 +msgid "Provider" +msgstr "Proveedor" + +#: includes/Admin/AuditPage.php:543 includes/Privacy/DataExporter.php:307 +msgid "Match Count" +msgstr "Número de coincidencias" + +#: includes/Admin/AuditPage.php:544 +msgid "Top Domains" +msgstr "Dominios principales" + #: includes/Admin/MediaListTable.php:51 msgid "Thumbnail" msgstr "Miniatura" #: includes/Admin/MediaListTable.php:53 msgid "ID" -msgstr "" +msgstr "ID" #: includes/Admin/MediaListTable.php:55 msgid "Usages" msgstr "Usos" -#: includes/Admin/MediaListTable.php:79 +#: includes/Admin/MediaListTable.php:81 msgid "Run External Scan" msgstr "Ejecutar escaneo externo" -#: includes/Admin/MediaListTable.php:80 +#: includes/Admin/MediaListTable.php:82 msgid "Purge External Data" msgstr "Purgar datos externos" -#: includes/Admin/MediaListTable.php:81 +#: includes/Admin/MediaListTable.php:83 msgid "Export CSV" msgstr "Exportar CSV" -#: includes/Admin/MediaListTable.php:165 +#: includes/Admin/MediaListTable.php:84 +msgid "Export External Results CSV" +msgstr "Exportar CSV de resultados externos" + +#: includes/Admin/MediaListTable.php:168 msgid "Edit" msgstr "Editar" -#: includes/Admin/MediaListTable.php:170 +#: includes/Admin/MediaListTable.php:173 msgid "View Details" msgstr "Ver detalles" -#: includes/Admin/MediaListTable.php:236 +#: includes/Admin/MediaListTable.php:239 msgid "(no title)" msgstr "(sin título)" #. translators: %d: number of additional usages -#: includes/Admin/MediaListTable.php:251 +#: includes/Admin/MediaListTable.php:254 #, php-format msgid "+ %d more…" msgstr "+ %d más…" -#: includes/Admin/MediaListTable.php:273 +#: includes/Admin/MediaListTable.php:276 includes/Admin/ToolsPage.php:580 +#: includes/Admin/ToolsPage.php:581 includes/Admin/ToolsPage.php:582 msgid "Pending" msgstr "Pendiente" -#: includes/Admin/MediaListTable.php:274 +#: includes/Admin/MediaListTable.php:277 msgid "Queued" msgstr "En cola" -#: includes/Admin/MediaListTable.php:276 +#: includes/Admin/MediaListTable.php:279 msgid "Matches Found" msgstr "Coincidencias encontradas" -#: includes/Admin/MediaListTable.php:277 +#: includes/Admin/MediaListTable.php:280 msgid "Error" msgstr "Error" -#: includes/Admin/MediaListTable.php:305 +#: includes/Admin/MediaListTable.php:308 msgid "View Results" msgstr "Ver resultados" -#: includes/Admin/MediaListTable.php:336 +#. translators: %s: human-readable time difference, e.g. "3 days" +#: includes/Admin/MediaListTable.php:350 +#, php-format +msgid "%s ago" +msgstr "hace %s" + +#: includes/Admin/MediaListTable.php:380 msgid "All statuses" msgstr "Todos los estados" -#: includes/Admin/MediaListTable.php:350 +#: includes/Admin/MediaListTable.php:394 msgid "All post types" msgstr "Todos los tipos de entrada" -#: includes/Admin/MediaListTable.php:365 +#: includes/Admin/MediaListTable.php:409 msgid "Unused only" msgstr "Solo sin usar" -#: includes/Admin/MediaListTable.php:368 +#: includes/Admin/MediaListTable.php:412 msgid "Filter" msgstr "Filtrar" -#: includes/Admin/MediaListTable.php:378 +#: includes/Admin/MediaListTable.php:422 msgid "" "No indexed attachments found. Run wp mra scan-internal to populate the index." msgstr "" @@ -435,6 +467,412 @@ msgstr "" "Máximo de solicitudes de API por minuto por proveedor (1–60). " "Predeterminado: 10." +#: includes/Admin/ToolsPage.php:85 +msgid "Done." +msgstr "Listo." + +#: includes/Admin/ToolsPage.php:86 +msgid "Stopped." +msgstr "Detenido." + +#: includes/Admin/ToolsPage.php:87 +msgid "An error occurred." +msgstr "Se ha producido un error." + +#: includes/Admin/ToolsPage.php:139 +msgid "Invalid batch type." +msgstr "Tipo de lote no válido." + +#: includes/Admin/ToolsPage.php:256 +msgid "System Status" +msgstr "Estado del sistema" + +#: includes/Admin/ToolsPage.php:259 +msgid "Operations" +msgstr "Operaciones" + +#: includes/Admin/ToolsPage.php:262 +msgid "Direct scan runner" +msgstr "Ejecutor de escaneo directo" + +#: includes/Admin/ToolsPage.php:264 +msgid "" +"Run scan batches directly from your browser without relying on background " +"jobs. Useful when WP-Cron is disabled or Action Scheduler is not running. " +"Keep this tab open while processing." +msgstr "" +"Ejecuta lotes de escaneo directamente desde tu navegador sin depender de " +"trabajos en segundo plano. Útil cuando WP-Cron está desactivado o Action " +"Scheduler no está en ejecución. Mantén esta pestaña abierta mientras se " +"procesa." + +#: includes/Admin/ToolsPage.php:300 +msgid "not installed" +msgstr "no instalado" + +#: includes/Admin/ToolsPage.php:358 +msgid "Internal scan scheduled." +msgstr "Escaneo interno programado." + +#: includes/Admin/ToolsPage.php:359 +msgid "External scan scheduled." +msgstr "Escaneo externo programado." + +#: includes/Admin/ToolsPage.php:360 +msgid "Failed scans re-queued. Background scan scheduled." +msgstr "Escaneos fallidos reencolados. Escaneo en segundo plano programado." + +#: includes/Admin/ToolsPage.php:361 +msgid "Usage data cleared. Background scan scheduled." +msgstr "Datos de uso eliminados. Escaneo en segundo plano programado." + +#: includes/Admin/ToolsPage.php:362 +msgid "Index cleared. Fresh indexing scheduled." +msgstr "Índice eliminado. Re-indexación programada." + +#: includes/Admin/ToolsPage.php:363 +msgid "External scan data purged." +msgstr "Datos de escaneo externo eliminados." + +#: includes/Admin/ToolsPage.php:364 +msgid "All data reset. Fresh indexing scheduled." +msgstr "Todos los datos restablecidos. Re-indexación programada." + +#: includes/Admin/ToolsPage.php:367 +msgid "Operation completed." +msgstr "Operación completada." + +#: includes/Admin/ToolsPage.php:411 +msgid "Database schema" +msgstr "Esquema de base de datos" + +#. translators: %s: version number +#: includes/Admin/ToolsPage.php:417 +#, php-format +msgid "v%s – up to date" +msgstr "v%s – actualizado" + +#. translators: 1: stored version, 2: expected version +#: includes/Admin/ToolsPage.php:423 +#, php-format +msgid "Installed: %1$s — Required: %2$s" +msgstr "Instalado: %1$s — Requerido: %2$s" + +#: includes/Admin/ToolsPage.php:432 +msgid "Deactivate and reactivate the plugin to apply pending migrations." +msgstr "" +"Desactiva y reactiva el plugin para aplicar las migraciones pendientes." + +#: includes/Admin/ToolsPage.php:439 +msgid "Action Scheduler" +msgstr "Action Scheduler" + +#: includes/Admin/ToolsPage.php:442 +msgid "Available" +msgstr "Disponible" + +#: includes/Admin/ToolsPage.php:444 +msgid "Not available" +msgstr "No disponible" + +#: includes/Admin/ToolsPage.php:449 +msgid "" +"Install and activate the Action Scheduler plugin. Background scanning will " +"not work without it." +msgstr "" +"Instala y activa el plugin Action Scheduler. El escaneo en segundo plano no " +"funcionará sin él." + +#: includes/Admin/ToolsPage.php:456 +msgid "WP-Cron" +msgstr "WP-Cron" + +#: includes/Admin/ToolsPage.php:459 +msgid "Disabled (DISABLE_WP_CRON)" +msgstr "Desactivado (DISABLE_WP_CRON)" + +#: includes/Admin/ToolsPage.php:461 +msgid "Enabled" +msgstr "Activado" + +#: includes/Admin/ToolsPage.php:466 +msgid "" +"Background jobs will not fire automatically. Trigger the cron externally or " +"use WP-CLI:" +msgstr "" +"Los trabajos en segundo plano no se ejecutarán automáticamente. Activa el " +"cron externamente o usa WP-CLI:" + +#: includes/Admin/ToolsPage.php:474 +msgid "Internal index" +msgstr "Índice interno" + +#. translators: 1: indexed count, 2: total images +#: includes/Admin/ToolsPage.php:479 +#, php-format +msgid "%1$s / %2$s images indexed" +msgstr "%1$s / %2$s imágenes indexadas" + +#. translators: %s: formatted count +#: includes/Admin/ToolsPage.php:490 +#, php-format +msgid "%s image not yet indexed." +msgid_plural "%s images not yet indexed." +msgstr[0] "%s imagen aún no indexada." +msgstr[1] "%s imágenes aún no indexadas." + +#: includes/Admin/ToolsPage.php:494 +msgid "Schedule an internal scan below." +msgstr "Programa un escaneo interno más abajo." + +#: includes/Admin/ToolsPage.php:502 +msgid "Usage scan" +msgstr "Escaneo de uso" + +#. translators: 1: scanned count, 2: total indexed +#: includes/Admin/ToolsPage.php:507 +#, php-format +msgid "%1$s / %2$s scanned for usage" +msgstr "%1$s / %2$s escaneadas de uso" + +#. translators: %s: formatted count +#: includes/Admin/ToolsPage.php:518 +#, php-format +msgid "%s pending." +msgid_plural "%s pending." +msgstr[0] "%s pendiente." +msgstr[1] "%s pendientes." + +#: includes/Admin/ToolsPage.php:539 includes/Admin/ToolsPage.php:736 +msgid "External scan" +msgstr "Escaneo externo" + +#. translators: 1: scanned 2: matches 3: queued 4: pending 5: errors +#: includes/Admin/ToolsPage.php:544 +#, php-format +msgid "%1$s scanned · %2$s matches · %3$s queued · %4$s pending · %5$s errors" +msgstr "" +"%1$s escaneadas · %2$s coincidencias · %3$s en cola · %4$s pendientes · %5$s " +"errores" + +#: includes/Admin/ToolsPage.php:555 +msgid "" +"Some scans failed. Check that API keys are configured correctly in Settings." +msgstr "" +"Algunos escaneos fallaron. Verifica que las claves de API están configuradas " +"correctamente en Ajustes." + +#: includes/Admin/ToolsPage.php:557 +msgid "Schedule an external scan below to process pending items." +msgstr "" +"Programa un escaneo externo más abajo para procesar los elementos pendientes." + +#: includes/Admin/ToolsPage.php:575 +msgid "Scheduled jobs" +msgstr "Trabajos programados" + +#: includes/Admin/ToolsPage.php:578 +msgid "Action Scheduler not available." +msgstr "Action Scheduler no disponible." + +#: includes/Admin/ToolsPage.php:580 +msgid "Index:" +msgstr "Índice:" + +#: includes/Admin/ToolsPage.php:581 +msgid "Usage:" +msgstr "Uso:" + +#: includes/Admin/ToolsPage.php:582 +msgid "External:" +msgstr "Externo:" + +#: includes/Admin/ToolsPage.php:587 +msgid "" +"There is work to do but no background job is scheduled. Use the operations " +"below." +msgstr "" +"Hay trabajo pendiente pero no hay ningún trabajo en segundo plano " +"programado. Usa las operaciones de abajo." + +#: includes/Admin/ToolsPage.php:589 +msgid "WP-Cron is disabled. Jobs will not fire automatically." +msgstr "" +"WP-Cron está desactivado. Los trabajos no se ejecutarán automáticamente." + +#: includes/Admin/ToolsPage.php:599 +msgid "WP-CLI commands to process scans manually:" +msgstr "Comandos WP-CLI para procesar escaneos manualmente:" + +#: includes/Admin/ToolsPage.php:616 +msgid "Schedule internal scan" +msgstr "Programar escaneo interno" + +#: includes/Admin/ToolsPage.php:617 +msgid "" +"Queue a background job to index all unindexed media files and scan their " +"usage across posts, pages, and custom fields. Safe to run at any time — will " +"not re-process completed work." +msgstr "" +"Cola un trabajo en segundo plano para indexar todos los archivos multimedia " +"no indexados y escanear su uso en entradas, páginas y campos personalizados. " +"Es seguro ejecutarlo en cualquier momento: no reprocesará el trabajo ya " +"completado." + +#: includes/Admin/ToolsPage.php:623 +msgid "Schedule external scan" +msgstr "Programar escaneo externo" + +#: includes/Admin/ToolsPage.php:624 +msgid "" +"Mark all pending attachments as queued and launch a background reverse-image-" +"search via the configured providers. Requires API keys in Settings." +msgstr "" +"Marca todos los adjuntos pendientes como en cola y lanza una búsqueda " +"inversa de imágenes en segundo plano a través de los proveedores " +"configurados. Requiere claves de API en Ajustes." + +#: includes/Admin/ToolsPage.php:630 +msgid "Requeue scan errors" +msgstr "Reencolar errores de escaneo" + +#: includes/Admin/ToolsPage.php:631 +msgid "" +"Re-queues only the attachments that failed with an error on the last " +"external scan run. Does not affect already-scanned items. Useful to retry " +"after fixing API key issues." +msgstr "" +"Reencola solo los adjuntos que fallaron con un error en la última ejecución " +"del escaneo externo. No afecta a los elementos ya escaneados. Útil para " +"reintentar después de corregir problemas con las claves de API." + +#: includes/Admin/ToolsPage.php:640 +msgid "Reset usage data" +msgstr "Restablecer datos de uso" + +#: includes/Admin/ToolsPage.php:641 +msgid "" +"Clears all usage rows and resets the usage-scan flag for every indexed " +"attachment. File metadata in the index is preserved. A background usage scan " +"is scheduled immediately after." +msgstr "" +"Elimina todas las filas de uso y restablece el indicador de escaneo de uso " +"para cada adjunto indexado. Los metadatos del archivo en el índice se " +"conservan. Se programa un escaneo en segundo plano inmediatamente después." + +#: includes/Admin/ToolsPage.php:643 +msgid "This will delete all usage data. Continue?" +msgstr "Esto eliminará todos los datos de uso. ¿Continuar?" + +#: includes/Admin/ToolsPage.php:647 +msgid "Re-index media library" +msgstr "Re-indexar la biblioteca de medios" + +#: includes/Admin/ToolsPage.php:648 +msgid "" +"Deletes all index and usage entries, then schedules a full fresh re-index. " +"External scan results are not affected. Useful if many media files have been " +"added or removed." +msgstr "" +"Elimina todas las entradas del índice y de uso, luego programa una re-" +"indexación completa. Los resultados del escaneo externo no se ven afectados. " +"Útil si se han añadido o eliminado muchos archivos multimedia." + +#: includes/Admin/ToolsPage.php:650 +msgid "This will delete all index and usage data. Continue?" +msgstr "Esto eliminará todos los datos del índice y de uso. ¿Continuar?" + +#: includes/Admin/ToolsPage.php:654 +msgid "Purge external data" +msgstr "Purgar datos externos" + +#: includes/Admin/ToolsPage.php:655 +msgid "" +"Deletes all external scan results and resets every attachment's external " +"status to pending. Use this before switching API providers or to force a " +"complete re-scan." +msgstr "" +"Elimina todos los resultados del escaneo externo y restablece el estado " +"externo de cada adjunto a pendiente. Úsalo antes de cambiar de proveedores " +"de API o para forzar un escaneo completo." + +#: includes/Admin/ToolsPage.php:657 +msgid "This will delete all external scan results. Continue?" +msgstr "Esto eliminará todos los resultados del escaneo externo. ¿Continuar?" + +#: includes/Admin/ToolsPage.php:661 +msgid "Reset all data" +msgstr "Restablecer todos los datos" + +#: includes/Admin/ToolsPage.php:662 +msgid "" +"Truncates all three plugin tables (index, usage, external results) and " +"schedules a fresh indexing run. Use this to start completely from scratch." +msgstr "" +"Vacía las tres tablas del plugin (índice, uso, resultados externos) y " +"programa una nueva indexación. Úsalo para empezar completamente desde cero." + +#: includes/Admin/ToolsPage.php:664 +msgid "" +"This will permanently delete ALL plugin data and cannot be undone. Are you " +"absolutely sure?" +msgstr "" +"Esto eliminará TODOS los datos del plugin de forma permanente y no se puede " +"deshacer. ¿Estás absolutamente seguro?" + +#: includes/Admin/ToolsPage.php:675 +msgid "Destructive operations" +msgstr "Operaciones destructivas" + +#: includes/Admin/ToolsPage.php:678 +msgid "" +"These operations permanently delete plugin data. Intended for forced " +"rescans, troubleshooting, or starting fresh. A confirmation prompt will " +"appear before any action is taken." +msgstr "" +"Estas operaciones eliminan datos del plugin de forma permanente. Pensadas " +"para forzar re-escaneos, solucionar problemas o empezar desde cero. " +"Aparecerá una confirmación antes de ejecutar cualquier acción." + +#: includes/Admin/ToolsPage.php:726 +msgid "Index media files" +msgstr "Indexar archivos multimedia" + +#: includes/Admin/ToolsPage.php:727 +msgid "" +"Discovers unindexed images and registers them in the plugin index. Run this " +"first." +msgstr "" +"Descubre imágenes no indexadas y las registra en el índice del plugin. " +"Ejecútalo primero." + +#: includes/Admin/ToolsPage.php:731 +msgid "Scan usage" +msgstr "Escanear uso" + +#: includes/Admin/ToolsPage.php:732 +msgid "" +"Checks every indexed image for usage across posts, pages, and custom fields." +msgstr "" +"Comprueba el uso de cada imagen indexada en entradas, páginas y campos " +"personalizados." + +#: includes/Admin/ToolsPage.php:737 +msgid "" +"Sends images to external providers for reverse-image search. Requires API " +"keys in Settings." +msgstr "" +"Envía imágenes a proveedores externos para búsqueda inversa de imágenes. " +"Requiere claves de API en Ajustes." + +#: includes/Admin/ToolsPage.php:754 +msgid "Run now" +msgstr "Ejecutar ahora" + +#: includes/Admin/ToolsPage.php:757 +msgid "Stop" +msgstr "Detener" + #. translators: %s: DB version number #: includes/CLI/Command.php:49 #, php-format @@ -495,7 +933,11 @@ msgstr "Ejecutando escaneos externos…" msgid "External scan complete. %d attachments processed." msgstr "Escaneo externo completo. %d adjuntos procesados." -#: includes/Core/Plugin.php:112 includes/Core/Plugin.php:113 +#: includes/Core/Plugin.php:122 includes/Core/Plugin.php:123 +msgid "Tools" +msgstr "Herramientas" + +#: includes/Core/Plugin.php:135 includes/Core/Plugin.php:136 msgid "Settings" msgstr "Ajustes" @@ -523,14 +965,15 @@ msgstr "Último escaneo (interno)" msgid "Last Scanned (external)" msgstr "Último escaneo (externo)" -#: includes/Privacy/DataExporter.php:303 -msgid "Provider" -msgstr "Proveedor" - -#: includes/Privacy/DataExporter.php:307 -msgid "Match Count" -msgstr "Número de coincidencias" - #: includes/Privacy/DataExporter.php:311 msgid "Scanned At" msgstr "Escaneado el" + +#~ msgid "https://robotstxt.es/plugins/media-audit/" +#~ msgstr "https://robotstxt.es/plugins/media-audit/" + +#~ msgid "Javier Casares" +#~ msgstr "Javier Casares" + +#~ msgid "https://javiercasares.com" +#~ msgstr "https://javiercasares.com" diff --git a/languages/robotstxt-mediaaudit.pot b/languages/robotstxt-mediaaudit.pot index 3109190..5510e77 100644 --- a/languages/robotstxt-mediaaudit.pot +++ b/languages/robotstxt-mediaaudit.pot @@ -1,15 +1,15 @@ -# Copyright (C) 2026 Javier Casares +# Copyright (C) 2026 ROBOTSTXT # This file is distributed under the GPL-3.0-or-later. msgid "" msgstr "" -"Project-Id-Version: Media Audit (by ROBOTSTXT) 1.0.0\n" +"Project-Id-Version: Media Audit (by ROBOTSTXT) 1.1.0\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/robotstxt-mediaaudit\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2026-05-02T06:06:40+00:00\n" +"POT-Creation-Date: 2026-05-02T07:36:54+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: robotstxt-mediaaudit\n" @@ -21,7 +21,7 @@ msgstr "" #. Plugin URI of the plugin #: robotstxt-mediaaudit.php -msgid "https://robotstxt.es/plugins/media-audit/" +msgid "https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit" msgstr "" #. Description of the plugin @@ -31,215 +31,243 @@ msgstr "" #. Author of the plugin #: robotstxt-mediaaudit.php -msgid "Javier Casares" +msgid "ROBOTSTXT" msgstr "" #. Author URI of the plugin #: robotstxt-mediaaudit.php -msgid "https://javiercasares.com" -msgstr "" - -#: includes/Admin/AuditPage.php:112 -msgid "Loading…" -msgstr "" - -#: includes/Admin/AuditPage.php:113 -msgid "Error loading details." +msgid "https://www.robotstxt.es/" msgstr "" #: includes/Admin/AuditPage.php:114 -#: includes/Admin/AuditPage.php:190 -msgid "This image is not used in any post." +msgid "Loading…" msgstr "" #: includes/Admin/AuditPage.php:115 -#: includes/Admin/AuditPage.php:152 +msgid "Error loading details." +msgstr "" + +#: includes/Admin/AuditPage.php:116 +#: includes/Admin/AuditPage.php:192 +msgid "This image is not used in any post." +msgstr "" + +#: includes/Admin/AuditPage.php:117 +#: includes/Admin/AuditPage.php:154 msgid "Usage Details" msgstr "" -#: includes/Admin/AuditPage.php:131 +#: includes/Admin/AuditPage.php:133 #: includes/Admin/Settings.php:325 +#: includes/Admin/ToolsPage.php:245 msgid "You do not have permission to access this page." msgstr "" -#: includes/Admin/AuditPage.php:138 -#: includes/Core/Plugin.php:89 -#: includes/Core/Plugin.php:90 -#: includes/Core/Plugin.php:103 -#: includes/Core/Plugin.php:104 +#: includes/Admin/AuditPage.php:140 +#: includes/Core/Plugin.php:99 +#: includes/Core/Plugin.php:100 +#: includes/Core/Plugin.php:113 +#: includes/Core/Plugin.php:114 #: includes/Privacy/DataEraser.php:33 #: includes/Privacy/DataExporter.php:45 msgid "Media Audit" msgstr "" -#: includes/Admin/AuditPage.php:144 +#: includes/Admin/AuditPage.php:146 msgid "Search" msgstr "" -#: includes/Admin/AuditPage.php:153 +#: includes/Admin/AuditPage.php:155 msgid "Close" msgstr "" -#: includes/Admin/AuditPage.php:171 -#: includes/Admin/AuditPage.php:459 -#: includes/Admin/AuditPage.php:510 -#: includes/Admin/AuditPage.php:542 +#: includes/Admin/AuditPage.php:173 +#: includes/Admin/AuditPage.php:461 +#: includes/Admin/AuditPage.php:515 +#: includes/Admin/AuditPage.php:648 +#: includes/Admin/AuditPage.php:680 +#: includes/Admin/ToolsPage.php:105 +#: includes/Admin/ToolsPage.php:161 msgid "Insufficient permissions." msgstr "" -#: includes/Admin/AuditPage.php:176 +#: includes/Admin/AuditPage.php:178 msgid "Invalid attachment ID." msgstr "" -#: includes/Admin/AuditPage.php:184 +#: includes/Admin/AuditPage.php:186 msgid "Featured Image" msgstr "" -#: includes/Admin/AuditPage.php:185 +#: includes/Admin/AuditPage.php:187 msgid "Post Content" msgstr "" -#: includes/Admin/AuditPage.php:186 +#: includes/Admin/AuditPage.php:188 msgid "Custom Field" msgstr "" -#: includes/Admin/AuditPage.php:194 +#: includes/Admin/AuditPage.php:196 msgid "Post" msgstr "" -#: includes/Admin/AuditPage.php:195 +#: includes/Admin/AuditPage.php:197 msgid "Type" msgstr "" -#: includes/Admin/AuditPage.php:196 +#: includes/Admin/AuditPage.php:198 msgid "Context" msgstr "" -#: includes/Admin/AuditPage.php:197 +#: includes/Admin/AuditPage.php:199 msgid "Status" msgstr "" -#: includes/Admin/AuditPage.php:240 +#: includes/Admin/AuditPage.php:242 msgid "Internal Usage" msgstr "" -#: includes/Admin/AuditPage.php:288 +#: includes/Admin/AuditPage.php:290 msgid "External Scan Results" msgstr "" #. translators: %d: number of external matches -#: includes/Admin/AuditPage.php:312 +#: includes/Admin/AuditPage.php:314 #, php-format msgid "%d match" msgid_plural "%d matches" msgstr[0] "" msgstr[1] "" -#: includes/Admin/AuditPage.php:317 +#: includes/Admin/AuditPage.php:319 msgid "No matches" msgstr "" -#: includes/Admin/AuditPage.php:328 +#: includes/Admin/AuditPage.php:330 msgid "Domain" msgstr "" -#: includes/Admin/AuditPage.php:329 +#: includes/Admin/AuditPage.php:331 msgid "Occurrences" msgstr "" -#: includes/Admin/AuditPage.php:350 +#: includes/Admin/AuditPage.php:352 msgid "Consensus Domains" msgstr "" #. translators: %d: number of providers that agree -#: includes/Admin/AuditPage.php:356 +#: includes/Admin/AuditPage.php:358 #, php-format msgid "%d provider" msgid_plural "%d providers" msgstr[0] "" msgstr[1] "" -#: includes/Admin/AuditPage.php:388 +#: includes/Admin/AuditPage.php:390 msgid "In Index" msgstr "" #. translators: %d: count of images not yet indexed -#: includes/Admin/AuditPage.php:392 +#: includes/Admin/AuditPage.php:394 #, php-format msgid "%d not yet indexed" msgstr "" -#: includes/Admin/AuditPage.php:395 +#: includes/Admin/AuditPage.php:397 msgid "All indexed" msgstr "" -#: includes/Admin/AuditPage.php:400 -#: includes/Admin/MediaListTable.php:275 +#: includes/Admin/AuditPage.php:402 +#: includes/Admin/MediaListTable.php:278 msgid "Scanned" msgstr "" #. translators: %d: percentage -#: includes/Admin/AuditPage.php:403 +#: includes/Admin/AuditPage.php:405 #, php-format msgid "%d%% of index" msgstr "" -#: includes/Admin/AuditPage.php:410 +#: includes/Admin/AuditPage.php:412 msgid "Used" msgstr "" -#: includes/Admin/AuditPage.php:416 -#: includes/Admin/MediaListTable.php:200 +#: includes/Admin/AuditPage.php:418 +#: includes/Admin/MediaListTable.php:203 msgid "Unused" msgstr "" -#: includes/Admin/AuditPage.php:423 +#: includes/Admin/AuditPage.php:425 msgid "External Matches" msgstr "" -#: includes/Admin/AuditPage.php:473 +#: includes/Admin/AuditPage.php:475 +#: includes/Admin/AuditPage.php:529 msgid "Could not open output stream." msgstr "" -#: includes/Admin/AuditPage.php:482 +#: includes/Admin/AuditPage.php:484 +#: includes/Admin/AuditPage.php:537 #: includes/Privacy/DataExporter.php:257 #: includes/Privacy/DataExporter.php:299 msgid "Attachment ID" msgstr "" -#: includes/Admin/AuditPage.php:483 +#: includes/Admin/AuditPage.php:485 +#: includes/Admin/AuditPage.php:538 #: includes/Admin/MediaListTable.php:52 #: includes/Privacy/DataExporter.php:261 msgid "Filename" msgstr "" -#: includes/Admin/AuditPage.php:484 +#: includes/Admin/AuditPage.php:486 +#: includes/Admin/AuditPage.php:539 #: includes/Privacy/DataExporter.php:265 msgid "File URL" msgstr "" -#: includes/Admin/AuditPage.php:485 +#: includes/Admin/AuditPage.php:487 msgid "MIME Type" msgstr "" -#: includes/Admin/AuditPage.php:486 +#: includes/Admin/AuditPage.php:488 msgid "File Size (bytes)" msgstr "" -#: includes/Admin/AuditPage.php:487 +#: includes/Admin/AuditPage.php:489 +#: includes/Admin/AuditPage.php:540 #: includes/Admin/MediaListTable.php:56 msgid "External Status" msgstr "" -#: includes/Admin/AuditPage.php:488 +#: includes/Admin/AuditPage.php:490 #: includes/Admin/MediaListTable.php:54 msgid "Usage Count" msgstr "" -#: includes/Admin/AuditPage.php:489 +#: includes/Admin/AuditPage.php:491 msgid "Used In (post titles)" msgstr "" +#: includes/Admin/AuditPage.php:541 +#: includes/Admin/MediaListTable.php:57 +msgid "Last Scanned" +msgstr "" + +#: includes/Admin/AuditPage.php:542 +#: includes/Privacy/DataExporter.php:303 +msgid "Provider" +msgstr "" + +#: includes/Admin/AuditPage.php:543 +#: includes/Privacy/DataExporter.php:307 +msgid "Match Count" +msgstr "" + +#: includes/Admin/AuditPage.php:544 +msgid "Top Domains" +msgstr "" + #: includes/Admin/MediaListTable.php:51 msgid "Thumbnail" msgstr "" @@ -252,73 +280,86 @@ msgstr "" msgid "Usages" msgstr "" -#: includes/Admin/MediaListTable.php:79 +#: includes/Admin/MediaListTable.php:81 msgid "Run External Scan" msgstr "" -#: includes/Admin/MediaListTable.php:80 +#: includes/Admin/MediaListTable.php:82 msgid "Purge External Data" msgstr "" -#: includes/Admin/MediaListTable.php:81 +#: includes/Admin/MediaListTable.php:83 msgid "Export CSV" msgstr "" -#: includes/Admin/MediaListTable.php:165 +#: includes/Admin/MediaListTable.php:84 +msgid "Export External Results CSV" +msgstr "" + +#: includes/Admin/MediaListTable.php:168 msgid "Edit" msgstr "" -#: includes/Admin/MediaListTable.php:170 +#: includes/Admin/MediaListTable.php:173 msgid "View Details" msgstr "" -#: includes/Admin/MediaListTable.php:236 +#: includes/Admin/MediaListTable.php:239 msgid "(no title)" msgstr "" #. translators: %d: number of additional usages -#: includes/Admin/MediaListTable.php:251 +#: includes/Admin/MediaListTable.php:254 #, php-format msgid "+ %d more…" msgstr "" -#: includes/Admin/MediaListTable.php:273 +#: includes/Admin/MediaListTable.php:276 +#: includes/Admin/ToolsPage.php:580 +#: includes/Admin/ToolsPage.php:581 +#: includes/Admin/ToolsPage.php:582 msgid "Pending" msgstr "" -#: includes/Admin/MediaListTable.php:274 +#: includes/Admin/MediaListTable.php:277 msgid "Queued" msgstr "" -#: includes/Admin/MediaListTable.php:276 +#: includes/Admin/MediaListTable.php:279 msgid "Matches Found" msgstr "" -#: includes/Admin/MediaListTable.php:277 +#: includes/Admin/MediaListTable.php:280 msgid "Error" msgstr "" -#: includes/Admin/MediaListTable.php:305 +#: includes/Admin/MediaListTable.php:308 msgid "View Results" msgstr "" -#: includes/Admin/MediaListTable.php:336 +#. translators: %s: human-readable time difference, e.g. "3 days" +#: includes/Admin/MediaListTable.php:350 +#, php-format +msgid "%s ago" +msgstr "" + +#: includes/Admin/MediaListTable.php:380 msgid "All statuses" msgstr "" -#: includes/Admin/MediaListTable.php:350 +#: includes/Admin/MediaListTable.php:394 msgid "All post types" msgstr "" -#: includes/Admin/MediaListTable.php:365 +#: includes/Admin/MediaListTable.php:409 msgid "Unused only" msgstr "" -#: includes/Admin/MediaListTable.php:368 +#: includes/Admin/MediaListTable.php:412 msgid "Filter" msgstr "" -#: includes/Admin/MediaListTable.php:378 +#: includes/Admin/MediaListTable.php:422 msgid "No indexed attachments found. Run wp mra scan-internal to populate the index." msgstr "" @@ -423,6 +464,325 @@ msgstr "" msgid "Maximum API requests per minute per provider (1–60). Default: 10." msgstr "" +#: includes/Admin/ToolsPage.php:85 +msgid "Done." +msgstr "" + +#: includes/Admin/ToolsPage.php:86 +msgid "Stopped." +msgstr "" + +#: includes/Admin/ToolsPage.php:87 +msgid "An error occurred." +msgstr "" + +#: includes/Admin/ToolsPage.php:139 +msgid "Invalid batch type." +msgstr "" + +#: includes/Admin/ToolsPage.php:256 +msgid "System Status" +msgstr "" + +#: includes/Admin/ToolsPage.php:259 +msgid "Operations" +msgstr "" + +#: includes/Admin/ToolsPage.php:262 +msgid "Direct scan runner" +msgstr "" + +#: includes/Admin/ToolsPage.php:264 +msgid "Run scan batches directly from your browser without relying on background jobs. Useful when WP-Cron is disabled or Action Scheduler is not running. Keep this tab open while processing." +msgstr "" + +#: includes/Admin/ToolsPage.php:300 +msgid "not installed" +msgstr "" + +#: includes/Admin/ToolsPage.php:358 +msgid "Internal scan scheduled." +msgstr "" + +#: includes/Admin/ToolsPage.php:359 +msgid "External scan scheduled." +msgstr "" + +#: includes/Admin/ToolsPage.php:360 +msgid "Failed scans re-queued. Background scan scheduled." +msgstr "" + +#: includes/Admin/ToolsPage.php:361 +msgid "Usage data cleared. Background scan scheduled." +msgstr "" + +#: includes/Admin/ToolsPage.php:362 +msgid "Index cleared. Fresh indexing scheduled." +msgstr "" + +#: includes/Admin/ToolsPage.php:363 +msgid "External scan data purged." +msgstr "" + +#: includes/Admin/ToolsPage.php:364 +msgid "All data reset. Fresh indexing scheduled." +msgstr "" + +#: includes/Admin/ToolsPage.php:367 +msgid "Operation completed." +msgstr "" + +#: includes/Admin/ToolsPage.php:411 +msgid "Database schema" +msgstr "" + +#. translators: %s: version number +#: includes/Admin/ToolsPage.php:417 +#, php-format +msgid "v%s – up to date" +msgstr "" + +#. translators: 1: stored version, 2: expected version +#: includes/Admin/ToolsPage.php:423 +#, php-format +msgid "Installed: %1$s — Required: %2$s" +msgstr "" + +#: includes/Admin/ToolsPage.php:432 +msgid "Deactivate and reactivate the plugin to apply pending migrations." +msgstr "" + +#: includes/Admin/ToolsPage.php:439 +msgid "Action Scheduler" +msgstr "" + +#: includes/Admin/ToolsPage.php:442 +msgid "Available" +msgstr "" + +#: includes/Admin/ToolsPage.php:444 +msgid "Not available" +msgstr "" + +#: includes/Admin/ToolsPage.php:449 +msgid "Install and activate the Action Scheduler plugin. Background scanning will not work without it." +msgstr "" + +#: includes/Admin/ToolsPage.php:456 +msgid "WP-Cron" +msgstr "" + +#: includes/Admin/ToolsPage.php:459 +msgid "Disabled (DISABLE_WP_CRON)" +msgstr "" + +#: includes/Admin/ToolsPage.php:461 +msgid "Enabled" +msgstr "" + +#: includes/Admin/ToolsPage.php:466 +msgid "Background jobs will not fire automatically. Trigger the cron externally or use WP-CLI:" +msgstr "" + +#: includes/Admin/ToolsPage.php:474 +msgid "Internal index" +msgstr "" + +#. translators: 1: indexed count, 2: total images +#: includes/Admin/ToolsPage.php:479 +#, php-format +msgid "%1$s / %2$s images indexed" +msgstr "" + +#. translators: %s: formatted count +#: includes/Admin/ToolsPage.php:490 +#, php-format +msgid "%s image not yet indexed." +msgid_plural "%s images not yet indexed." +msgstr[0] "" +msgstr[1] "" + +#: includes/Admin/ToolsPage.php:494 +msgid "Schedule an internal scan below." +msgstr "" + +#: includes/Admin/ToolsPage.php:502 +msgid "Usage scan" +msgstr "" + +#. translators: 1: scanned count, 2: total indexed +#: includes/Admin/ToolsPage.php:507 +#, php-format +msgid "%1$s / %2$s scanned for usage" +msgstr "" + +#. translators: %s: formatted count +#: includes/Admin/ToolsPage.php:518 +#, php-format +msgid "%s pending." +msgid_plural "%s pending." +msgstr[0] "" +msgstr[1] "" + +#: includes/Admin/ToolsPage.php:539 +#: includes/Admin/ToolsPage.php:736 +msgid "External scan" +msgstr "" + +#. translators: 1: scanned 2: matches 3: queued 4: pending 5: errors +#: includes/Admin/ToolsPage.php:544 +#, php-format +msgid "%1$s scanned · %2$s matches · %3$s queued · %4$s pending · %5$s errors" +msgstr "" + +#: includes/Admin/ToolsPage.php:555 +msgid "Some scans failed. Check that API keys are configured correctly in Settings." +msgstr "" + +#: includes/Admin/ToolsPage.php:557 +msgid "Schedule an external scan below to process pending items." +msgstr "" + +#: includes/Admin/ToolsPage.php:575 +msgid "Scheduled jobs" +msgstr "" + +#: includes/Admin/ToolsPage.php:578 +msgid "Action Scheduler not available." +msgstr "" + +#: includes/Admin/ToolsPage.php:580 +msgid "Index:" +msgstr "" + +#: includes/Admin/ToolsPage.php:581 +msgid "Usage:" +msgstr "" + +#: includes/Admin/ToolsPage.php:582 +msgid "External:" +msgstr "" + +#: includes/Admin/ToolsPage.php:587 +msgid "There is work to do but no background job is scheduled. Use the operations below." +msgstr "" + +#: includes/Admin/ToolsPage.php:589 +msgid "WP-Cron is disabled. Jobs will not fire automatically." +msgstr "" + +#: includes/Admin/ToolsPage.php:599 +msgid "WP-CLI commands to process scans manually:" +msgstr "" + +#: includes/Admin/ToolsPage.php:616 +msgid "Schedule internal scan" +msgstr "" + +#: includes/Admin/ToolsPage.php:617 +msgid "Queue a background job to index all unindexed media files and scan their usage across posts, pages, and custom fields. Safe to run at any time — will not re-process completed work." +msgstr "" + +#: includes/Admin/ToolsPage.php:623 +msgid "Schedule external scan" +msgstr "" + +#: includes/Admin/ToolsPage.php:624 +msgid "Mark all pending attachments as queued and launch a background reverse-image-search via the configured providers. Requires API keys in Settings." +msgstr "" + +#: includes/Admin/ToolsPage.php:630 +msgid "Requeue scan errors" +msgstr "" + +#: includes/Admin/ToolsPage.php:631 +msgid "Re-queues only the attachments that failed with an error on the last external scan run. Does not affect already-scanned items. Useful to retry after fixing API key issues." +msgstr "" + +#: includes/Admin/ToolsPage.php:640 +msgid "Reset usage data" +msgstr "" + +#: includes/Admin/ToolsPage.php:641 +msgid "Clears all usage rows and resets the usage-scan flag for every indexed attachment. File metadata in the index is preserved. A background usage scan is scheduled immediately after." +msgstr "" + +#: includes/Admin/ToolsPage.php:643 +msgid "This will delete all usage data. Continue?" +msgstr "" + +#: includes/Admin/ToolsPage.php:647 +msgid "Re-index media library" +msgstr "" + +#: includes/Admin/ToolsPage.php:648 +msgid "Deletes all index and usage entries, then schedules a full fresh re-index. External scan results are not affected. Useful if many media files have been added or removed." +msgstr "" + +#: includes/Admin/ToolsPage.php:650 +msgid "This will delete all index and usage data. Continue?" +msgstr "" + +#: includes/Admin/ToolsPage.php:654 +msgid "Purge external data" +msgstr "" + +#: includes/Admin/ToolsPage.php:655 +msgid "Deletes all external scan results and resets every attachment's external status to pending. Use this before switching API providers or to force a complete re-scan." +msgstr "" + +#: includes/Admin/ToolsPage.php:657 +msgid "This will delete all external scan results. Continue?" +msgstr "" + +#: includes/Admin/ToolsPage.php:661 +msgid "Reset all data" +msgstr "" + +#: includes/Admin/ToolsPage.php:662 +msgid "Truncates all three plugin tables (index, usage, external results) and schedules a fresh indexing run. Use this to start completely from scratch." +msgstr "" + +#: includes/Admin/ToolsPage.php:664 +msgid "This will permanently delete ALL plugin data and cannot be undone. Are you absolutely sure?" +msgstr "" + +#: includes/Admin/ToolsPage.php:675 +msgid "Destructive operations" +msgstr "" + +#: includes/Admin/ToolsPage.php:678 +msgid "These operations permanently delete plugin data. Intended for forced rescans, troubleshooting, or starting fresh. A confirmation prompt will appear before any action is taken." +msgstr "" + +#: includes/Admin/ToolsPage.php:726 +msgid "Index media files" +msgstr "" + +#: includes/Admin/ToolsPage.php:727 +msgid "Discovers unindexed images and registers them in the plugin index. Run this first." +msgstr "" + +#: includes/Admin/ToolsPage.php:731 +msgid "Scan usage" +msgstr "" + +#: includes/Admin/ToolsPage.php:732 +msgid "Checks every indexed image for usage across posts, pages, and custom fields." +msgstr "" + +#: includes/Admin/ToolsPage.php:737 +msgid "Sends images to external providers for reverse-image search. Requires API keys in Settings." +msgstr "" + +#: includes/Admin/ToolsPage.php:754 +msgid "Run now" +msgstr "" + +#: includes/Admin/ToolsPage.php:757 +msgid "Stop" +msgstr "" + #. translators: %s: DB version number #: includes/CLI/Command.php:49 #, php-format @@ -484,8 +844,13 @@ msgstr "" msgid "External scan complete. %d attachments processed." msgstr "" -#: includes/Core/Plugin.php:112 -#: includes/Core/Plugin.php:113 +#: includes/Core/Plugin.php:122 +#: includes/Core/Plugin.php:123 +msgid "Tools" +msgstr "" + +#: includes/Core/Plugin.php:135 +#: includes/Core/Plugin.php:136 msgid "Settings" msgstr "" @@ -513,14 +878,6 @@ msgstr "" msgid "Last Scanned (external)" msgstr "" -#: includes/Privacy/DataExporter.php:303 -msgid "Provider" -msgstr "" - -#: includes/Privacy/DataExporter.php:307 -msgid "Match Count" -msgstr "" - #: includes/Privacy/DataExporter.php:311 msgid "Scanned At" msgstr "" diff --git a/messages.mo b/messages.mo deleted file mode 100644 index 3c3d11f47365ee9504e67cdaeecc03d7d6568578..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7894 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 diff --git a/readme.txt b/readme.txt index 646b45e..b9c4a00 100644 --- a/readme.txt +++ b/readme.txt @@ -1,11 +1,11 @@ === Media Audit (by ROBOTSTXT) === -Contributors: javiercasares +Contributors: javiercasares, robotstxt Tags: media, copyright, images, reverse image search, media library Requires at least: 6.8 Tested up to: 7.0 Requires PHP: 8.2 Requires Plugins: action-scheduler -Stable tag: 1.0.0 +Stable tag: 1.2.0 License: GPL-3.0-or-later License URI: https://www.gnu.org/licenses/gpl-3.0.txt @@ -98,6 +98,17 @@ By default, no. Enable **Settings → Delete data on uninstall** if you want all Only the 3 latest versions. The full changelog is in [changelog.txt](changelog.txt). += 1.2.0 = + +* Added browser-based AJAX scan runner in the Tools page (runs without WP-Cron or Action Scheduler). +* Added "Requeue scan errors" operation to retry failed external scans after fixing API key issues. +* Added "Last Scanned" column in the audit list, sortable with NULLs-last ordering. +* Added bulk action to export external scan results as a CSV file. + += 1.1.0 = + +* Updated plugin URI, author, and contributor metadata. + = 1.0.0 = * First stable release. Internal usage scanning, external reverse image search (Google Vision + TinEye), consensus detection, WP-CLI commands, GDPR privacy tools, and Spanish/Catalan translations. diff --git a/robotstxt-mediaaudit.php b/robotstxt-mediaaudit.php index 589cab1..9874634 100644 --- a/robotstxt-mediaaudit.php +++ b/robotstxt-mediaaudit.php @@ -1,15 +1,16 @@ $baseDir . '/includes/Admin/AuditPage.php', 'MediaRightsAudit\\Admin\\MediaListTable' => $baseDir . '/includes/Admin/MediaListTable.php', 'MediaRightsAudit\\Admin\\Settings' => $baseDir . '/includes/Admin/Settings.php', + 'MediaRightsAudit\\Admin\\ToolsPage' => $baseDir . '/includes/Admin/ToolsPage.php', 'MediaRightsAudit\\CLI\\Command' => $baseDir . '/includes/CLI/Command.php', 'MediaRightsAudit\\Core\\Activator' => $baseDir . '/includes/Core/Activator.php', 'MediaRightsAudit\\Core\\Database' => $baseDir . '/includes/Core/Database.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 7b57174..5fcb1d6 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -25,6 +25,7 @@ class ComposerStaticInit957728ab3efa005f456e5f9df13a19d2 'MediaRightsAudit\\Admin\\AuditPage' => __DIR__ . '/../..' . '/includes/Admin/AuditPage.php', 'MediaRightsAudit\\Admin\\MediaListTable' => __DIR__ . '/../..' . '/includes/Admin/MediaListTable.php', 'MediaRightsAudit\\Admin\\Settings' => __DIR__ . '/../..' . '/includes/Admin/Settings.php', + 'MediaRightsAudit\\Admin\\ToolsPage' => __DIR__ . '/../..' . '/includes/Admin/ToolsPage.php', 'MediaRightsAudit\\CLI\\Command' => __DIR__ . '/../..' . '/includes/CLI/Command.php', 'MediaRightsAudit\\Core\\Activator' => __DIR__ . '/../..' . '/includes/Core/Activator.php', 'MediaRightsAudit\\Core\\Database' => __DIR__ . '/../..' . '/includes/Core/Database.php',