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:
233
classes/pro/integrations/easy-digital-downloads.php
Normal file
233
classes/pro/integrations/easy-digital-downloads.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Pro\Integrations;
|
||||
|
||||
use Amazon_S3_And_CloudFront_Pro;
|
||||
use DeliciousBrains\WP_Offload_Media\Integrations\Integration;
|
||||
use DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item;
|
||||
use DeliciousBrains\WP_Offload_Media\Pro\Items\Update_Acl_Handler;
|
||||
use Exception;
|
||||
use WP_Error;
|
||||
|
||||
class Easy_Digital_Downloads extends Integration {
|
||||
|
||||
/**
|
||||
* @var Amazon_S3_And_CloudFront_Pro
|
||||
*/
|
||||
protected $as3cf;
|
||||
|
||||
/**
|
||||
* Is installed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_installed(): bool {
|
||||
if ( class_exists( 'Easy_Digital_Downloads' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init integration.
|
||||
*/
|
||||
public function init() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setup() {
|
||||
// Set download method to redirect
|
||||
add_filter( 'edd_file_download_method', array( $this, 'set_download_method' ) );
|
||||
// Disable using symlinks for download.
|
||||
add_filter( 'edd_symlink_file_downloads', array( $this, 'disable_symlink_file_downloads' ) );
|
||||
// Hook into edd_requested_file to swap in the S3 secure URL
|
||||
add_filter( 'edd_requested_file', array( $this, 'get_download_url' ), 10, 3 );
|
||||
// Hook into the save download files metabox to apply the private ACL
|
||||
add_filter( 'edd_metabox_save_edd_download_files', array( $this, 'make_edd_files_private_on_provider' ), 11 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set download method
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function set_download_method( $method ) {
|
||||
return 'redirect';
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable symlink file downloads
|
||||
*
|
||||
* @param bool $use_symlink
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function disable_symlink_file_downloads( $use_symlink ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the secure S3 url for downloads of a file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $download_files
|
||||
* @param string $file_key
|
||||
*
|
||||
* @return bool|string|WP_Error
|
||||
* @throws Exception
|
||||
*
|
||||
* @handles edd_requested_file
|
||||
*/
|
||||
public function get_download_url( $file, $download_files, $file_key ) {
|
||||
global $edd_options;
|
||||
|
||||
if ( empty( $file ) || empty( $download_files ) || empty( $file_key ) || ! is_array( $download_files ) || empty( $download_files[ $file_key ] ) ) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$file_data = $download_files[ $file_key ];
|
||||
$file_name = $file_data['file'];
|
||||
$post_id = $file_data['attachment_id'];
|
||||
$expires = apply_filters( 'as3cf_edd_download_expires', 5 );
|
||||
$headers = apply_filters( 'as3cf_edd_download_headers', array(
|
||||
'ResponseContentDisposition' => 'attachment',
|
||||
), $file_data );
|
||||
|
||||
// Standard Offloaded Media Library Item.
|
||||
$as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
|
||||
|
||||
if ( $as3cf_item && ! is_wp_error( $as3cf_item ) ) {
|
||||
return $as3cf_item->get_provider_url( null, $expires, $headers );
|
||||
}
|
||||
|
||||
// Official EDD S3 addon upload - path should not start with '/', 'http', 'https' or 'ftp' or contain AWSAccessKeyId
|
||||
$url = parse_url( $file_name );
|
||||
|
||||
if ( ( '/' !== $file_name[0] && false === isset( $url['scheme'] ) ) || false !== ( strpos( $file_name, 'AWSAccessKeyId' ) ) ) {
|
||||
$bucket = ( isset( $edd_options['edd_amazon_s3_bucket'] ) ) ? trim( $edd_options['edd_amazon_s3_bucket'] ) : $this->as3cf->get_setting( 'bucket' );
|
||||
$expires = time() + $expires;
|
||||
|
||||
return $this->as3cf->get_provider_client()->get_object_url( $bucket, $file_name, $expires, $headers );
|
||||
}
|
||||
|
||||
// None S3 upload
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply ACL to files uploaded outside of EDD on save of EDD download files
|
||||
*
|
||||
* @param array $files
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function make_edd_files_private_on_provider( $files ) {
|
||||
global $post;
|
||||
|
||||
// get existing files attached to download
|
||||
$old_files = edd_get_download_files( $post->ID );
|
||||
$old_attachment_ids = wp_list_pluck( $old_files, 'attachment_id' );
|
||||
$new_attachment_ids = array();
|
||||
|
||||
/** @var Update_Acl_Handler $acl_handler */
|
||||
$acl_handler = $this->as3cf->get_item_handler( Update_Acl_Handler::get_item_handler_key_name() );
|
||||
|
||||
if ( is_array( $files ) ) {
|
||||
foreach ( $files as $file ) {
|
||||
$new_attachment_ids[] = $file['attachment_id'];
|
||||
|
||||
$as3cf_item = Media_Library_Item::get_by_source_id( $file['attachment_id'] );
|
||||
|
||||
if ( ! $as3cf_item ) {
|
||||
// not offloaded, ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $as3cf_item->is_private() ) {
|
||||
// already private
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $this->as3cf->is_pro_plugin_setup( true ) ) {
|
||||
|
||||
$options = array(
|
||||
'object_keys' => array( null ),
|
||||
'set_private' => true,
|
||||
);
|
||||
$result = $acl_handler->handle( $as3cf_item, $options );
|
||||
|
||||
if ( true === $result ) {
|
||||
$this->as3cf->make_acl_admin_notice( $as3cf_item );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determine which attachments have been removed and maybe set to public
|
||||
$removed_attachment_ids = array_diff( $old_attachment_ids, $new_attachment_ids );
|
||||
$this->maybe_make_removed_edd_files_public( $removed_attachment_ids, $post->ID );
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove public ACL from attachments removed from a download
|
||||
* as long as they are not attached to any other downloads
|
||||
*
|
||||
* @param array $attachment_ids
|
||||
* @param integer $download_id
|
||||
*/
|
||||
function maybe_make_removed_edd_files_public( $attachment_ids, $download_id ) {
|
||||
global $wpdb;
|
||||
|
||||
/** @var Update_Acl_Handler $acl_handler */
|
||||
$acl_handler = $this->as3cf->get_item_handler( Update_Acl_Handler::get_item_handler_key_name() );
|
||||
|
||||
foreach ( $attachment_ids as $id ) {
|
||||
$as3cf_item = Media_Library_Item::get_by_source_id( $id );
|
||||
|
||||
if ( ! $as3cf_item ) {
|
||||
// Not offloaded, ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $as3cf_item->is_private() ) {
|
||||
// already public
|
||||
continue;
|
||||
}
|
||||
|
||||
$length = strlen( $id );
|
||||
|
||||
// check the attachment isn't used by other downloads
|
||||
$sql = "
|
||||
SELECT COUNT(*)
|
||||
FROM `{$wpdb->prefix}postmeta`
|
||||
WHERE `{$wpdb->prefix}postmeta`.`meta_key` = 'edd_download_files'
|
||||
AND `{$wpdb->prefix}postmeta`.`post_id` != {$download_id}
|
||||
AND `{$wpdb->prefix}postmeta`.`meta_value` LIKE '%s:13:\"attachment_id\";s:{$length}:\"{$id}\"%'
|
||||
";
|
||||
|
||||
if ( $wpdb->get_var( $sql ) > 0 ) {
|
||||
// used for another download, ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
// set acl to public
|
||||
$options = array(
|
||||
'object_keys' => array( null ),
|
||||
'set_private' => false,
|
||||
);
|
||||
$result = $acl_handler->handle( $as3cf_item, $options );
|
||||
|
||||
if ( true === $result ) {
|
||||
$this->as3cf->make_acl_admin_notice( $as3cf_item );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user