How to set a custom domain name', '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 .= '
';
$message .= join( '
', $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 enable ACLs 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 Read more',
'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 Read more',
'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. Read more',
'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 Block All Public Access is enabled. Edit bucket security',
'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' ),
"" . AS3CF_Utils::human_readable_join( ", ", " or ", $valid_providers ) . ""
)
);
}
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 Deliver Offloaded Media 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. Read more',
'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 How to set a delivery domain',
'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' );
}
}