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
211 lines
5.9 KiB
PHP
211 lines
5.9 KiB
PHP
<?php
|
|
/**
|
|
* Update File Sizes
|
|
*
|
|
* @package amazon-s3-and-cloudfront
|
|
* @subpackage Classes/Upgrades/File-Sizes
|
|
* @copyright Copyright (c) 2015, Delicious Brains
|
|
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
|
* @since 0.9.3
|
|
*/
|
|
|
|
namespace DeliciousBrains\WP_Offload_Media\Upgrades;
|
|
|
|
use AS3CF_Error;
|
|
use AS3CF_Utils;
|
|
use DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item;
|
|
use Exception;
|
|
|
|
/**
|
|
* Upgrade_File_Sizes Class
|
|
*
|
|
* This class handles updating the file sizes in the meta data
|
|
* for attachments that have been removed from the local server
|
|
*
|
|
* @since 0.9.3
|
|
*/
|
|
class Upgrade_File_Sizes extends Upgrade {
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected $upgrade_id = 2;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $upgrade_name = 'file_sizes';
|
|
|
|
/**
|
|
* @var string 'metadata', 'attachment'
|
|
*/
|
|
protected $upgrade_type = 'attachments';
|
|
|
|
/**
|
|
* Get running update text.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_running_update_text() {
|
|
return __( 'and updating the metadata with the sizes of files that have been removed from the server. This will allow us to serve the correct size for media items and the total space used in Multisite subsites.', 'amazon-s3-and-cloudfront' );
|
|
}
|
|
|
|
/**
|
|
* Get the total file sizes for an attachment and associated files.
|
|
*
|
|
* @param mixed $item
|
|
*
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
protected function upgrade_item( $item ) {
|
|
$provider_object = AS3CF_Utils::maybe_fix_serialized_string( $item->provider_object );
|
|
$fixed = $item->provider_object !== $provider_object;
|
|
|
|
$provider_object = AS3CF_Utils::maybe_unserialize( $provider_object );
|
|
|
|
if ( false === $provider_object ) {
|
|
AS3CF_Error::log( 'Failed to unserialize offload meta for attachment ' . $item->ID . ': ' . $item->provider_object );
|
|
$this->error_count++;
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( $fixed ) {
|
|
if ( update_post_meta( $item->ID, 'amazonS3_info', $provider_object ) ) {
|
|
$msg = sprintf( __( 'Fixed legacy amazonS3_info metadata when updating file size metadata, please check bucket and path for attachment ID %1$s', 'amazon-s3-and-cloudfront' ), $item->ID );
|
|
AS3CF_Error::log( $msg );
|
|
} else {
|
|
AS3CF_Error::log( 'Failed to fix broken serialized legacy offload metadata for attachment ' . $item->ID . ': ' . $item->provider_object );
|
|
$this->error_count++;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Using Media_Library_Item::get_by_source_id falls back to legacy metadata and substitutes in defaults and potentially missing values.
|
|
$as3cf_item = Media_Library_Item::get_by_source_id( $item->ID );
|
|
|
|
if ( ! $as3cf_item ) {
|
|
AS3CF_Error::log( 'Could not construct item for attachment with ID ' . $item->ID . ' from legacy offload metadata.' );
|
|
$this->error_count++;
|
|
|
|
return false;
|
|
}
|
|
|
|
// $as3cf_item can't exist without a region value, so we can just use it here.
|
|
$region = $as3cf_item->region();
|
|
|
|
$provider_client = $this->as3cf->get_provider_client( $region, true );
|
|
$main_file = $provider_object['key'];
|
|
|
|
$ext = pathinfo( $main_file, PATHINFO_EXTENSION );
|
|
$prefix = trailingslashit( dirname( $provider_object['key'] ) );
|
|
|
|
// Used to search S3 for all files related to an attachment
|
|
$search_prefix = $prefix . wp_basename( $main_file, ".$ext" );
|
|
|
|
$args = array(
|
|
'Bucket' => $provider_object['bucket'],
|
|
'Prefix' => $search_prefix,
|
|
);
|
|
|
|
try {
|
|
// List objects for the attachment
|
|
$result = $provider_client->list_objects( $args );
|
|
} catch ( Exception $e ) {
|
|
AS3CF_Error::log( 'Error listing objects of prefix ' . $search_prefix . ' for attachment ' . $item->ID . ' in bucket: ' . $e->getMessage() );
|
|
$this->error_count++;
|
|
|
|
return false;
|
|
}
|
|
|
|
$file_size_total = 0;
|
|
$main_file_size = 0;
|
|
|
|
if ( ! empty( $result['Contents'] ) ) {
|
|
foreach ( $result['Contents'] as $object ) {
|
|
if ( ! isset( $object['Size'] ) ) {
|
|
continue;
|
|
}
|
|
|
|
$size = $object['Size'];
|
|
|
|
// Increment the total size of files for the attachment
|
|
$file_size_total += $size;
|
|
|
|
if ( $object['Key'] === $main_file ) {
|
|
// Record the size of the main file
|
|
$main_file_size = $size;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0 === $file_size_total ) {
|
|
AS3CF_Error::log( 'Total file size for the attachment is 0: ' . $item->ID );
|
|
$this->error_count++;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Update the main file size for the attachment
|
|
$meta = get_post_meta( $item->ID, '_wp_attachment_metadata', true );
|
|
$meta['filesize'] = $main_file_size;
|
|
update_post_meta( $item->ID, '_wp_attachment_metadata', $meta );
|
|
|
|
// Add the total file size for all image sizes
|
|
update_post_meta( $item->ID, 'wpos3_filesize_total', $file_size_total );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get all attachments removed from the server.
|
|
*
|
|
* @param string $prefix
|
|
* @param int $limit
|
|
* @param bool|mixed $offset
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function get_items_to_process( $prefix, $limit, $offset = false ) {
|
|
$all_attachments = $this->get_provider_attachments( $prefix, $limit );
|
|
$attachments = array();
|
|
|
|
foreach ( $all_attachments as $attachment ) {
|
|
if ( ! file_exists( get_attached_file( $attachment->ID, true ) ) ) {
|
|
$attachments[] = $attachment;
|
|
}
|
|
}
|
|
|
|
return $attachments;
|
|
}
|
|
|
|
/**
|
|
* Wrapper for database call to get attachments uploaded to S3,
|
|
* that don't have the file size meta added already
|
|
*
|
|
* @param string $prefix
|
|
* @param null|int $limit
|
|
*
|
|
* @return mixed
|
|
*/
|
|
protected function get_provider_attachments( $prefix, $limit = null ) {
|
|
global $wpdb;
|
|
|
|
$sql = "SELECT pm1.`post_id` as `ID`, pm1.`meta_value` AS 'provider_object'
|
|
FROM `{$prefix}postmeta` pm1
|
|
LEFT OUTER JOIN `{$prefix}postmeta` pm2
|
|
ON pm1.`post_id` = pm2.`post_id`
|
|
AND pm2.`meta_key` = 'wpos3_filesize_total'
|
|
WHERE pm1.`meta_key` = 'amazonS3_info'
|
|
AND pm2.`post_id` is null";
|
|
|
|
if ( $limit && $limit > 0 ) {
|
|
$sql .= sprintf( ' LIMIT %d', (int) $limit );
|
|
}
|
|
|
|
return $wpdb->get_results( $sql, OBJECT );
|
|
}
|
|
}
|