feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)
Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.
Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
- Provider key: s3compatible
- Reads user-configured endpoint URL from settings
- Uses path-style URL access (required by most S3-compatible services)
- Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
- Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
This commit is contained in:
242
classes/settings/validation-manager.php
Normal file
242
classes/settings/validation-manager.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Settings;
|
||||
|
||||
use Amazon_S3_And_CloudFront;
|
||||
use WP_Error;
|
||||
|
||||
class Validation_Manager {
|
||||
/**
|
||||
* @var Amazon_S3_And_CloudFront
|
||||
*/
|
||||
protected $as3cf;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $validation_result = array();
|
||||
|
||||
/**
|
||||
* The threshold in seconds for the relative time to be considered "just now".
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static $threshold_just_now = 60;
|
||||
|
||||
/**
|
||||
* @var Validator_Interface[]
|
||||
*/
|
||||
private $settings_validators = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $base_last_validation_result_key = 'as3cf_last_settings_validation_result';
|
||||
|
||||
/**
|
||||
* @param Amazon_S3_And_CloudFront $as3cf
|
||||
*/
|
||||
public function __construct( Amazon_S3_And_CloudFront $as3cf ) {
|
||||
$this->as3cf = $as3cf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a validator class for a section key.
|
||||
*
|
||||
* @param string $section
|
||||
* @param Validator_Interface $settings_validator
|
||||
*/
|
||||
public function register_validator( string $section, Validator_Interface $settings_validator ) {
|
||||
// Only one instance can be responsible for a section, so we just overwrite if called twice.
|
||||
$this->settings_validators[ $section ] = $settings_validator;
|
||||
|
||||
// The validator may know about one or more actions that are fired when its settings are saved.
|
||||
foreach ( $settings_validator->post_save_settings_actions() as $action ) {
|
||||
add_action( $action, array( $this, 'action_post_save_settings' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all registered validator classes and return the result in an array. If the
|
||||
* force flag is set to true, the validators may run checks that are time-consuming
|
||||
* or affects the global plugin state (notices).
|
||||
*
|
||||
* @param bool $force
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validate( bool $force = false ): array {
|
||||
// Sort the validators by priority.
|
||||
uasort(
|
||||
$this->settings_validators,
|
||||
function ( Validator_Interface $a, Validator_Interface $b ) {
|
||||
return $a->get_validator_priority() <=> $b->get_validator_priority();
|
||||
}
|
||||
);
|
||||
|
||||
$validation_start = time();
|
||||
|
||||
// Run all registered settings validators.
|
||||
foreach ( $this->settings_validators as $section => $settings_validator ) {
|
||||
$transient_key = $this->last_validation_result_key( $section );
|
||||
$this->validation_result[ $section ] = get_site_transient( $transient_key );
|
||||
|
||||
if ( ! $force && is_array( $this->validation_result[ $section ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure only one instance of a validator is processing at once unless forced.
|
||||
$this->validation_result[ $section ] = array(
|
||||
'type' => Validator_Interface::AS3CF_STATUS_MESSAGE_INFO,
|
||||
'message' => array( _x( 'Processing…', 'Validation in progress', 'amazon-s3-and-cloudfront' ) ),
|
||||
'timestamp' => $validation_start,
|
||||
);
|
||||
set_site_transient( $transient_key, $this->validation_result[ $section ], MINUTE_IN_SECONDS );
|
||||
|
||||
$result = $settings_validator->validate_settings( $force );
|
||||
|
||||
$this->validation_result[ $section ]['type'] = $result->get_error_code();
|
||||
$this->validation_result[ $section ]['message'] = array( $result->get_error_message() );
|
||||
|
||||
$timeout = Validator_Interface::AS3CF_STATUS_MESSAGE_ERROR === $result->get_error_code() ? 5 * MINUTE_IN_SECONDS : DAY_IN_SECONDS;
|
||||
|
||||
/**
|
||||
* Adjust the transient timeout for the cache of a setting section's validation results.
|
||||
*
|
||||
* Min: 3 seconds
|
||||
* Max: 7 days
|
||||
*
|
||||
* The default is 1 day unless the result is an error, in which case it is then 5 minutes.
|
||||
*
|
||||
* @param int $timeout Time until transient expires in seconds.
|
||||
* @param string $section The setting section, e.g. storage, delivery or assets.
|
||||
* @param WP_Error $result The result to be cached.
|
||||
*/
|
||||
$timeout = min( max( 3, (int) apply_filters( $this->base_last_validation_result_key . '_timeout', $timeout, $section, $result ) ), 7 * DAY_IN_SECONDS );
|
||||
|
||||
set_site_transient( $transient_key, $this->validation_result[ $section ], $timeout );
|
||||
}
|
||||
|
||||
$this->update_relative_time();
|
||||
|
||||
return $this->validation_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific or all registered sections and status messages.
|
||||
*
|
||||
* If all sections to be returned, they're in an associative array
|
||||
* with the sections as the keys.
|
||||
*
|
||||
* @param string $section Optional specific section's result only.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_validation_result( string $section = '' ) {
|
||||
if ( empty( $this->validation_result ) ) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ( empty( $section ) ) {
|
||||
return $this->validation_result;
|
||||
} elseif ( ! empty( $this->validation_result[ $section ] ) ) {
|
||||
return $this->validation_result[ $section ];
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the validation status for a section.
|
||||
*
|
||||
* @param string $section
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_validation_status( string $section ): string {
|
||||
$result = $this->get_validation_result( $section );
|
||||
|
||||
if ( ! isset( $result['type'] ) ) {
|
||||
return Validator_Interface::AS3CF_STATUS_MESSAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
return $result['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the last validation timestamp to force validation on next check.
|
||||
*
|
||||
* @param bool $saved Whether settings were successfully saved or not
|
||||
*/
|
||||
public function action_post_save_settings( bool $saved ) {
|
||||
if ( empty( $this->settings_validators ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
static $deleted = array();
|
||||
|
||||
foreach ( $this->settings_validators as $section => $validator ) {
|
||||
if ( in_array( $section, $deleted ) || ! in_array( current_action(), $validator->post_save_settings_actions() ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
delete_site_transient( $this->last_validation_result_key( $section ) );
|
||||
$deleted[] = $section;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given section currently have a validation error?
|
||||
*
|
||||
* @param string $section
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function section_has_error( string $section ): bool {
|
||||
if ( empty( $section ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Validator_Interface::AS3CF_STATUS_MESSAGE_ERROR === $this->get_validation_status( $section );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a correctly formatted validation result transient key.
|
||||
*
|
||||
* @param string $section
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function last_validation_result_key( string $section ): string {
|
||||
return $this->base_last_validation_result_key . '_' . $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the relative time for each section's last validation.
|
||||
*/
|
||||
protected function update_relative_time() {
|
||||
foreach ( array_keys( $this->validation_result ) as $key ) {
|
||||
if ( ! isset( $this->validation_result[ $key ]['timestamp'] ) ) {
|
||||
$this->validation_result[ $key ]['last_update'] = _x( 'Unknown', 'Relative time in settings notice', 'amazon-s3-and-cloudfront' );
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->validation_result[ $key ]['last_update'] = $this->get_relative_time( $this->validation_result[ $key ]['timestamp'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relative time display string.
|
||||
*
|
||||
* @param int $timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_relative_time( int $timestamp ): string {
|
||||
if ( time() - $timestamp <= static::$threshold_just_now ) {
|
||||
return _x( 'Just now', 'Relative time in settings notice', 'amazon-s3-and-cloudfront' );
|
||||
}
|
||||
|
||||
return sprintf( __( '%s ago', 'amazon-s3-and-cloudfront' ), human_time_diff( $timestamp ) );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user