Files
WPS3Media/classes/pro/tools-manager.php
Malin 3248cbb029 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
2026-03-03 12:30:18 +01:00

291 lines
7.1 KiB
PHP

<?php
namespace DeliciousBrains\WP_Offload_Media\Pro;
use Amazon_S3_And_CloudFront_Pro;
use DeliciousBrains\WP_Offload_Media\API\V1\State;
class Tools_Manager {
/**
* @var Tools_Manager
*/
protected static $instance;
/**
* Registered tools.
*
* @var array
*/
private $tools = array();
/**
* Make this class a singleton.
*
* Use this instead of __construct().
*
* @param Amazon_S3_And_CloudFront_Pro $as3cf
*
* @return Tools_Manager
*/
public static function get_instance( $as3cf ) {
if ( ! isset( static::$instance ) && ! ( self::$instance instanceof Tools_Manager ) ) {
static::$instance = new Tools_Manager();
// Initialize the class
static::$instance->init( $as3cf );
}
return static::$instance;
}
/**
* Init.
*
* @param Amazon_S3_And_CloudFront_Pro $as3cf
*/
private function init( $as3cf ) {
add_filter( 'as3cfpro_js_strings', array( $this, 'add_strings' ) );
add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) ); // phpcs:ignore WordPress.WP.CronInterval
add_filter( $as3cf->get_plugin_prefix() . '_api_response_get_' . State::name(), array( $this, 'api_response' ) );
}
/**
* Add additional translated strings for tools.
*
* @param array $strings
*
* @return array
*/
public function add_strings( $strings ) {
return array_merge( $strings, array(
'pause_button' => _x( 'Pause', 'Button text', 'amazon-s3-and-cloudfront' ),
'resume_button' => _x( 'Resume', 'Button text', 'amazon-s3-and-cloudfront' ),
'disabled_tool_button' => _x( 'Disabled because another background process is running.', 'Disabled button tooltip', 'amazon-s3-and-cloudfront' ),
'disabled_tool_bucket_access' => _x( 'Disabled because this tool requires write access to the bucket.', 'Disabled button tooltip', 'amazon-s3-and-cloudfront' ),
'item' => __( 'Item', 'amazon-s3-and-cloudfront' ),
'media_library_item' => _x( 'Media Library Item', 'Source type for item error', 'amazon-s3-and-cloudfront' ),
'edit_item' => _x( 'Edit', 'Link title', 'amazon-s3-and-cloudfront' ),
'dismiss_all' => _x( 'Dismiss All', 'Link title', 'amazon-s3-and-cloudfront' ),
'dismiss' => _x( 'Dismiss', 'Link title', 'amazon-s3-and-cloudfront' ),
// No tools to show yet
'no_tools_header' => _x( 'No Tools Available (Yet)', 'No tools graphic', 'amazon-s3-and-cloudfront' ),
'no_tools_description' => _x( 'Once an active license and media items are detected, the following tools will become available for bulk management between this server and the storage provider.', 'No tools graphic', 'amazon-s3-and-cloudfront' ),
) );
}
/**
* Add cron schedules.
*
* @param array $schedules
*
* @return mixed
*/
public function cron_schedules( $schedules ) {
if ( property_exists( $this, 'cron_interval' ) ) {
$interval = apply_filters( 'as3cf_tool_cron_interval', $this->cron_interval );
} else {
$interval = apply_filters( 'as3cf_tool_cron_interval', 1 );
}
if ( 1 === $interval ) {
$display = __( 'Every Minute', 'amazon-s3-and-cloudfront' );
} else {
$display = sprintf( __( 'Every %d Minutes', 'amazon-s3-and-cloudfront' ), $interval );
}
// Adds our schedule to the existing schedules.
$schedules['as3cf_tool_cron_interval'] = array(
'interval' => MINUTE_IN_SECONDS * $interval,
'display' => $display,
);
return $schedules;
}
/**
* If a get state API response is an array without any info for tools, add tools info.
*
* @handles as3cf_api_response_get_state
*
* @param mixed $response
*
* @return mixed
*/
public function api_response( $response ) {
if ( empty( $response ) || is_wp_error( $response ) || ! is_array( $response ) || isset( $response['tools'] ) ) {
return $response;
}
$response['tools'] = $this->get_tools_info();
return $response;
}
/**
* Register a tool.
*
* @param Tool $tool
* @param string $context
*
* @return bool
*/
public function register_tool( Tool $tool, $context = 'background' ) {
if ( ! empty( $this->tools[ $context ][ $tool->get_tool_key() ] ) ) {
return false;
}
$this->tools[ $context ][ $tool->get_tool_key() ] = $tool;
$tool->priority( $this->get_tools_count() )->init();
return true;
}
/**
* Get tool.
*
* @param string $name
*
* @return bool|Tool
*/
public function get_tool( $name ) {
foreach ( $this->tools as $context ) {
if ( array_key_exists( $name, $context ) ) {
return $context[ $name ];
}
}
return false;
}
/**
* Get tools count.
*
* @return int
*/
public function get_tools_count() {
$count = 0;
foreach ( $this->tools as $context ) {
$count += count( $context );
}
return $count;
}
/**
* Get all tools.
*
* @param string $context Optional context to restrict by.
*
* @return array
*/
public function get_tools( $context = null ) {
$result = array();
foreach ( $this->tools as $_context => $tools ) {
if ( ! empty( $context ) && $_context !== $context ) {
continue;
}
/**
* @var string $key
* @var Tool $tool
*/
foreach ( $tools as $key => $tool ) {
$result[ $key ] = $tool;
}
}
return $result;
}
/**
* Get tool's info, including current status.
*
* @param string $context Optional context to restrict by.
*
* @return array
*/
public function get_tools_info( $context = null ) {
$data = array();
/** @var Tool $tool */
foreach ( $this->get_tools( $context ) as $key => $tool ) {
$data[ $key ] = $tool->get_info();
}
return $data;
}
/**
* Get running tool, or null if none running.
*
* @param string $context Optional context to restrict by.
*
* @return Tool|null
*/
public function get_running_tool( $context = null ) {
/** @var Tool $tool */
foreach ( $this->get_tools( $context ) as $tool ) {
$data = $tool->get_info();
if ( ! empty( $data['is_processing'] ) || ! empty( $data['is_queued'] ) || ! empty( $data['is_paused'] ) ) {
return $tool;
}
}
return null;
}
/**
* Try and perform the requested action for a tool identified by its key.
*
* @param string $tool_key
* @param string $action
*
* @return bool
*/
public function perform_action( $tool_key, $action ) {
$tool = $this->get_tool( $tool_key );
if ( false === $tool ) {
return false;
}
if ( ! method_exists( $tool, 'handle_' . $action ) ) {
return false;
}
$running_tool = $this->get_running_tool();
// Only one tool can be running or interacted with at once.
if ( ! empty( $running_tool ) && $tool->get_tool_key() !== $running_tool->get_tool_key() ) {
return false;
}
call_user_func( array( $tool, 'handle_' . $action ) );
return true;
}
/**
* Protected constructor to prevent creating a new instance of the
* class via the `new` operator from outside this class.
*/
protected function __construct() {
}
/**
* As this class is a singleton it should not be clone-able.
*/
protected function __clone() {
}
/**
* As this class is a singleton it should not be able to be un-serialized.
*/
public function __wakeup() {
}
}