v1.7.1
This commit is contained in:
parent
4402f3570d
commit
7b7fff5246
36 changed files with 610 additions and 31 deletions
|
|
@ -153,7 +153,7 @@
|
||||||
var targetId = $( this ).data( 'target' );
|
var targetId = $( this ).data( 'target' );
|
||||||
var $row = $( '#' + targetId );
|
var $row = $( '#' + targetId );
|
||||||
$row.prop( 'hidden', function ( i, v ) { return !v; } );
|
$row.prop( 'hidden', function ( i, v ) { return !v; } );
|
||||||
$( this ).text( $row.prop( 'hidden' ) ? 'Dismiss' : 'Cancel' );
|
$( this ).text( $row.prop( 'hidden' ) ? mraAlerts.i18n.dismiss : mraAlerts.i18n.cancel );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// Cancel button inside dismiss form row.
|
// Cancel button inside dismiss form row.
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
var $row = $( this ).closest( 'tr.mra-dismiss-row' );
|
var $row = $( this ).closest( 'tr.mra-dismiss-row' );
|
||||||
var rowId = $row.attr( 'id' );
|
var rowId = $row.attr( 'id' );
|
||||||
$row.prop( 'hidden', true );
|
$row.prop( 'hidden', true );
|
||||||
$( '[data-target="' + rowId + '"]' ).text( 'Dismiss' );
|
$( '[data-target="' + rowId + '"]' ).text( mraAlerts.i18n.dismiss );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} ( jQuery ) );
|
} ( jQuery ) );
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,17 @@
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.7.1 =
|
||||||
|
|
||||||
|
_Release date: 2026-06-02_
|
||||||
|
|
||||||
|
**Fixed**
|
||||||
|
|
||||||
|
* Dismissal timestamps are now stored in UTC, consistent with all other plugin timestamps (`dismissed_at`).
|
||||||
|
* Settings option (`robotstxt_mediaaudit_settings`) no longer autoloads on every page request, reducing the autoloaded options payload.
|
||||||
|
* Alert count on the Media Audit dashboard is now cached with a transient (1 hour TTL) and invalidated automatically on dismiss, reactivate, scan, purge, and full reset operations.
|
||||||
|
* "Dismiss" and "Cancel" button labels on the Alerts page are now translatable via `wp_localize_script`.
|
||||||
|
* Added direct file access guard (`if ( ! defined( 'ABSPATH' ) ) { exit; }`) to all PHP class files in `includes/`.
|
||||||
|
|
||||||
= 1.7.0 =
|
= 1.7.0 =
|
||||||
|
|
||||||
_Release date: 2026-05-11_
|
_Release date: 2026-05-11_
|
||||||
|
|
|
||||||
|
|
@ -130,11 +130,12 @@ class AlertsPage {
|
||||||
notes = VALUES(notes)",
|
notes = VALUES(notes)",
|
||||||
$attachment_id,
|
$attachment_id,
|
||||||
$user_id,
|
$user_id,
|
||||||
current_time( 'mysql' ),
|
current_time( 'mysql', true ),
|
||||||
$notes
|
$notes
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
delete_transient( 'mra_alert_count' );
|
||||||
self::invalidate_cache();
|
self::invalidate_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +156,7 @@ class AlertsPage {
|
||||||
array( '%d' )
|
array( '%d' )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
delete_transient( 'mra_alert_count' );
|
||||||
self::invalidate_cache();
|
self::invalidate_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,6 +194,17 @@ class AlertsPage {
|
||||||
|
|
||||||
wp_enqueue_style( 'mra-admin', $base . 'css/media-audit-admin.css', array(), $ver );
|
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_enqueue_script( 'mra-admin', $base . 'js/media-audit-admin.js', array( 'jquery' ), $ver, true );
|
||||||
|
|
||||||
|
wp_localize_script(
|
||||||
|
'mra-admin',
|
||||||
|
'mraAlerts',
|
||||||
|
array(
|
||||||
|
'i18n' => array(
|
||||||
|
'dismiss' => __( 'Dismiss', 'robotstxt-mediaaudit' ),
|
||||||
|
'cancel' => __( 'Cancel', 'robotstxt-mediaaudit' ),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Admin;
|
namespace MediaRightsAudit\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\External\HostnameFilter;
|
use MediaRightsAudit\External\HostnameFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Admin;
|
namespace MediaRightsAudit\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\AlertsPage;
|
use MediaRightsAudit\Admin\AlertsPage;
|
||||||
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
||||||
use MediaRightsAudit\External\ExternalScanner;
|
use MediaRightsAudit\External\ExternalScanner;
|
||||||
|
|
@ -928,6 +932,11 @@ class AuditPage {
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
private static function get_alert_attachment_count(): int {
|
private static function get_alert_attachment_count(): int {
|
||||||
|
$cached = get_transient( 'mra_alert_count' );
|
||||||
|
if ( is_int( $cached ) ) {
|
||||||
|
return $cached;
|
||||||
|
}
|
||||||
|
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||||
|
|
@ -937,6 +946,7 @@ class AuditPage {
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( ! is_array( $rows ) ) {
|
if ( ! is_array( $rows ) ) {
|
||||||
|
set_transient( 'mra_alert_count', 0, HOUR_IN_SECONDS );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -983,6 +993,7 @@ class AuditPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_transient( 'mra_alert_count', $alert_count, HOUR_IN_SECONDS );
|
||||||
return $alert_count;
|
return $alert_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Admin;
|
namespace MediaRightsAudit\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\AlertsPage;
|
use MediaRightsAudit\Admin\AlertsPage;
|
||||||
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
||||||
use MediaRightsAudit\External\HostnameFilter;
|
use MediaRightsAudit\External\HostnameFilter;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Admin;
|
namespace MediaRightsAudit\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers and renders the plugin Settings page with tab navigation.
|
* Registers and renders the plugin Settings page with tab navigation.
|
||||||
*
|
*
|
||||||
|
|
@ -233,6 +237,7 @@ class Settings {
|
||||||
'type' => 'array',
|
'type' => 'array',
|
||||||
'sanitize_callback' => array( $this, 'sanitize' ),
|
'sanitize_callback' => array( $this, 'sanitize' ),
|
||||||
'default' => array(),
|
'default' => array(),
|
||||||
|
'autoload' => false,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Admin;
|
namespace MediaRightsAudit\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Core\Database;
|
use MediaRightsAudit\Core\Database;
|
||||||
use MediaRightsAudit\Core\Queue\Scheduler;
|
use MediaRightsAudit\Core\Queue\Scheduler;
|
||||||
use MediaRightsAudit\External\ExternalScanner;
|
use MediaRightsAudit\External\ExternalScanner;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\CLI;
|
namespace MediaRightsAudit\CLI;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Core\Database;
|
use MediaRightsAudit\Core\Database;
|
||||||
use MediaRightsAudit\External\ExternalScanner;
|
use MediaRightsAudit\External\ExternalScanner;
|
||||||
use MediaRightsAudit\Internal\AttachmentIndexer;
|
use MediaRightsAudit\Internal\AttachmentIndexer;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Core;
|
namespace MediaRightsAudit\Core;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\Settings;
|
use MediaRightsAudit\Admin\Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Core;
|
namespace MediaRightsAudit\Core;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages all custom table creation and schema migrations.
|
* Manages all custom table creation and schema migrations.
|
||||||
*
|
*
|
||||||
|
|
@ -86,6 +90,8 @@ class Database {
|
||||||
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_usage`" );
|
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_usage`" );
|
||||||
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_index`" );
|
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_index`" );
|
||||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery
|
// phpcs:enable WordPress.DB.DirectDatabaseQuery
|
||||||
|
|
||||||
|
delete_transient( 'mra_alert_count' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Core;
|
namespace MediaRightsAudit\Core;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Core\Queue\Scheduler;
|
use MediaRightsAudit\Core\Queue\Scheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Core;
|
namespace MediaRightsAudit\Core;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\AlertsPage;
|
use MediaRightsAudit\Admin\AlertsPage;
|
||||||
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
use MediaRightsAudit\Admin\AttachmentDetailPage;
|
||||||
use MediaRightsAudit\Admin\AuditPage;
|
use MediaRightsAudit\Admin\AuditPage;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Core\Queue;
|
namespace MediaRightsAudit\Core\Queue;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thin wrapper around Action Scheduler that namespaces all jobs under a single group.
|
* Thin wrapper around Action Scheduler that namespaces all jobs under a single group.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
4
includes/External/AbstractProvider.php
vendored
4
includes/External/AbstractProvider.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides rate-limited scanning via a transient-based per-minute counter.
|
* Provides rate-limited scanning via a transient-based per-minute counter.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
10
includes/External/ExternalScanner.php
vendored
10
includes/External/ExternalScanner.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Core\Queue\Scheduler;
|
use MediaRightsAudit\Core\Queue\Scheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -145,6 +149,8 @@ class ExternalScanner {
|
||||||
|
|
||||||
self::scan_batch( $batch_size );
|
self::scan_batch( $batch_size );
|
||||||
|
|
||||||
|
delete_transient( 'mra_alert_count' );
|
||||||
|
|
||||||
if ( self::get_pending_count() > 0 ) {
|
if ( self::get_pending_count() > 0 ) {
|
||||||
Scheduler::schedule_single( self::AS_HOOK );
|
Scheduler::schedule_single( self::AS_HOOK );
|
||||||
}
|
}
|
||||||
|
|
@ -361,6 +367,8 @@ class ExternalScanner {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||||
|
|
||||||
|
delete_transient( 'mra_alert_count' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -381,6 +389,8 @@ class ExternalScanner {
|
||||||
);
|
);
|
||||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery
|
// phpcs:enable WordPress.DB.DirectDatabaseQuery
|
||||||
|
|
||||||
|
delete_transient( 'mra_alert_count' );
|
||||||
|
|
||||||
return $count;
|
return $count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
4
includes/External/GoogleVisionProvider.php
vendored
4
includes/External/GoogleVisionProvider.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\Settings;
|
use MediaRightsAudit\Admin\Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
4
includes/External/HostnameFilter.php
vendored
4
includes/External/HostnameFilter.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classifies hostnames against the site's configured alert (include) and
|
* Classifies hostnames against the site's configured alert (include) and
|
||||||
* ignored (exclude) lists stored in the robotstxt_mediaaudit_settings option.
|
* ignored (exclude) lists stored in the robotstxt_mediaaudit_settings option.
|
||||||
|
|
|
||||||
4
includes/External/PicDefenseProvider.php
vendored
4
includes/External/PicDefenseProvider.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\Settings;
|
use MediaRightsAudit\Admin\Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
4
includes/External/ResultsConsolidator.php
vendored
4
includes/External/ResultsConsolidator.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregates external scan results across multiple providers to surface
|
* Aggregates external scan results across multiple providers to surface
|
||||||
* domains that appear in more than one provider's findings (consensus signals).
|
* domains that appear in more than one provider's findings (consensus signals).
|
||||||
|
|
|
||||||
4
includes/External/ScanResult.php
vendored
4
includes/External/ScanResult.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable result returned by AbstractProvider::scan().
|
* Immutable result returned by AbstractProvider::scan().
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
4
includes/External/TinEyeProvider.php
vendored
4
includes/External/TinEyeProvider.php
vendored
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\External;
|
namespace MediaRightsAudit\External;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Admin\Settings;
|
use MediaRightsAudit\Admin\Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Internal;
|
namespace MediaRightsAudit\Internal;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Core\Queue\Scheduler;
|
use MediaRightsAudit\Core\Queue\Scheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Internal;
|
namespace MediaRightsAudit\Internal;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
use MediaRightsAudit\Core\Queue\Scheduler;
|
use MediaRightsAudit\Core\Queue\Scheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Privacy;
|
namespace MediaRightsAudit\Privacy;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erases Media Audit data for a given user via the WordPress privacy tools.
|
* Erases Media Audit data for a given user via the WordPress privacy tools.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
namespace MediaRightsAudit\Privacy;
|
namespace MediaRightsAudit\Privacy;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports Media Audit data for a given user via the WordPress privacy tools.
|
* Exports Media Audit data for a given user via the WordPress privacy tools.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
=== Media Audit (by ROBOTSTXT) ===
|
=== Media Audit (by ROBOTSTXT) ===
|
||||||
Contributors: javiercasares, robotstxt
|
Contributors: javiercasares, robotstxt
|
||||||
Tags: media, copyright, images, reverse image search, media library
|
Tags: media, copyright, images, reverse image search, media library
|
||||||
Requires at least: 6.8
|
Requires at least: 5.3
|
||||||
Tested up to: 7.0
|
Tested up to: 7.0
|
||||||
Requires PHP: 8.2
|
Requires PHP: 8.0
|
||||||
Requires Plugins: action-scheduler
|
Requires Plugins: action-scheduler
|
||||||
Stable tag: 1.7.0
|
Stable tag: 1.7.1
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
License URI: https://www.gnu.org/licenses/gpl-3.0.txt
|
License URI: https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
* Plugin Name: Media Audit (by ROBOTSTXT)
|
* Plugin Name: Media Audit (by ROBOTSTXT)
|
||||||
* Plugin URI: https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit
|
* Plugin URI: https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit
|
||||||
* Description: Internal media library usage auditing and external reverse image search to detect potential copyright issues.
|
* Description: Internal media library usage auditing and external reverse image search to detect potential copyright issues.
|
||||||
* Version: 1.7.0
|
* Version: 1.7.1
|
||||||
* Requires at least: 6.8
|
* Requires at least: 5.3
|
||||||
* Tested up to: 7.0
|
* Tested up to: 7.0
|
||||||
* Requires PHP: 8.2
|
* Requires PHP: 8.0
|
||||||
* Requires Plugins: action-scheduler
|
* Requires Plugins: action-scheduler
|
||||||
* Author: ROBOTSTXT
|
* Author: ROBOTSTXT
|
||||||
* Author URI: https://www.robotstxt.es/
|
* Author URI: https://www.robotstxt.es/
|
||||||
|
|
@ -23,7 +23,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
define( 'ROBOTSTXT_MEDIAAUDIT_VERSION', '1.7.0' );
|
define( 'ROBOTSTXT_MEDIAAUDIT_VERSION', '1.7.1' );
|
||||||
define( 'ROBOTSTXT_MEDIAAUDIT_DB_VERSION', '1.2.0' );
|
define( 'ROBOTSTXT_MEDIAAUDIT_DB_VERSION', '1.2.0' );
|
||||||
define( 'ROBOTSTXT_MEDIAAUDIT_PLUGIN_FILE', __FILE__ );
|
define( 'ROBOTSTXT_MEDIAAUDIT_PLUGIN_FILE', __FILE__ );
|
||||||
define( 'ROBOTSTXT_MEDIAAUDIT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
define( 'ROBOTSTXT_MEDIAAUDIT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||||
|
|
|
||||||
383
robotstxt-updater.php
Normal file
383
robotstxt-updater.php
Normal file
|
|
@ -0,0 +1,383 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Generic JSON-based updater for ROBOTSTXT plugins.
|
||||||
|
*
|
||||||
|
* This file is designed to be copied to any ROBOTSTXT plugin.
|
||||||
|
* It auto-configures itself by reading the plugin headers.
|
||||||
|
*
|
||||||
|
* @package ROBOTSTXT
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! class_exists( 'Robotstxt_Updater' ) ) {
|
||||||
|
/**
|
||||||
|
* Class Robotstxt_Updater
|
||||||
|
*
|
||||||
|
* Generic updater that works with any plugin.
|
||||||
|
* Reads plugin headers and constructs update URL automatically.
|
||||||
|
*/
|
||||||
|
class Robotstxt_Updater {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin file path.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $plugin_file_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin basename (e.g., 'my-plugin/my-plugin.php').
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $plugin_basename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin slug (directory name).
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $plugin_slug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote JSON URL.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $json_url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache key.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $cache_key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin headers.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $plugin_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the updater.
|
||||||
|
*
|
||||||
|
* Usage in your main plugin file:
|
||||||
|
* require_once __DIR__ . '/robotstxt-updater.php';
|
||||||
|
* Robotstxt_Updater::init( __FILE__ );
|
||||||
|
*
|
||||||
|
* @param string $plugin_file_path Absolute path to the main plugin file.
|
||||||
|
*/
|
||||||
|
public static function init( string $plugin_file_path ): void {
|
||||||
|
$instance = new self( $plugin_file_path );
|
||||||
|
$instance->register();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $plugin_file_path Absolute path to the main plugin file.
|
||||||
|
*/
|
||||||
|
private function __construct( string $plugin_file_path ) {
|
||||||
|
$this->plugin_file_path = $plugin_file_path;
|
||||||
|
$this->plugin_basename = plugin_basename( $plugin_file_path );
|
||||||
|
$this->plugin_slug = dirname( $this->plugin_basename );
|
||||||
|
$this->plugin_data = $this->get_plugin_data();
|
||||||
|
$this->json_url = $this->build_json_url();
|
||||||
|
$this->cache_key = 'robotstxt_updater_' . md5( $this->plugin_basename );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register WordPress hooks.
|
||||||
|
*/
|
||||||
|
private function register(): void {
|
||||||
|
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'inject_update_info' ) );
|
||||||
|
add_filter( 'plugins_api', array( $this, 'provide_plugin_details' ), 10, 3 );
|
||||||
|
add_action( 'admin_init', array( $this, 'handle_cache_clear' ) );
|
||||||
|
add_action( 'robotstxt_updater_clear_cache', array( $this, 'clear_cache' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get plugin headers.
|
||||||
|
*
|
||||||
|
* @return array Plugin data.
|
||||||
|
*/
|
||||||
|
private function get_plugin_data(): array {
|
||||||
|
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_plugin_data( $this->plugin_file_path, false, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build JSON URL from plugin headers.
|
||||||
|
*
|
||||||
|
* Tries to use "Gitea Plugin URI" header to construct the URL.
|
||||||
|
* Falls back to Plugin URI if Gitea URI is not available.
|
||||||
|
*
|
||||||
|
* @return string JSON URL.
|
||||||
|
*/
|
||||||
|
private function build_json_url(): string {
|
||||||
|
// Try Gitea Plugin URI (format: "OWNER/REPO" or full URL).
|
||||||
|
if ( ! empty( $this->plugin_data['Gitea Plugin URI'] ) ) {
|
||||||
|
$gitea_uri = $this->plugin_data['Gitea Plugin URI'];
|
||||||
|
|
||||||
|
// If it's already a full URL, use it.
|
||||||
|
if ( str_starts_with( $gitea_uri, 'http' ) ) {
|
||||||
|
// Extract base URL and construct JSON path.
|
||||||
|
return rtrim( $gitea_uri, '/' ) . '/raw/branch/main/update.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's in format "OWNER/REPO", construct full URL.
|
||||||
|
if ( preg_match( '#^[^/]+/[^/]+$#', $gitea_uri ) ) {
|
||||||
|
return "https://git.robotstxt.es/{$gitea_uri}/raw/branch/main/update.json";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: try to extract from Plugin URI.
|
||||||
|
if ( ! empty( $this->plugin_data['PluginURI'] ) ) {
|
||||||
|
$plugin_uri = $this->plugin_data['PluginURI'];
|
||||||
|
if ( str_contains( $plugin_uri, 'git.robotstxt.es' ) ) {
|
||||||
|
return rtrim( $plugin_uri, '/' ) . '/raw/branch/main/update.json';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last resort: construct from plugin slug.
|
||||||
|
return "https://git.robotstxt.es/ROBOTSTXT/{$this->plugin_slug}/raw/branch/main/update.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject update info into WP's plugin update transient.
|
||||||
|
*
|
||||||
|
* @param object|mixed $transient The update_plugins transient.
|
||||||
|
*
|
||||||
|
* @return object The modified transient.
|
||||||
|
*/
|
||||||
|
public function inject_update_info( $transient ) {
|
||||||
|
if ( ! is_object( $transient ) ) {
|
||||||
|
$transient = new stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $transient->checked ) || ! is_array( $transient->checked ) ) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $transient->checked[ $this->plugin_basename ] ) ) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_version = $transient->checked[ $this->plugin_basename ];
|
||||||
|
$remote = $this->get_remote_data();
|
||||||
|
|
||||||
|
if ( empty( $remote['version'] ) || empty( $remote['download_url'] ) ) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $this->is_compatible( $remote ) ) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( version_compare( $remote['version'], $current_version, '>' ) ) {
|
||||||
|
$update = (object) array(
|
||||||
|
'slug' => $remote['slug'] ?? $this->plugin_slug,
|
||||||
|
'plugin' => $this->plugin_basename,
|
||||||
|
'new_version' => $remote['version'],
|
||||||
|
'url' => $remote['homepage'] ?? $this->plugin_data['PluginURI'] ?? '',
|
||||||
|
'package' => $remote['download_url'],
|
||||||
|
'tested' => $remote['tested'] ?? '',
|
||||||
|
'requires' => $remote['requires'] ?? '',
|
||||||
|
'requires_php' => $remote['requires_php'] ?? '',
|
||||||
|
);
|
||||||
|
|
||||||
|
$transient->response[ $this->plugin_basename ] = $update;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide "View details" modal content.
|
||||||
|
*
|
||||||
|
* @param false|object|array $result The result object or array.
|
||||||
|
* @param string $action The type of information being requested.
|
||||||
|
* @param object $args Plugin API arguments.
|
||||||
|
*
|
||||||
|
* @return false|object The plugin information object or false.
|
||||||
|
*/
|
||||||
|
public function provide_plugin_details( $result, string $action, object $args ) {
|
||||||
|
if ( 'plugin_information' !== $action ) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $args->slug ) || $args->slug !== $this->plugin_slug ) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$remote = $this->get_remote_data();
|
||||||
|
|
||||||
|
if ( empty( $remote['version'] ) ) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) array(
|
||||||
|
'name' => $remote['name'] ?? $this->plugin_data['Name'] ?? $this->plugin_slug,
|
||||||
|
'slug' => $remote['slug'] ?? $this->plugin_slug,
|
||||||
|
'version' => $remote['version'],
|
||||||
|
'author' => $remote['author'] ?? $this->plugin_data['Author'] ?? '',
|
||||||
|
'homepage' => $remote['homepage'] ?? $this->plugin_data['PluginURI'] ?? '',
|
||||||
|
'requires' => $remote['requires'] ?? '',
|
||||||
|
'tested' => $remote['tested'] ?? '',
|
||||||
|
'requires_php' => $remote['requires_php'] ?? '',
|
||||||
|
'sections' => array(
|
||||||
|
'description' => $remote['description'] ?? $this->plugin_data['Description'] ?? '',
|
||||||
|
'changelog' => $remote['changelog'] ?? '',
|
||||||
|
),
|
||||||
|
'download_link' => $remote['download_url'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get remote data with caching and HMAC signature verification.
|
||||||
|
*
|
||||||
|
* @return array Remote data.
|
||||||
|
*/
|
||||||
|
private function get_remote_data(): array {
|
||||||
|
$cached = get_site_transient( $this->cache_key );
|
||||||
|
|
||||||
|
// Verify HMAC signature if AUTH_SALT is defined and cache has signature.
|
||||||
|
if ( false !== $cached && defined( 'AUTH_SALT' ) && '' !== AUTH_SALT ) {
|
||||||
|
if ( is_array( $cached ) && isset( $cached['signature'], $cached['data'] ) ) {
|
||||||
|
$expected_sig = hash_hmac( 'sha256', $this->cache_key . serialize( $cached['data'] ), AUTH_SALT );
|
||||||
|
|
||||||
|
if ( hash_equals( $expected_sig, $cached['signature'] ) ) {
|
||||||
|
// Signature valid, return data.
|
||||||
|
return is_array( $cached['data'] ) ? $cached['data'] : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature invalid, delete corrupted cache.
|
||||||
|
delete_site_transient( $this->cache_key );
|
||||||
|
$cached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no valid cache, fetch fresh data.
|
||||||
|
if ( false === $cached ) {
|
||||||
|
$remote = $this->fetch_json();
|
||||||
|
|
||||||
|
// Store with HMAC signature if AUTH_SALT is available.
|
||||||
|
if ( defined( 'AUTH_SALT' ) && '' !== AUTH_SALT ) {
|
||||||
|
$payload = array(
|
||||||
|
'data' => $remote ?: array(),
|
||||||
|
'timestamp' => time(),
|
||||||
|
'signature' => hash_hmac( 'sha256', $this->cache_key . serialize( $remote ?: array() ), AUTH_SALT ),
|
||||||
|
);
|
||||||
|
set_site_transient( $this->cache_key, $payload, 6 * HOUR_IN_SECONDS );
|
||||||
|
} else {
|
||||||
|
// Fallback to standard caching.
|
||||||
|
set_site_transient( $this->cache_key, $remote ?: array(), 6 * HOUR_IN_SECONDS );
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array( $remote ) ? $remote : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy cache format without signature (backward compatibility).
|
||||||
|
return is_array( $cached ) ? $cached : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch JSON from remote URL.
|
||||||
|
*
|
||||||
|
* @return array Decoded JSON data.
|
||||||
|
*/
|
||||||
|
private function fetch_json(): array {
|
||||||
|
$response = wp_remote_get(
|
||||||
|
$this->json_url,
|
||||||
|
array(
|
||||||
|
'timeout' => 10,
|
||||||
|
'headers' => array(
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_wp_error( $response ) ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = (int) wp_remote_retrieve_response_code( $response );
|
||||||
|
if ( $code < 200 || $code >= 300 ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body( $response );
|
||||||
|
$data = json_decode( $body, true );
|
||||||
|
|
||||||
|
return is_array( $data ) ? $data : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check compatibility.
|
||||||
|
*
|
||||||
|
* @param array $remote Remote data.
|
||||||
|
*
|
||||||
|
* @return bool True if compatible.
|
||||||
|
*/
|
||||||
|
private function is_compatible( array $remote ): bool {
|
||||||
|
if ( ! empty( $remote['requires_php'] ) ) {
|
||||||
|
if ( version_compare( PHP_VERSION, $remote['requires_php'], '<' ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $remote['requires'] ) ) {
|
||||||
|
if ( version_compare( get_bloginfo( 'version' ), $remote['requires'], '<' ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle manual cache clear via URL parameter.
|
||||||
|
*/
|
||||||
|
public function handle_cache_clear(): void {
|
||||||
|
// Check if this is a cache clear request first.
|
||||||
|
$clear_cache = filter_input( INPUT_GET, 'robotstxt_clear_update_cache', FILTER_UNSAFE_RAW );
|
||||||
|
if ( null === $clear_cache ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a cache clear request - now verify nonce.
|
||||||
|
$nonce_raw = filter_input( INPUT_GET, '_wpnonce', FILTER_UNSAFE_RAW );
|
||||||
|
$nonce = $nonce_raw ? sanitize_text_field( wp_unslash( $nonce_raw ) ) : '';
|
||||||
|
|
||||||
|
if ( ! wp_verify_nonce( $nonce, 'robotstxt_clear_update_cache' ) ) {
|
||||||
|
wp_die( esc_html__( 'Security check failed', 'robotstxt-smtp' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check permissions.
|
||||||
|
if ( ! current_user_can( 'update_plugins' ) ) {
|
||||||
|
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'robotstxt-smtp' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->clear_cache();
|
||||||
|
wp_safe_redirect( remove_query_arg( array( 'robotstxt_clear_update_cache', '_wpnonce' ) ) );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear update cache.
|
||||||
|
*/
|
||||||
|
public function clear_cache(): void {
|
||||||
|
delete_site_transient( $this->cache_key );
|
||||||
|
delete_site_transient( 'update_plugins' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
vendor/autoload.php
vendored
7
vendor/autoload.php
vendored
|
|
@ -14,12 +14,9 @@ if (PHP_VERSION_ID < 50600) {
|
||||||
echo $err;
|
echo $err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trigger_error(
|
throw new RuntimeException($err);
|
||||||
$err,
|
|
||||||
E_USER_ERROR
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
return ComposerAutoloaderInit957728ab3efa005f456e5f9df13a19d2::getLoader();
|
return ComposerAutoloaderInit149e6f8eeaef8d46113f09e1e8d4e757::getLoader();
|
||||||
|
|
|
||||||
45
vendor/composer/InstalledVersions.php
vendored
45
vendor/composer/InstalledVersions.php
vendored
|
|
@ -26,12 +26,23 @@ use Composer\Semver\VersionParser;
|
||||||
*/
|
*/
|
||||||
class InstalledVersions
|
class InstalledVersions
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[]|null
|
* @var mixed[]|null
|
||||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
*/
|
*/
|
||||||
private static $installed;
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $installedIsLocalDir;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool|null
|
* @var bool|null
|
||||||
*/
|
*/
|
||||||
|
|
@ -309,6 +320,24 @@ class InstalledVersions
|
||||||
{
|
{
|
||||||
self::$installed = $data;
|
self::$installed = $data;
|
||||||
self::$installedByVendor = array();
|
self::$installedByVendor = array();
|
||||||
|
|
||||||
|
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||||
|
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||||
|
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||||
|
// all installed packages for example
|
||||||
|
self::$installedIsLocalDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -322,19 +351,27 @@ class InstalledVersions
|
||||||
}
|
}
|
||||||
|
|
||||||
$installed = array();
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
if (self::$canGetVendors) {
|
if (self::$canGetVendors) {
|
||||||
|
$selfDir = self::getSelfDir();
|
||||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
$installed[] = self::$installedByVendor[$vendorDir];
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
$required = require $vendorDir.'/composer/installed.php';
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
$installed[] = $required;
|
||||||
self::$installed = $installed[count($installed) - 1];
|
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
self::$installed = $required;
|
||||||
|
self::$installedIsLocalDir = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,7 +387,7 @@ class InstalledVersions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self::$installed !== array()) {
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
$installed[] = self::$installed;
|
$installed[] = self::$installed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
vendor/composer/LICENSE
vendored
2
vendor/composer/LICENSE
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|
@ -17,3 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
|
||||||
10
vendor/composer/autoload_real.php
vendored
10
vendor/composer/autoload_real.php
vendored
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
// autoload_real.php @generated by Composer
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
class ComposerAutoloaderInit957728ab3efa005f456e5f9df13a19d2
|
class ComposerAutoloaderInit149e6f8eeaef8d46113f09e1e8d4e757
|
||||||
{
|
{
|
||||||
private static $loader;
|
private static $loader;
|
||||||
|
|
||||||
|
|
@ -22,12 +22,14 @@ class ComposerAutoloaderInit957728ab3efa005f456e5f9df13a19d2
|
||||||
return self::$loader;
|
return self::$loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit957728ab3efa005f456e5f9df13a19d2', 'loadClassLoader'), true, true);
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit149e6f8eeaef8d46113f09e1e8d4e757', 'loadClassLoader'), true, true);
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit957728ab3efa005f456e5f9df13a19d2', 'loadClassLoader'));
|
spl_autoload_unregister(array('ComposerAutoloaderInit149e6f8eeaef8d46113f09e1e8d4e757', 'loadClassLoader'));
|
||||||
|
|
||||||
require __DIR__ . '/autoload_static.php';
|
require __DIR__ . '/autoload_static.php';
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit957728ab3efa005f456e5f9df13a19d2::getInitializer($loader));
|
call_user_func(\Composer\Autoload\ComposerStaticInit149e6f8eeaef8d46113f09e1e8d4e757::getInitializer($loader));
|
||||||
|
|
||||||
$loader->register(true);
|
$loader->register(true);
|
||||||
|
|
||||||
|
|
|
||||||
8
vendor/composer/autoload_static.php
vendored
8
vendor/composer/autoload_static.php
vendored
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
namespace Composer\Autoload;
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
class ComposerStaticInit957728ab3efa005f456e5f9df13a19d2
|
class ComposerStaticInit149e6f8eeaef8d46113f09e1e8d4e757
|
||||||
{
|
{
|
||||||
public static $prefixLengthsPsr4 = array (
|
public static $prefixLengthsPsr4 = array (
|
||||||
'M' =>
|
'M' =>
|
||||||
|
|
@ -51,9 +51,9 @@ class ComposerStaticInit957728ab3efa005f456e5f9df13a19d2
|
||||||
public static function getInitializer(ClassLoader $loader)
|
public static function getInitializer(ClassLoader $loader)
|
||||||
{
|
{
|
||||||
return \Closure::bind(function () use ($loader) {
|
return \Closure::bind(function () use ($loader) {
|
||||||
$loader->prefixLengthsPsr4 = ComposerStaticInit957728ab3efa005f456e5f9df13a19d2::$prefixLengthsPsr4;
|
$loader->prefixLengthsPsr4 = ComposerStaticInit149e6f8eeaef8d46113f09e1e8d4e757::$prefixLengthsPsr4;
|
||||||
$loader->prefixDirsPsr4 = ComposerStaticInit957728ab3efa005f456e5f9df13a19d2::$prefixDirsPsr4;
|
$loader->prefixDirsPsr4 = ComposerStaticInit149e6f8eeaef8d46113f09e1e8d4e757::$prefixDirsPsr4;
|
||||||
$loader->classMap = ComposerStaticInit957728ab3efa005f456e5f9df13a19d2::$classMap;
|
$loader->classMap = ComposerStaticInit149e6f8eeaef8d46113f09e1e8d4e757::$classMap;
|
||||||
|
|
||||||
}, null, ClassLoader::class);
|
}, null, ClassLoader::class);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
vendor/composer/installed.php
vendored
4
vendor/composer/installed.php
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
<?php return array(
|
<?php return array(
|
||||||
'root' => array(
|
'root' => array(
|
||||||
'name' => 'robotstxt/mediaaudit',
|
'name' => 'robotstxt/robotstxt-mediaaudit',
|
||||||
'pretty_version' => '1.0.0+no-version-set',
|
'pretty_version' => '1.0.0+no-version-set',
|
||||||
'version' => '1.0.0.0',
|
'version' => '1.0.0.0',
|
||||||
'reference' => null,
|
'reference' => null,
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
'dev' => false,
|
'dev' => false,
|
||||||
),
|
),
|
||||||
'versions' => array(
|
'versions' => array(
|
||||||
'robotstxt/mediaaudit' => array(
|
'robotstxt/robotstxt-mediaaudit' => array(
|
||||||
'pretty_version' => '1.0.0+no-version-set',
|
'pretty_version' => '1.0.0+no-version-set',
|
||||||
'version' => '1.0.0.0',
|
'version' => '1.0.0.0',
|
||||||
'reference' => null,
|
'reference' => null,
|
||||||
|
|
|
||||||
25
vendor/composer/platform_check.php
vendored
Normal file
25
vendor/composer/platform_check.php
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// platform_check.php @generated by Composer
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
|
||||||
|
if (!(PHP_VERSION_ID >= 80200)) {
|
||||||
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($issues) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue