rebase from live enviornment
This commit is contained in:
513
wp/plugins/imagify/inc/3rd-party/amazon-s3-and-cloudfront/classes/CDN/WP/AS3.php
vendored
Normal file
513
wp/plugins/imagify/inc/3rd-party/amazon-s3-and-cloudfront/classes/CDN/WP/AS3.php
vendored
Normal file
@@ -0,0 +1,513 @@
|
||||
<?php
|
||||
namespace Imagify\ThirdParty\AS3CF\CDN\WP;
|
||||
|
||||
use Imagify\CDN\PushCDNInterface;
|
||||
use Imagify\Context\ContextInterface;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* AS3 CDN for WP context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class AS3 implements PushCDNInterface {
|
||||
|
||||
/**
|
||||
* The media ID.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Tell if we’re playing in WP 5.3’s garden.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9.8
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_wp53;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $media_id The media ID.
|
||||
*/
|
||||
public function __construct( $media_id ) {
|
||||
$this->id = (int) $media_id;
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the CDN is ready (not necessarily reachable).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
global $as3cf;
|
||||
static $is;
|
||||
|
||||
if ( ! isset( $is ) ) {
|
||||
$is = $as3cf && $as3cf->is_plugin_setup();
|
||||
}
|
||||
|
||||
return $is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the media is on the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function media_is_on_cdn() {
|
||||
return (bool) $this->get_cdn_info();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files from the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $file_paths A list of file paths.
|
||||
* @return bool|\WP_Error True on success. A \WP_error object on failure.
|
||||
*/
|
||||
public function get_files_from_cdn( $file_paths ) {
|
||||
global $as3cf;
|
||||
|
||||
if ( ! $this->is_ready() ) {
|
||||
return new \WP_Error( 'not_ready', __( 'CDN is not set up.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$cdn_info = $this->get_cdn_info();
|
||||
|
||||
if ( ! $cdn_info ) {
|
||||
// The media is not on the CDN.
|
||||
return new \WP_Error( 'not_on_cdn', __( 'This media could not be found on the CDN.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$directory = $this->filesystem->dir_path( $cdn_info['key'] );
|
||||
$directory = $this->filesystem->is_root( $directory ) ? '' : $directory;
|
||||
$new_method = method_exists( $as3cf->plugin_compat, 'copy_s3_file_to_server' );
|
||||
$errors = [];
|
||||
|
||||
foreach ( $file_paths as $file_path ) {
|
||||
$cdn_info['key'] = $directory . $this->filesystem->file_name( $file_path );
|
||||
|
||||
// Retrieve file from the CDN.
|
||||
if ( $new_method ) {
|
||||
$as3cf->plugin_compat->copy_s3_file_to_server( $cdn_info, $file_path );
|
||||
} else {
|
||||
$as3cf->plugin_compat->copy_provider_file_to_server( $cdn_info, $file_path );
|
||||
}
|
||||
|
||||
if ( ! $this->filesystem->exists( $file_path ) ) {
|
||||
$errors[] = $file_path;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $errors ) {
|
||||
$nbr_errors = count( $errors );
|
||||
$errors_txt = array_map( [ $this->filesystem, 'make_path_relative' ], $errors );
|
||||
$errors_txt = wp_sprintf_l( '%l', $errors_txt );
|
||||
|
||||
return new \WP_Error(
|
||||
'not_retrieved',
|
||||
sprintf(
|
||||
/* translators: %s is a list of file paths. */
|
||||
_n( 'The following file could not be retrieved from the CDN: %s.', 'The following files could not be retrieved from the CDN: %s.', $nbr_errors, 'imagify' ),
|
||||
$errors_txt
|
||||
),
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove files from the CDN.
|
||||
* Don't use this to empty a folder.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $file_paths A list of file paths. Those paths are not necessary absolute, and can be also file names.
|
||||
* @return bool|\WP_Error True on success. A \WP_error object on failure.
|
||||
*/
|
||||
public function remove_files_from_cdn( $file_paths ) {
|
||||
global $as3cf;
|
||||
|
||||
if ( ! $this->is_ready() ) {
|
||||
return new \WP_Error( 'not_ready', __( 'CDN is not set up.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$cdn_info = $this->get_cdn_info();
|
||||
|
||||
if ( ! $cdn_info ) {
|
||||
// The media is not on the CDN.
|
||||
return new \WP_Error( 'not_on_cdn', __( 'This media could not be found on the CDN.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$directory = $this->filesystem->dir_path( $cdn_info['key'] );
|
||||
$directory = $this->filesystem->is_root( $directory ) ? '' : $directory;
|
||||
|
||||
if ( method_exists( $as3cf, 'get_s3object_region' ) ) {
|
||||
$region = $as3cf->get_s3object_region( $cdn_info );
|
||||
} else {
|
||||
$region = $as3cf->get_provider_object_region( $cdn_info );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $region ) ) {
|
||||
$region = '';
|
||||
}
|
||||
|
||||
$to_remove = [];
|
||||
|
||||
foreach ( $file_paths as $file_path ) {
|
||||
$to_remove[] = [
|
||||
'Key' => $directory . $this->filesystem->file_name( $file_path ),
|
||||
];
|
||||
}
|
||||
|
||||
if ( method_exists( $as3cf, 'delete_s3_objects' ) ) {
|
||||
$result = $as3cf->delete_s3_objects( $region, $cdn_info['bucket'], $to_remove, false, false, false );
|
||||
} else {
|
||||
$result = $as3cf->delete_objects( $region, $cdn_info['bucket'], $to_remove, false, false, false );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return new \WP_Error( 'deletion_failed', __( 'File(s) could not be removed from the CDN.', 'imagify' ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all files from a media to the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $is_new_upload Tell if the current media is a new upload. If not, it means it's a media being regenerated, restored, etc.
|
||||
* @return bool|\WP_Error True/False if sent or not. A \WP_error object on failure.
|
||||
*/
|
||||
public function send_to_cdn( $is_new_upload ) {
|
||||
global $as3cf;
|
||||
|
||||
if ( ! $this->is_ready() ) {
|
||||
return new \WP_Error( 'not_ready', __( 'CDN is not set up.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $this->can_send_to_cdn( $is_new_upload ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the missing files from the CDN: we must send all of them at once, even those that have not been modified.
|
||||
$file_paths = \AS3CF_Utils::get_attachment_file_paths( $this->id, false );
|
||||
|
||||
if ( $file_paths ) {
|
||||
foreach ( $file_paths as $size => $file_path ) {
|
||||
if ( $this->filesystem->exists( $file_path ) ) {
|
||||
// Keep only the files that don't exist.
|
||||
unset( $file_paths[ $size ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $file_paths ) {
|
||||
$result = $this->get_files_from_cdn( $file_paths );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
$remove_local_files = $this->should_delete_files( $is_new_upload );
|
||||
|
||||
if ( ! $is_new_upload ) {
|
||||
if ( $remove_local_files ) {
|
||||
// Force files deletion when not a new media.
|
||||
add_filter( 'as3cf_get_setting', [ $this, 'force_local_file_removal_setting' ], 100, 2 );
|
||||
add_filter( 'as3cf_setting_remove-local-file', 'imagify_return_true', 100 );
|
||||
} else {
|
||||
// Force to keep the files when not a new media.
|
||||
add_filter( 'as3cf_get_setting', [ $this, 'force_local_file_keep_setting' ], 100, 2 );
|
||||
add_filter( 'as3cf_setting_remove-local-file', 'imagify_return_false', 100 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( method_exists( $as3cf, 'upload_attachment_to_s3' ) ) {
|
||||
$result = $as3cf->upload_attachment_to_s3( $this->id, null, null, false, $remove_local_files );
|
||||
} else {
|
||||
$result = $as3cf->upload_attachment( $this->id, null, null, false, $remove_local_files );
|
||||
}
|
||||
|
||||
if ( ! $is_new_upload ) {
|
||||
if ( $remove_local_files ) {
|
||||
remove_filter( 'as3cf_get_setting', [ $this, 'force_local_file_removal_setting' ], 100 );
|
||||
remove_filter( 'as3cf_setting_remove-local-file', 'imagify_return_true', 100 );
|
||||
} else {
|
||||
remove_filter( 'as3cf_get_setting', [ $this, 'force_local_file_keep_setting' ], 100 );
|
||||
remove_filter( 'as3cf_setting_remove-local-file', 'imagify_return_false', 100 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file URL.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_name Name of the file. Leave empty for the full size file.
|
||||
* @return string URL to the file.
|
||||
*/
|
||||
public function get_file_url( $file_name = false ) {
|
||||
$file_url = wp_get_attachment_url( $this->id );
|
||||
|
||||
if ( $file_name ) {
|
||||
// It's not the full size.
|
||||
$file_url = $this->filesystem->dir_path( $file_url ) . $file_name;
|
||||
}
|
||||
|
||||
return $file_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file path.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_name Name of the file. Leave empty for the full size file. Use 'original' to get the path to the original file.
|
||||
* @return string Path to the file.
|
||||
*/
|
||||
public function get_file_path( $file_name = false ) {
|
||||
if ( ! $file_name ) {
|
||||
// Full size.
|
||||
return get_attached_file( $this->id, true );
|
||||
}
|
||||
|
||||
if ( 'original' === $file_name ) {
|
||||
// Original file.
|
||||
if ( $this->is_wp_53() ) {
|
||||
// `wp_get_original_image_path()` may return false.
|
||||
$file_path = wp_get_original_image_path( $this->id );
|
||||
} else {
|
||||
$file_path = false;
|
||||
}
|
||||
|
||||
if ( ! $file_path ) {
|
||||
$file_path = get_attached_file( $this->id, true );
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
// Thumbnail.
|
||||
$file_path = get_attached_file( $this->id, true );
|
||||
$file_path = $this->filesystem->dir_path( $file_path ) . $file_name;
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INTERNAL TOOLS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Filter the CDN setting 'remove-local-file': this is used to force deletion when the media is not a new one.
|
||||
* Caution to not use $this->should_delete_files() when this filter is used!
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $setting The setting value.
|
||||
* @param string $key The setting name.
|
||||
* @return bool
|
||||
*/
|
||||
public function force_local_file_removal_setting( $setting, $key ) {
|
||||
if ( 'remove-local-file' === $key ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the CDN setting 'remove-local-file': this is used to force not-deletion when the media is not a new one.
|
||||
* Caution to not use $this->should_delete_files() when this filter is used!
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $setting The setting value.
|
||||
* @param string $key The setting name.
|
||||
* @return bool
|
||||
*/
|
||||
public function force_local_file_keep_setting( $setting, $key ) {
|
||||
if ( 'remove-local-file' === $key ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a media is stored on the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array|bool The CDN info on success. False if the media is not on the CDN.
|
||||
*/
|
||||
protected function get_cdn_info() {
|
||||
global $as3cf;
|
||||
|
||||
if ( ! $as3cf ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( method_exists( $as3cf, 'get_attachment_s3_info' ) ) {
|
||||
return $as3cf->get_attachment_s3_info( $this->id );
|
||||
}
|
||||
|
||||
return $as3cf->get_attachment_provider_info( $this->id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a media can (and should) be sent to the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $is_new_upload Tell if the current media is a new upload. If not, it means it's a media being regenerated, restored, etc.
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_send_to_cdn( $is_new_upload ) {
|
||||
global $as3cf;
|
||||
static $can = [];
|
||||
static $cdn_setting;
|
||||
|
||||
if ( isset( $can[ $this->id ] ) ) {
|
||||
return $can[ $this->id ];
|
||||
}
|
||||
|
||||
if ( ! $this->is_ready() ) {
|
||||
$can[ $this->id ] = false;
|
||||
return $can[ $this->id ];
|
||||
}
|
||||
|
||||
if ( ! isset( $cdn_setting ) ) {
|
||||
$cdn_setting = $as3cf && $as3cf->get_setting( 'copy-to-s3' );
|
||||
}
|
||||
|
||||
// The CDN is set up: test if the media is on it.
|
||||
$can[ $this->id ] = $this->media_is_on_cdn();
|
||||
|
||||
if ( $can[ $this->id ] && $is_new_upload ) {
|
||||
// Use the CDN setting to tell if we're allowed to send the files (should be true since it's a new upload and it's already there).
|
||||
$can[ $this->id ] = $cdn_setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a media can (and should) be sent to the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $can True if the media can be sent. False otherwize.
|
||||
* @param PushCDNInterface $cdn The CDN instance.
|
||||
* @param bool $cdn_setting CDN setting that tells if a new media can be sent to the CDN.
|
||||
* @param bool $is_new_upload Tell if the current media is a new upload. If not, it means it's a media being regenerated, restored, etc.
|
||||
*/
|
||||
$can[ $this->id ] = (bool) apply_filters( 'imagify_can_send_to_cdn', $can[ $this->id ], $this, $cdn_setting, $is_new_upload );
|
||||
|
||||
return $can[ $this->id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the files should be deleted after optimization.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $is_new_upload Tell if the current media is a new upload. If not, it means it's a media being regenerated, restored, etc.
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_delete_files( $is_new_upload ) {
|
||||
global $as3cf;
|
||||
|
||||
if ( $is_new_upload ) {
|
||||
return (bool) $as3cf->get_setting( 'remove-local-file' );
|
||||
}
|
||||
|
||||
// If the attachment has a 'filesize' metadata, that means the local files are meant to be deleted.
|
||||
return (bool) get_post_meta( $this->id, 'wpos3_filesize_total', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we’re playing in WP 5.3’s garden.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_wp_53() {
|
||||
if ( isset( $this->is_wp53 ) ) {
|
||||
return $this->is_wp53;
|
||||
}
|
||||
|
||||
$this->is_wp53 = function_exists( 'wp_get_original_image_path' );
|
||||
|
||||
return $this->is_wp53;
|
||||
}
|
||||
}
|
||||
631
wp/plugins/imagify/inc/3rd-party/amazon-s3-and-cloudfront/classes/Main.php
vendored
Normal file
631
wp/plugins/imagify/inc/3rd-party/amazon-s3-and-cloudfront/classes/Main.php
vendored
Normal file
@@ -0,0 +1,631 @@
|
||||
<?php
|
||||
namespace Imagify\ThirdParty\AS3CF;
|
||||
|
||||
use \Imagify\Optimization\File;
|
||||
use \Imagify\ThirdParty\AS3CF\CDN\WP\AS3 as CDN;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Imagify WP Offload S3 class.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Main extends \Imagify_AS3CF_Deprecated {
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* AS3CF settings.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $s3_settings;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var \Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @since 1.6.6
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected function __construct() {
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @since 1.6.6
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function init() {
|
||||
static $done = false;
|
||||
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
$done = true;
|
||||
|
||||
/**
|
||||
* WebP images to display with a <picture> tag.
|
||||
*/
|
||||
add_action( 'as3cf_init', [ $this, 'store_s3_settings' ] );
|
||||
add_filter( 'imagify_webp_picture_process_image', [ $this, 'picture_tag_webp_image' ] );
|
||||
|
||||
/**
|
||||
* Register CDN.
|
||||
*/
|
||||
add_filter( 'imagify_cdn', [ $this, 'register_cdn' ], 8, 3 );
|
||||
|
||||
/**
|
||||
* Optimization process.
|
||||
*/
|
||||
add_filter( 'imagify_before_optimize_size', [ $this, 'maybe_copy_file_from_cdn_before_optimization' ], 8, 6 );
|
||||
add_action( 'imagify_after_optimize', [ $this, 'maybe_send_media_to_cdn_after_optimization' ], 8, 2 );
|
||||
|
||||
/**
|
||||
* Restoration process.
|
||||
*/
|
||||
add_action( 'imagify_after_restore_media', [ $this, 'maybe_send_media_to_cdn_after_restore' ], 8, 4 );
|
||||
|
||||
/**
|
||||
* WebP support.
|
||||
*/
|
||||
add_filter( 'as3cf_attachment_file_paths', [ $this, 'add_webp_images_to_attachment' ], 8, 3 );
|
||||
add_filter( 'mime_types', [ $this, 'add_webp_support' ] );
|
||||
|
||||
/**
|
||||
* Redirections.
|
||||
*/
|
||||
add_filter( 'imagify_redirect_to', [ $this, 'redirect_referrer' ] );
|
||||
|
||||
/**
|
||||
* Stats.
|
||||
*/
|
||||
add_filter( 'imagify_total_attachment_filesize', [ $this, 'add_stats_for_s3_files' ], 8, 4 );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION PROCESS ==================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* On AS3CF init, store its settings.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param \Amazon_S3_And_CloudFront $as3cf AS3CF’s main instance.
|
||||
*/
|
||||
public function store_s3_settings( $as3cf ) {
|
||||
if ( method_exists( $as3cf, 'get_settings' ) ) {
|
||||
$this->store_s3_settings = (array) $as3cf->get_settings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WebP images to display with a <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see \Imagify\Webp\Picture\Display->process_image()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data An array of data for this image.
|
||||
* @return array
|
||||
*/
|
||||
public function picture_tag_webp_image( $data ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! empty( $data['src']['webp_path'] ) ) {
|
||||
// The file is local.
|
||||
return $data;
|
||||
}
|
||||
|
||||
$match = $this->is_s3_url( $data['src']['url'] );
|
||||
|
||||
if ( ! $match ) {
|
||||
// The file is not on S3.
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Get the image ID.
|
||||
$post_id = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
|
||||
// We use only year/month + filename, we should not have any subdir between them for the main file.
|
||||
$match['year_month'] . $match['filename']
|
||||
)
|
||||
);
|
||||
|
||||
if ( $post_id <= 0 ) {
|
||||
// Not in the database.
|
||||
return $data;
|
||||
}
|
||||
|
||||
$s3_info = get_post_meta( $post_id, 'amazonS3_info', true );
|
||||
$imagify_data = get_post_meta( $post_id, '_imagify_data', true );
|
||||
|
||||
if ( ! $s3_info || ! $imagify_data ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$webp_size_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::WEBP_SUFFIX' );
|
||||
$webp_size_name = 'full' . $webp_size_suffix;
|
||||
|
||||
if ( ! empty( $imagify_data['sizes'][ $webp_size_name ]['success'] ) ) {
|
||||
// We have a WebP image.
|
||||
$data['src']['webp_exists'] = true;
|
||||
}
|
||||
|
||||
if ( empty( $data['srcset'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$meta_data = get_post_meta( $post_id, '_wp_attachment_metadata', true );
|
||||
|
||||
if ( empty( $meta_data['sizes'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Ease the search for corresponding file name.
|
||||
$size_files = [];
|
||||
|
||||
foreach ( $meta_data['sizes'] as $size_name => $size_data ) {
|
||||
$size_files[ $size_data['file'] ] = $size_name;
|
||||
}
|
||||
|
||||
// Look for a corresponding size name.
|
||||
foreach ( $data['srcset'] as $i => $srcset_data ) {
|
||||
if ( empty( $srcset_data['webp_url'] ) ) {
|
||||
// Not a supported image format.
|
||||
continue;
|
||||
}
|
||||
if ( ! empty( $srcset_data['webp_path'] ) ) {
|
||||
// The file is local.
|
||||
continue;
|
||||
}
|
||||
|
||||
$match = $this->is_s3_url( $srcset_data['url'] );
|
||||
|
||||
if ( ! $match ) {
|
||||
// Not on S3.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try with no subdirs.
|
||||
$filename = $match['filename'];
|
||||
|
||||
if ( isset( $size_files[ $filename ] ) ) {
|
||||
$size_name = $size_files[ $filename ];
|
||||
} else {
|
||||
// Try with subdirs.
|
||||
$filename = $match['subdirs'] . $match['filename'];
|
||||
|
||||
if ( isset( $size_files[ $filename ] ) ) {
|
||||
$size_name = $size_files[ $filename ];
|
||||
} elseif ( preg_match( '@/\d+/$@', $match['subdirs'] ) ) {
|
||||
// Last try: the subdirs may contain the S3 versioning. If not the case, we can still build a pyramid with this code.
|
||||
$filename = preg_replace( '@/\d+/$@', '/', $match['subdirs'] ) . $match['filename'];
|
||||
|
||||
if ( isset( $size_files[ $filename ] ) ) {
|
||||
$size_name = $size_files[ $filename ];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$webp_size_name = $size_name . $webp_size_suffix;
|
||||
|
||||
if ( ! empty( $imagify_data['sizes'][ $webp_size_name ]['success'] ) ) {
|
||||
// We have a WebP image.
|
||||
$data['srcset'][ $i ]['webp_exists'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The CDN to use for this media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool|PushCDNInterface $cdn A PushCDNInterface instance. False if no CDN is used.
|
||||
* @param int $media_id The media ID.
|
||||
* @param ContextInterface $context The context object.
|
||||
*/
|
||||
public function register_cdn( $cdn, $media_id, $context ) {
|
||||
if ( 'wp' !== $context->get_name() ) {
|
||||
return $cdn;
|
||||
}
|
||||
if ( $cdn instanceof PushCDNInterface ) {
|
||||
return $cdn;
|
||||
}
|
||||
|
||||
return new CDN( $media_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Before performing a file optimization, download the file from the CDN if it is missing.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param null|\WP_Error $response Null by default.
|
||||
* @param ProcessInterface $process The optimization process instance.
|
||||
* @param File $file The file instance. If $webp is true, $file references the non-webp file.
|
||||
* @param string $thumb_size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @param bool $webp The image will be converted to WebP.
|
||||
* @return null|\WP_Error Null. A \WP_Error object on error.
|
||||
*/
|
||||
public function maybe_copy_file_from_cdn_before_optimization( $response, $process, $file, $thumb_size, $optimization_level, $webp ) {
|
||||
if ( is_wp_error( $response ) || 'wp' !== $process->get_media()->get_context() ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$media = $process->get_media();
|
||||
$cdn = $media->get_cdn();
|
||||
|
||||
if ( ! $cdn instanceof CDN ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( $this->filesystem->exists( $file->get_path() ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Get files from the CDN.
|
||||
$result = $cdn->get_files_from_cdn( [ $file->get_path() ] );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* After performing a media optimization:
|
||||
* - Save some data,
|
||||
* - Upload the files to the CDN,
|
||||
* - Maybe delete them from the server.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed.
|
||||
*/
|
||||
public function maybe_send_media_to_cdn_after_optimization( $process, $item ) {
|
||||
if ( 'wp' !== $process->get_media()->get_context() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$media = $process->get_media();
|
||||
$cdn = $media->get_cdn();
|
||||
|
||||
if ( ! $cdn instanceof CDN ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cdn->send_to_cdn( ! empty( $item['data']['is_new_upload'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* After restoring a media:
|
||||
* - Save some data,
|
||||
* - Upload the files to the CDN,
|
||||
* - Maybe delete WebP files from the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param bool|WP_Error $response The result of the operation: true on success, a WP_Error object on failure.
|
||||
* @param array $files The list of files, before restoring them.
|
||||
* @param array $data The optimization data, before deleting it.
|
||||
*/
|
||||
public function maybe_send_media_to_cdn_after_restore( $process, $response, $files, $data ) {
|
||||
if ( 'wp' !== $process->get_media()->get_context() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$media = $process->get_media();
|
||||
$cdn = $media->get_cdn();
|
||||
|
||||
if ( ! $cdn instanceof CDN ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error_code = $response->get_error_code();
|
||||
|
||||
if ( 'copy_failed' === $error_code ) {
|
||||
// No files have been restored.
|
||||
return;
|
||||
}
|
||||
|
||||
// No thumbnails left?
|
||||
}
|
||||
|
||||
$cdn->send_to_cdn( false );
|
||||
|
||||
// Remove WebP files from CDN.
|
||||
$webp_files = [];
|
||||
|
||||
if ( $files ) {
|
||||
// Get the paths to the WebP files.
|
||||
foreach ( $files as $size_name => $file ) {
|
||||
$webp_size_name = $size_name . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( empty( $data['sizes'][ $webp_size_name ]['success'] ) ) {
|
||||
// This size has no WebP version.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 0 === strpos( $file['mime-type'], 'image/' ) ) {
|
||||
$webp_file = new File( $file['path'] );
|
||||
|
||||
if ( ! $webp_file->is_webp() ) {
|
||||
$webp_files[] = $webp_file->get_path_to_webp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $webp_files ) {
|
||||
$cdn->remove_files_from_cdn( $webp_files );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION PROCESS ==================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add the WebP files to the list of files that the CDN must handle.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $paths A list of file paths, keyed by size name. 'file' for the full size. Includes a 'backup' size and a 'thumb' size.
|
||||
* @param int $attachment_id The media ID.
|
||||
* @param array $metadata The attachment meta data.
|
||||
* @return array
|
||||
*/
|
||||
public function add_webp_images_to_attachment( $paths, $attachment_id, $metadata ) {
|
||||
if ( ! $paths ) {
|
||||
// ¯\(°_o)/¯.
|
||||
return $paths;
|
||||
}
|
||||
|
||||
$process = imagify_get_optimization_process( $attachment_id, 'wp' );
|
||||
|
||||
if ( ! $process->is_valid() ) {
|
||||
return $paths;
|
||||
}
|
||||
|
||||
$media = $process->get_media();
|
||||
|
||||
if ( ! $media->is_image() ) {
|
||||
return $paths;
|
||||
}
|
||||
|
||||
// Use the optimization data (the files may not be on the server).
|
||||
$data = $process->get_data()->get_optimization_data();
|
||||
|
||||
if ( empty( $data['sizes'] ) ) {
|
||||
return $paths;
|
||||
}
|
||||
|
||||
foreach ( $paths as $size_name => $file_path ) {
|
||||
if ( 'thumb' === $size_name || 'backup' === $size_name || $process->is_size_webp( $size_name ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'file' === $size_name ) {
|
||||
$size_name = 'full';
|
||||
}
|
||||
|
||||
$webp_size_name = $size_name . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( empty( $data['sizes'][ $webp_size_name ]['success'] ) ) {
|
||||
// This size has no WebP version.
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = new File( $file_path );
|
||||
|
||||
if ( ! $file->is_webp() ) {
|
||||
$paths[ $webp_size_name ] = $file->get_path_to_webp();
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add WebP format to the list of allowed mime types.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @see get_allowed_mime_types()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $mime_types A list of mime types.
|
||||
* @return array
|
||||
*/
|
||||
public function add_webp_support( $mime_types ) {
|
||||
$mime_types['webp'] = 'image/webp';
|
||||
return $mime_types;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** VARIOUS HOOKS =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* After a non-ajax optimization, remove some unnecessary arguments from the referrer used for the redirection.
|
||||
* Those arguments don't break anything, they're just not relevant and display obsolete admin notices.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $redirect The URL to redirect to.
|
||||
* @return string
|
||||
*/
|
||||
public function redirect_referrer( $redirect ) {
|
||||
return remove_query_arg( [ 'as3cfpro-action', 'as3cf_id', 'errors', 'count' ], $redirect );
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the file sizes and the number of thumbnails for files that are only on S3.
|
||||
*
|
||||
* @since 1.6.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $size_and_count False by default.
|
||||
* @param int $image_id The attachment ID.
|
||||
* @param array $files An array of file paths with thumbnail sizes as keys.
|
||||
* @param array $image_ids An array of all attachment IDs.
|
||||
* @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails).
|
||||
*/
|
||||
public function add_stats_for_s3_files( $size_and_count, $image_id, $files, $image_ids ) {
|
||||
static $data;
|
||||
|
||||
if ( is_array( $size_and_count ) ) {
|
||||
return $size_and_count;
|
||||
}
|
||||
|
||||
if ( $this->filesystem->exists( $files['full'] ) ) {
|
||||
// If the full size is on the server, that probably means all files are on the server too.
|
||||
return $size_and_count;
|
||||
}
|
||||
|
||||
if ( ! isset( $data ) ) {
|
||||
$data = \Imagify_DB::get_metas( [
|
||||
// Get the filesizes.
|
||||
's3_filesize' => 'wpos3_filesize_total',
|
||||
], $image_ids );
|
||||
|
||||
$data = array_map( 'absint', $data['s3_filesize'] );
|
||||
}
|
||||
|
||||
if ( empty( $data[ $image_id ] ) ) {
|
||||
// The file is not on S3.
|
||||
return $size_and_count;
|
||||
}
|
||||
|
||||
// We can't take the disallowed sizes into account here.
|
||||
return [
|
||||
'filesize' => (int) $data[ $image_id ],
|
||||
'thumbnails' => count( $files ) - 1,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if an URL is a S3 one.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url The URL to test.
|
||||
* @return array|bool {
|
||||
* An array if an S3 URL. False otherwise.
|
||||
*
|
||||
* @type string $key Bucket key. Ex: subdir/wp-content/uploads/2019/02/13142432/foobar-480x510.jpg.
|
||||
* @type string $year_month The uploads year/month folders. Ex: 2019/02/.
|
||||
* @type string $subdirs Sub-directories between year/month folders and the filename.
|
||||
* It can be the S3 versioning folder, any folder added by a plugin, or both.
|
||||
* There is no way to know which one it is. Ex: foo/13142432/.
|
||||
* @type string $filename The file name. Ex: foobar-480x510.jpg.
|
||||
* }
|
||||
*/
|
||||
public function is_s3_url( $url ) {
|
||||
static $uploads_dir;
|
||||
static $domain;
|
||||
|
||||
/**
|
||||
* Tell if an URL is a S3 one.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param null|array|bool $is Null by default. Must return an array if an S3 URL, or false if not.
|
||||
* @param string $url The URL to test.
|
||||
*/
|
||||
$is = apply_filters( 'imagify_as3cf_is_s3_url', null, $url );
|
||||
|
||||
if ( false === $is ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_array( $is ) ) {
|
||||
return imagify_merge_intersect( $is, [
|
||||
'key' => '',
|
||||
'year_month' => '',
|
||||
'subdirs' => '',
|
||||
'filename' => '',
|
||||
] );
|
||||
}
|
||||
|
||||
if ( ! isset( $uploads_dir ) ) {
|
||||
$uploads_dir = wp_parse_url( $this->filesystem->get_upload_baseurl() );
|
||||
$uploads_dir = trim( $uploads_dir['path'], '/' ) . '/';
|
||||
}
|
||||
|
||||
if ( ! isset( $domain ) ) {
|
||||
if ( ! empty( $this->store_s3_settings['cloudfront'] ) ) {
|
||||
$domain = sanitize_text_field( $this->store_s3_settings['cloudfront'] );
|
||||
$domain = preg_replace( '@^(?:https?:)?//@', '//', $domain );
|
||||
$domain = preg_quote( $domain, '@' );
|
||||
} else {
|
||||
$domain = 's3-.+\.amazonaws\.com/[^/]+/';
|
||||
}
|
||||
}
|
||||
|
||||
$pattern = '@^(?:https?:)?//' . $domain . '/(?<key>' . $uploads_dir . '(?<year_month>\d{4}/\d{2}/)?(?<subdirs>.+/)?(?<filename>[^/]+))$@i';
|
||||
|
||||
if ( ! preg_match( $pattern, $url, $match ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset( $match[0] );
|
||||
|
||||
return array_merge( [
|
||||
'year_month' => '',
|
||||
'subdirs' => '',
|
||||
], $match );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user