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
928 lines
29 KiB
PHP
928 lines
29 KiB
PHP
<?php
|
|
|
|
namespace DeliciousBrains\WP_Offload_Media\Providers\Delivery;
|
|
|
|
use AS3CF_Utils;
|
|
use DeliciousBrains\WP_Offload_Media\Settings_Validator_Trait;
|
|
use DeliciousBrains\WP_Offload_Media\Items\Item;
|
|
use DeliciousBrains\WP_Offload_Media\Providers\Provider;
|
|
use DeliciousBrains\WP_Offload_Media\Settings\Delivery_Check;
|
|
use DeliciousBrains\WP_Offload_Media\Settings\Domain_Check;
|
|
use DeliciousBrains\WP_Offload_Media\Settings\Validator_Interface;
|
|
use Exception;
|
|
use WP_Error as AS3CF_Result;
|
|
|
|
abstract class Delivery_Provider extends Provider implements Validator_Interface {
|
|
use Settings_Validator_Trait;
|
|
|
|
const VALIDATOR_KEY = 'delivery';
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected static $provider_service_name_setting_name = 'delivery-provider-service-name';
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $console_url_prefix_param = '/';
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
protected static $delivery_domain_allowed = true;
|
|
|
|
/**
|
|
* Which storage providers does the delivery provider support, empty means all.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $supported_storage_providers = array();
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected static $signed_urls_key_id_setting_name = 'signed-urls-key-id';
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected static $signed_urls_key_file_path_setting_name = 'signed-urls-key-file-path';
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected static $signed_urls_object_prefix_setting_name = 'signed-urls-object-prefix';
|
|
|
|
/**
|
|
* If left empty, private signing key file not allowed.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $signed_urls_key_id_constants = array();
|
|
|
|
/**
|
|
* If left empty, private signing key file not allowed.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $signed_urls_key_file_path_constants = array();
|
|
|
|
/**
|
|
* If left empty, private signing key file not allowed.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $signed_urls_object_prefix_constants = array();
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $validator_priority = 10;
|
|
|
|
/**
|
|
* A description for the Rewrite Media URLs setting.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_rewrite_media_urls_desc(): string {
|
|
global $as3cf;
|
|
|
|
$mesg = sprintf(
|
|
_x( 'Serves offloaded media files by rewriting local URLs so that they point to %s.', 'Setting description', 'amazon-s3-and-cloudfront' ),
|
|
static::get_provider_service_name()
|
|
);
|
|
$mesg .= ' ' . $as3cf::settings_more_info_link( 'serve-from-s3', 'How URL rewriting works' );
|
|
|
|
return $mesg;
|
|
}
|
|
|
|
/**
|
|
* Does the delivery provider allow for setting a custom delivery domain?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function delivery_domain_allowed() {
|
|
return static::$delivery_domain_allowed;
|
|
}
|
|
|
|
/**
|
|
* A description for the delivery domain settings.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_delivery_domain_desc(): string {
|
|
return sprintf(
|
|
__( 'Serves media from a custom domain that has been pointed to %1$s. <a href="%2$s" target="_blank">How to set a custom domain name</a>', 'amazon-s3-and-cloudfront' ),
|
|
static::get_provider_service_name(),
|
|
static::get_provider_service_quick_start_url() . '#create-cname'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns array of supported storage provider key names, empty array means all supported.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_supported_storage_providers() {
|
|
return static::$supported_storage_providers;
|
|
}
|
|
|
|
/**
|
|
* Does provider support given storage provider?
|
|
*
|
|
* @param string $storage_provider_key
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function supports_storage( $storage_provider_key ) {
|
|
if ( empty( static::$supported_storage_providers ) || in_array( $storage_provider_key, static::$supported_storage_providers ) ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Title used in various places for enabling Signed URLs.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_option_name() {
|
|
return __( 'Serve Private Media from Delivery Provider', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* Description used in various places for enabling Signed URLs.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_option_description() {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Title used in various places for the Signed URLs Key ID.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_key_id_name() {
|
|
return __( 'Signing Key ID', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* Description used in various places for the Signed URLs Key ID.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_key_id_description() {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Title used in various places for the Signed URLs Key File Path.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_key_file_path_name() {
|
|
return __( 'Signing Key File Path', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* Description used in various places for the Signed URLs Key File Path.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_key_file_path_description() {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Title used in various places for the Signed URLs Private Object Prefix.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_object_prefix_name() {
|
|
return __( 'Private Bucket Path', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* Description used in various places for the Signed URLs Private Object Prefix.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_object_prefix_description() {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Description used in settings notice when all tests pass.
|
|
*
|
|
* @param array $recommendations Array of hints/recommendation to add to the success message.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected static function delivery_tests_pass_desc( array $recommendations = array() ): string {
|
|
$message = __( 'Delivery provider is successfully connected and serving offloaded media.', 'amazon-s3-and-cloudfront' );
|
|
|
|
if ( ! empty( $recommendations ) ) {
|
|
$message = __( 'Delivery settings validated.', 'amazon-s3-and-cloudfront' );
|
|
$message .= '<br><br>';
|
|
$message .= join( '<br><br>', $recommendations );
|
|
}
|
|
|
|
return $message;
|
|
}
|
|
|
|
/**
|
|
* Is the provider able to use a private signing key file?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function use_signed_urls_key_file_allowed() {
|
|
return ! empty( static::$signed_urls_key_id_constants ) && ! empty( static::$signed_urls_key_file_path_constants ) && ! empty( static::$signed_urls_object_prefix_constants );
|
|
}
|
|
|
|
/**
|
|
* If private signing key file allowed, returns first (preferred) key id constant that should be defined, otherwise blank.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function preferred_signed_urls_key_id_constant() {
|
|
if ( static::use_signed_urls_key_file_allowed() ) {
|
|
return static::$signed_urls_key_id_constants[0];
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If private signing key file allowed, returns first (preferred) key file path constant that should be defined, otherwise blank.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function preferred_signed_urls_key_file_path_constant() {
|
|
if ( static::use_signed_urls_key_file_allowed() ) {
|
|
return static::$signed_urls_key_file_path_constants[0];
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If private signing key file allowed, returns first (preferred) object prefix constant that should be defined, otherwise blank.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function preferred_signed_urls_object_prefix_constant() {
|
|
if ( static::use_signed_urls_key_file_allowed() ) {
|
|
return static::$signed_urls_object_prefix_constants[0];
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if private signing key id and file path set.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function use_signed_urls_key_file() {
|
|
if ( ! static::use_signed_urls_key_file_allowed() ) {
|
|
return false;
|
|
}
|
|
|
|
return $this->get_signed_urls_key_id() && $this->get_signed_urls_key_file_path() && $this->get_signed_urls_object_prefix();
|
|
}
|
|
|
|
/**
|
|
* Get the private signing key id from a constant or the settings.
|
|
*
|
|
* Falls back to settings if constant is not defined.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_signed_urls_key_id() {
|
|
if ( static::is_signed_urls_key_id_constant_defined() ) {
|
|
$constant = static::signed_urls_key_id_constant();
|
|
|
|
return $constant ? constant( $constant ) : '';
|
|
}
|
|
|
|
return $this->as3cf->get_core_setting( static::$signed_urls_key_id_setting_name );
|
|
}
|
|
|
|
/**
|
|
* Get the private signing key file path from a constant or the settings.
|
|
*
|
|
* Falls back to settings if constant is not defined.
|
|
*
|
|
* @return string|bool
|
|
*/
|
|
public function get_signed_urls_key_file_path() {
|
|
if ( static::is_signed_urls_key_file_path_constant_defined() ) {
|
|
$constant = static::signed_urls_key_file_path_constant();
|
|
|
|
if ( $constant ) {
|
|
return $this->validate_signed_urls_key_file_path( constant( $constant ) );
|
|
} else {
|
|
// Constant defined but value is not a non-empty string.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return $this->validate_signed_urls_key_file_path( $this->as3cf->get_core_setting( static::$signed_urls_key_file_path_setting_name, false ) );
|
|
}
|
|
|
|
/**
|
|
* Get the private signing object prefix from a constant or the settings.
|
|
*
|
|
* Falls back to settings if constant is not defined.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_signed_urls_object_prefix() {
|
|
if ( static::is_signed_urls_object_prefix_constant_defined() ) {
|
|
$constant = static::signed_urls_object_prefix_constant();
|
|
|
|
return $constant ? constant( $constant ) : '';
|
|
}
|
|
|
|
return $this->as3cf->get_core_setting( static::$signed_urls_object_prefix_setting_name );
|
|
}
|
|
|
|
/**
|
|
* Validate a private signing key file path to ensure it exists, is readable, and contains something.
|
|
*
|
|
* @param string $signed_urls_key_file_path
|
|
*
|
|
* @return bool|string
|
|
*/
|
|
public function validate_signed_urls_key_file_path( $signed_urls_key_file_path ) {
|
|
$notice_id = ( $this->as3cf->saving_settings() ? 'temp-' : '' ) . 'validate-signed-urls-key-file-path';
|
|
$this->as3cf->notices->remove_notice_by_id( $notice_id );
|
|
|
|
$notice_settings = array(
|
|
'type' => 'error',
|
|
'only_show_in_settings' => true,
|
|
'only_show_on_tab' => 'media',
|
|
'custom_id' => $notice_id,
|
|
'hide_on_parent' => ! $this->as3cf->saving_settings(),
|
|
);
|
|
|
|
if ( empty( $signed_urls_key_file_path ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( ! file_exists( $signed_urls_key_file_path ) ) {
|
|
$this->as3cf->notices->add_notice( __( 'Given Signing Key File Path is invalid or could not be accessed.', 'amazon-s3-and-cloudfront' ), $notice_settings );
|
|
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$value = file_get_contents( $signed_urls_key_file_path );
|
|
|
|
// An exception isn't always thrown, so check value instead.
|
|
if ( empty( $value ) ) {
|
|
$this->as3cf->notices->add_notice( __( 'Could not read Signing Key File Path\'s contents.', 'amazon-s3-and-cloudfront' ), $notice_settings );
|
|
|
|
return false;
|
|
}
|
|
} catch ( Exception $e ) {
|
|
$this->as3cf->notices->add_notice( __( 'Could not read Signing Key File Path\'s contents.', 'amazon-s3-and-cloudfront' ), $notice_settings );
|
|
|
|
return false;
|
|
}
|
|
|
|
// File exists and has contents.
|
|
return $signed_urls_key_file_path;
|
|
}
|
|
|
|
/**
|
|
* Check if private signing key id constant is defined.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function is_signed_urls_key_id_constant_defined() {
|
|
$constant = static::signed_urls_key_id_constant();
|
|
|
|
return $constant && constant( $constant );
|
|
}
|
|
|
|
/**
|
|
* Check if private signing key file path constant is defined, for speed, does not check validity of file path.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function is_signed_urls_key_file_path_constant_defined() {
|
|
$constant = static::signed_urls_key_file_path_constant();
|
|
|
|
return $constant && constant( $constant );
|
|
}
|
|
|
|
/**
|
|
* Check if private signing object prefix constant is defined.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function is_signed_urls_object_prefix_constant_defined() {
|
|
$constant = static::signed_urls_object_prefix_constant();
|
|
|
|
return $constant && constant( $constant );
|
|
}
|
|
|
|
/**
|
|
* Get the constant used to define the private signing key id.
|
|
*
|
|
* @return string|false Constant name if defined, otherwise false
|
|
*/
|
|
public static function signed_urls_key_id_constant() {
|
|
return AS3CF_Utils::get_first_defined_constant( static::$signed_urls_key_id_constants );
|
|
}
|
|
|
|
/**
|
|
* Get the constant used to define the private signing key file path.
|
|
*
|
|
* @return string|false Constant name if defined, otherwise false
|
|
*/
|
|
public static function signed_urls_key_file_path_constant() {
|
|
return AS3CF_Utils::get_first_defined_constant( static::$signed_urls_key_file_path_constants );
|
|
}
|
|
|
|
/**
|
|
* Get the constant used to define the private signing object prefix.
|
|
*
|
|
* @return string|false Constant name if defined, otherwise false
|
|
*/
|
|
public static function signed_urls_object_prefix_constant() {
|
|
return AS3CF_Utils::get_first_defined_constant( static::$signed_urls_object_prefix_constants );
|
|
}
|
|
|
|
/**
|
|
* Get a site specific file path example for a signed URL key file.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_key_file_path_placeholder() {
|
|
$filename = 'pk-12345678ABCDE.pem';
|
|
|
|
return dirname( ABSPATH ) . DIRECTORY_SEPARATOR . $filename;
|
|
}
|
|
|
|
/**
|
|
* Return a fully formed and potentially expiring signed URL for the given Item.
|
|
*
|
|
* @param Item $as3cf_item
|
|
* @param string $path The required bucket path, may differ from Item's path if image subsize etc.
|
|
* @param string $domain The domain to use for the URL if at all possible.
|
|
* @param string $scheme The scheme to be used if possible.
|
|
* @param array|null $headers Optional array of headers to be passed along to underlying requests.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_url( Item $as3cf_item, $path, $domain, $scheme, $headers = array() ) {
|
|
$item_path = $this->as3cf->maybe_update_delivery_path( $path, $domain );
|
|
$item_path = AS3CF_Utils::encode_filename_in_path( $item_path );
|
|
|
|
return $scheme . '://' . $domain . '/' . $item_path;
|
|
}
|
|
|
|
/**
|
|
* Return a fully formed and expiring signed URL for the given Item.
|
|
*
|
|
* @param Item $as3cf_item
|
|
* @param string $path The required bucket path, may differ from Item's path if image subsize etc.
|
|
* @param string $domain The domain to use for the URL if at all possible.
|
|
* @param string $scheme The scheme to be used if possible.
|
|
* @param int $timestamp URL expires at the given time.
|
|
* @param array|null $headers Optional array of headers to be passed along to underlying requests.
|
|
*
|
|
* @return string
|
|
* @throws Exception
|
|
*/
|
|
public function get_signed_url( Item $as3cf_item, $path, $domain, $scheme, $timestamp, $headers = array() ) {
|
|
/**
|
|
* This default implementation defers to the storage provider's signed URLs.
|
|
* Therefore, we need to use a storage provider client instance for the item's region.
|
|
*/
|
|
if ( ! empty( $as3cf_item->region() ) && ( $this->as3cf->get_storage_provider()->region_required() || $this->as3cf->get_storage_provider()->get_default_region() !== $as3cf_item->region() ) ) {
|
|
$region = $this->as3cf->get_storage_provider()->sanitize_region( $as3cf_item->region() );
|
|
} else {
|
|
$region = '';
|
|
}
|
|
|
|
// Storage Provider may support signing custom domain, e.g. GCP.
|
|
if ( $this->as3cf->get_storage_provider()->get_domain() !== $domain ) {
|
|
$headers['BaseURL'] = $scheme . '://' . $domain;
|
|
}
|
|
|
|
return $this->as3cf->get_provider_client( $region )->get_object_url( $as3cf_item->bucket(), $path, $timestamp, $headers );
|
|
}
|
|
|
|
/**
|
|
* A short description of whether delivery is fast (distributed) or not.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function edge_server_support_desc() {
|
|
return __( 'Fast', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* A short description of whether signed URLs for private media is supported or not.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function signed_urls_support_desc() {
|
|
return __( 'No Private Media', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* Description for when Block All Public Access is enabled and Delivery Provider does not support it.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_block_public_access_enabled_unsupported_desc() {
|
|
return sprintf(
|
|
__( 'You need to disable Block All Public Access so that %1$s can access your bucket for delivery.', 'amazon-s3-and-cloudfront' ),
|
|
static::get_provider_name()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prompt text to confirm that everything is in place to enable Block All Public Access without issues for Delivery Provider.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_block_public_access_confirm_setup_prompt() {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Description for when Object Ownership is enforced and Delivery Provider does not support it.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_object_ownership_enforced_unsupported_desc(): string {
|
|
global $as3cf;
|
|
|
|
$object_ownership_doc = $as3cf::dbrains_url(
|
|
'/wp-offload-media/doc/amazon-s3-bucket-object-ownership/',
|
|
array( 'utm_campaign' => 'support+docs', 'utm_content' => 'change+bucket+access' )
|
|
);
|
|
|
|
return sprintf(
|
|
__( 'You need to edit the bucket\'s Object Ownership setting and <a href="%1$s">enable ACLs</a> so that %2$s can access your bucket for delivery.', 'amazon-s3-and-cloudfront' ),
|
|
$object_ownership_doc,
|
|
static::get_provider_name()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Notice text for when a public file can't be accessed.
|
|
*
|
|
* @param string $error_message
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_cannot_access_public_file_desc( string $error_message ): string {
|
|
return sprintf(
|
|
__(
|
|
'Offloaded media URLs may be broken. %1$s <a href="%2$s" target="_blank">Read more</a>',
|
|
'amazon-s3-and-cloudfront'
|
|
),
|
|
$error_message,
|
|
static::get_provider_service_quick_start_url()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Notice text for when a private file can't be accessed using a signed private URL.
|
|
*
|
|
* @param string $error_message
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_cannot_access_private_file_desc( string $error_message ): string {
|
|
return sprintf(
|
|
__(
|
|
'Private offloaded media URLs may be broken. %1$s <a href="%2$s" target="_blank">Read more</a>',
|
|
'amazon-s3-and-cloudfront'
|
|
),
|
|
$error_message,
|
|
static::get_provider_service_quick_start_url()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Notice text for when a private file can be accessed using an unsigned URL.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_unsigned_url_can_access_private_file_desc(): string {
|
|
global $as3cf;
|
|
|
|
$storage_provider = $as3cf->get_storage_provider();
|
|
|
|
return apply_filters(
|
|
'as3cf_get_unsigned_url_can_access_private_file_desc_' . $storage_provider->get_provider_key_name(),
|
|
sprintf(
|
|
__(
|
|
'Delivery provider is connected, but private media is currently exposed through unsigned URLs. Restore privacy by verifying the configuration of private media settings. <a href="%1$s" target="_blank">Read more</a>',
|
|
'amazon-s3-and-cloudfront'
|
|
),
|
|
static::get_provider_service_quick_start_url()
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prompt text to confirm that everything is in place to enforce Object Ownership without issues for Delivery Provider.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_object_ownership_confirm_setup_prompt(): string {
|
|
// Using the same text as for the Block Public Access prompt.
|
|
return static::get_block_public_access_confirm_setup_prompt();
|
|
}
|
|
|
|
/**
|
|
* Get the link to the provider's console.
|
|
*
|
|
* @param string $bucket
|
|
* @param string $prefix
|
|
* @param string $region
|
|
*
|
|
* @return string
|
|
*
|
|
* NOTE: By default delivery providers append the suffix to the base console URL only.
|
|
* The usual bucket, prefix and region params are still processed and the suffix
|
|
* may end up being either a path, params or both that get deeper into the console.
|
|
*/
|
|
public function get_console_url( string $bucket = '', string $prefix = '', string $region = '' ): string {
|
|
if ( '' !== $prefix ) {
|
|
$prefix = $this->get_console_url_prefix_param() . apply_filters( 'as3cf_' . static::$provider_key_name . '_' . static::$service_key_name . '_console_url_prefix_value', $prefix );
|
|
}
|
|
|
|
$suffix = apply_filters( 'as3cf_' . static::$provider_key_name . '_' . static::$service_key_name . '_console_url_suffix_param', $this->get_console_url_suffix_param( $bucket, $prefix, $region ) );
|
|
|
|
return apply_filters( 'as3cf_' . static::$provider_key_name . '_' . static::$service_key_name . '_console_url', $this->console_url ) . $suffix;
|
|
}
|
|
|
|
/**
|
|
* Get the suffix param to append to the link to the provider's console.
|
|
*
|
|
* @param string $bucket
|
|
* @param string $prefix
|
|
* @param string $region
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_console_url_suffix_param( string $bucket = '', string $prefix = '', string $region = '' ): string {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Validate delivery settings for the configured provider for the delivery status indicator.
|
|
*
|
|
* @param bool $force Force time resource consuming or state altering tests to run.
|
|
*
|
|
* @return AS3CF_Result
|
|
*/
|
|
public function validate_settings( bool $force = false ): AS3CF_Result {
|
|
$storage_provider = $this->as3cf->get_storage_provider();
|
|
$bucket = $this->as3cf->get_setting( 'bucket' );
|
|
$region = $this->as3cf->get_setting( 'region' );
|
|
$recommendations = array();
|
|
|
|
// Validate the delivery provider key.
|
|
$valid_delivery_provider_key = $this->validate_delivery_provider_key();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $valid_delivery_provider_key->get_error_code() ) {
|
|
return $valid_delivery_provider_key;
|
|
}
|
|
|
|
// The storage provider has lower priority and runs before delivery, so we should always have a fresh result.
|
|
if ( $this->is_result_code_unknown_or_error( $this->as3cf->validation_manager->get_validation_status( 'storage' ) ) ) {
|
|
return new AS3CF_Result(
|
|
Validator_Interface::AS3CF_STATUS_MESSAGE_WARNING,
|
|
__( 'Delivery of offloaded media cannot be tested until the storage provider is successfully connected. See "Storage Settings" for more information.', 'amazon-s3-and-cloudfront' )
|
|
);
|
|
}
|
|
|
|
// Ensure the storage provider client is initiated before BAPA/OOE tests.
|
|
$storage_provider->get_client( array( 'region' => $region ) );
|
|
|
|
// If storage BAPA setting is enabled, validate that it's supported by delivery provider.
|
|
if ( ! static::block_public_access_supported() && $storage_provider->block_public_access_supported() && $storage_provider->public_access_blocked( $bucket ) ) {
|
|
return new AS3CF_Result(
|
|
Validator_Interface::AS3CF_STATUS_MESSAGE_ERROR,
|
|
sprintf(
|
|
_x(
|
|
'Offloaded media cannot be delivered because <strong>Block All Public Access</strong> is enabled. <a href="%1$s">Edit bucket security</a>',
|
|
'Delivery setting notice for issue with BAPA enabled on Storage Provider',
|
|
'amazon-s3-and-cloudfront'
|
|
),
|
|
'#/storage/security'
|
|
)
|
|
);
|
|
}
|
|
|
|
$delivery_domain_settings = $this->validate_delivery_domain();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $delivery_domain_settings->get_error_code() ) {
|
|
return $delivery_domain_settings;
|
|
}
|
|
|
|
// Are settings for delivering signed URLs valid?
|
|
$signed_url_settings = $this->validate_signed_url_settings();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $signed_url_settings->get_error_code() ) {
|
|
return $signed_url_settings;
|
|
}
|
|
|
|
// Test accessing files via provider.
|
|
$connection_test = $this->provider_connection_test();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $connection_test->get_error_code() ) {
|
|
return $connection_test;
|
|
}
|
|
|
|
// Is Deliver Offloaded Media enabled?
|
|
$deliver_media = $this->validate_deliver_offloaded_media_enabled();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $deliver_media->get_error_code() ) {
|
|
return $deliver_media;
|
|
}
|
|
|
|
// All good.
|
|
return new AS3CF_Result(
|
|
count( $recommendations ) === 0 ? Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS : Validator_Interface::AS3CF_STATUS_MESSAGE_WARNING,
|
|
static::delivery_tests_pass_desc( $recommendations )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Validate that the delivery provider key provided in settings is valid for the storage provider. This should
|
|
* only happen if the user is using defines statements or has manually edited settings in the db.
|
|
*
|
|
* @return AS3CF_Result
|
|
*/
|
|
protected function validate_delivery_provider_key(): AS3CF_Result {
|
|
$storage_provider = $this->as3cf->get_storage_provider();
|
|
$storage_provider_key = $storage_provider->get_provider_key_name();
|
|
$delivery_provider_key = $this->as3cf->get_core_setting( 'delivery-provider' );
|
|
|
|
$valid_providers = array_keys( $this->as3cf->get_available_delivery_provider_details( $storage_provider_key ) );
|
|
if ( ! in_array( $delivery_provider_key, $valid_providers ) ) {
|
|
return new AS3CF_Result(
|
|
Validator_Interface::AS3CF_STATUS_MESSAGE_ERROR,
|
|
sprintf(
|
|
__( 'An invalid delivery provider has been defined for the active storage provider. Please use %1$s.', 'amazon-s3-and-cloudfront' ),
|
|
"<code>" . AS3CF_Utils::human_readable_join( "</code>, <code>", "</code> or <code>", $valid_providers ) . "</code>"
|
|
)
|
|
);
|
|
}
|
|
|
|
return new AS3CF_Result( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS );
|
|
}
|
|
|
|
/**
|
|
* Validate settings for serving signed URLs.
|
|
*
|
|
* @return AS3CF_Result
|
|
*/
|
|
protected function validate_signed_url_settings(): AS3CF_Result {
|
|
return new AS3CF_Result( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS );
|
|
}
|
|
|
|
/**
|
|
* Validate Deliver Offloaded Media is enabled.
|
|
*
|
|
* @return AS3CF_Result
|
|
*/
|
|
protected function validate_deliver_offloaded_media_enabled(): AS3CF_Result {
|
|
if ( ! $this->as3cf->get_setting( 'serve-from-s3' ) ) {
|
|
return new AS3CF_Result(
|
|
Validator_Interface::AS3CF_STATUS_MESSAGE_WARNING,
|
|
__(
|
|
'Delivery provider is successfully connected, but offloaded media will not be served until <strong>Deliver Offloaded Media</strong> is enabled. In the meantime, local media is being served if available.',
|
|
'amazon-s3-and-cloudfront'
|
|
)
|
|
);
|
|
}
|
|
|
|
return new AS3CF_Result( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS );
|
|
}
|
|
|
|
/**
|
|
* Test settings for custom delivery domain.
|
|
*
|
|
* @return AS3CF_Result
|
|
*/
|
|
protected function validate_delivery_domain(): AS3CF_Result {
|
|
$delivery_domain = $this->as3cf->get_setting( 'delivery-domain' );
|
|
$enable_delivery_domain = $this->as3cf->get_setting( 'enable-delivery-domain' );
|
|
|
|
if ( ! static::delivery_domain_allowed() ) {
|
|
return new AS3CF_Result( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS );
|
|
}
|
|
|
|
// Custom domain enabled?
|
|
if ( ! $enable_delivery_domain || empty( $delivery_domain ) ) {
|
|
return new AS3CF_Result(
|
|
Validator_Interface::AS3CF_STATUS_MESSAGE_WARNING,
|
|
sprintf(
|
|
__(
|
|
'Offloaded media cannot be delivered from the CDN until a delivery domain is set. <a href="%1$s" target="_blank">Read more</a>',
|
|
'amazon-s3-and-cloudfront'
|
|
),
|
|
static::get_provider_service_quick_start_url() . '#configure-plugin'
|
|
)
|
|
);
|
|
}
|
|
|
|
// Is the custom domain name valid?
|
|
$domain_check = new Domain_Check( $delivery_domain );
|
|
$domain_issue = $domain_check->get_validation_issue();
|
|
if ( ! empty( $domain_issue ) ) {
|
|
return new AS3CF_Result(
|
|
Validator_Interface::AS3CF_STATUS_MESSAGE_ERROR,
|
|
sprintf(
|
|
__(
|
|
'Offloaded media URLs may be broken due to an invalid delivery domain. %1$s <a href="%2$s">How to set a delivery domain</a>',
|
|
'amazon-s3-and-cloudfront'
|
|
),
|
|
$domain_issue,
|
|
static::get_provider_service_quick_start_url() . '#configure-plugin'
|
|
)
|
|
);
|
|
}
|
|
|
|
return new AS3CF_Result( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS );
|
|
}
|
|
|
|
/**
|
|
* Test public and private delivery from bucket using test files.
|
|
*
|
|
* @return AS3CF_Result
|
|
*/
|
|
protected function provider_connection_test(): AS3CF_Result {
|
|
$delivery_check = new Delivery_Check( $this->as3cf );
|
|
|
|
$setup_files = $delivery_check->setup_test_file( false );
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $setup_files->get_error_code() ) {
|
|
return new AS3CF_Result( $setup_files->get_error_code(), $setup_files->get_error_message() );
|
|
}
|
|
|
|
// Verify that the public file is accessible.
|
|
$delivery_issue = $delivery_check->test_public_file_access();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $delivery_issue->get_error_code() ) {
|
|
$delivery_check->remove_test_files();
|
|
|
|
return new AS3CF_Result(
|
|
$delivery_issue->get_error_code(),
|
|
static::get_cannot_access_public_file_desc( $delivery_issue->get_error_message() )
|
|
);
|
|
}
|
|
|
|
$setup_files = $delivery_check->setup_test_file( true );
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $setup_files->get_error_code() ) {
|
|
return new AS3CF_Result( $setup_files->get_error_code(), $setup_files->get_error_message() );
|
|
}
|
|
|
|
// Verify that the private file is accessible.
|
|
$delivery_issue = $delivery_check->test_private_file_access();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $delivery_issue->get_error_code() ) {
|
|
$delivery_check->remove_test_files();
|
|
|
|
return new AS3CF_Result(
|
|
$delivery_issue->get_error_code(),
|
|
static::get_cannot_access_private_file_desc( $delivery_issue->get_error_message() )
|
|
);
|
|
}
|
|
|
|
// Verify that the private file can't be accessed with unsigned URL.
|
|
$delivery_issue = $delivery_check->test_private_file_access_unsigned();
|
|
if ( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS !== $delivery_issue->get_error_code() ) {
|
|
$delivery_check->remove_test_files();
|
|
|
|
return new AS3CF_Result(
|
|
$delivery_issue->get_error_code(),
|
|
static::get_unsigned_url_can_access_private_file_desc()
|
|
);
|
|
}
|
|
|
|
// Ensure all test files are removed.
|
|
$delivery_check->remove_test_files();
|
|
|
|
return new AS3CF_Result( Validator_Interface::AS3CF_STATUS_MESSAGE_SUCCESS );
|
|
}
|
|
|
|
/**
|
|
* Get the name of the actions that are fired when the settings that the validator
|
|
* is responsible for are saved.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function post_save_settings_actions(): array {
|
|
return array( 'as3cf_post_save_settings', 'as3cf_post_update_bucket' );
|
|
}
|
|
}
|