Files
WPS3Media/classes/integrations/media-library.php

1716 lines
54 KiB
PHP
Raw Normal View History

<?php
namespace DeliciousBrains\WP_Offload_Media\Integrations;
use AS3CF_Error;
use AS3CF_Utils;
use DeliciousBrains\WP_Offload_Media\Items\Item;
use DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item;
use DeliciousBrains\WP_Offload_Media\Items\Remove_Provider_Handler;
use DeliciousBrains\WP_Offload_Media\Items\Upload_Handler;
use Exception;
use WP_Error;
use WP_Post;
class Media_Library extends Integration {
/**
* Is the current process deleting an attachment?
*
* @var bool
*/
private $deleting_attachment = false;
/**
* Keep track of items that are being updated multiple times in one request. I.e. when WP
* calls wp_update_attachment_metadata repeatedly during thumbnail generation
*
* @var array
*/
protected $items_in_progress = array();
/**
* Keep track of items that has been replaced by an edit image operation
*
* @var array
*/
protected $replaced_object_keys = array();
/**
* Keep track of context when rendering media library actions.
*
* @var string
*/
protected $render_context = 'list';
/**
* Init Media Library integration.
*/
public function init() {
Media_Library_Item::init_cache();
}
/**
* @inheritDoc
*/
public function setup() {
// Filter from WordPress media library handling, plugin needs to be set up
add_filter( 'wp_unique_filename', array( $this, 'wp_unique_filename' ), 10, 3 );
add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 110, 2 );
add_filter( 'pre_delete_attachment', array( $this, 'pre_delete_attachment' ), 20 );
add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
add_action( 'delete_post', array( $this, 'delete_post' ) );
add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 );
add_filter( 'update_post_metadata', array( $this, 'update_post_metadata' ), 100, 5 );
// Attachment screens/modals
add_action( 'load-upload.php', array( $this, 'load_media_assets' ), 11 );
add_action( 'admin_enqueue_scripts', array( $this, 'load_attachment_assets' ), 11 );
add_action( 'add_meta_boxes', array( $this, 'attachment_provider_meta_box' ) );
// AJAX
add_action( 'wp_ajax_as3cf_get_attachment_provider_details', array( $this, 'ajax_get_attachment_provider_details' ) );
// Rewriting URLs, doesn't depend on plugin being set up
add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 99, 2 );
add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 99, 3 );
add_filter( 'get_image_tag', array( $this, 'maybe_encode_get_image_tag' ), 99, 6 );
add_filter( 'wp_get_attachment_image_src', array( $this, 'maybe_encode_wp_get_attachment_image_src' ), 99, 4 );
add_filter( 'wp_prepare_attachment_for_js', array( $this, 'maybe_encode_wp_prepare_attachment_for_js' ), 99, 3 );
add_filter( 'image_get_intermediate_size', array( $this, 'maybe_encode_image_get_intermediate_size' ), 99, 3 );
add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
add_filter( 'wp_get_original_image_path', array( $this, 'get_attached_file' ), 10, 2 );
add_filter( 'wp_audio_shortcode', array( $this, 'wp_media_shortcode' ), 100, 5 );
add_filter( 'wp_video_shortcode', array( $this, 'wp_media_shortcode' ), 100, 5 );
// Srcset handling
add_filter( 'wp_image_file_matches_image_meta', array( $this, 'image_file_matches_image_meta' ), 10, 4 );
// Internal filters and actions
add_filter( 'as3cf_get_provider_url_for_item_source', array( $this, 'filter_get_provider_url_for_item_source' ), 10, 3 );
add_filter( 'as3cf_get_local_url_for_item_source', array( $this, 'filter_get_local_url_for_item_source' ), 10, 3 );
add_filter( 'as3cf_get_size_string_from_url_for_item_source', array( $this, 'get_size_string_from_url_for_item_source' ), 10, 3 );
add_filter( 'as3cf_get_item_secure_url', array( $this, 'get_item_secure_url' ), 10, 5 );
add_filter( 'as3cf_get_item_url', array( $this, 'get_item_url' ), 10, 5 );
add_filter( 'as3cf_remove_local_files', array( $this, 'filter_remove_local_files' ), 10, 3 );
add_filter( 'as3cf_remove_source_files_from_provider', array( $this, 'filter_remove_source_files_from_provider' ), 10, 3 );
add_action( 'as3cf_post_upload_item', array( $this, 'post_upload_item' ), 10, 1 );
add_filter( 'as3cf_pre_handle_item_' . Upload_Handler::get_item_handler_key_name(), array( $this, 'pre_handle_item_upload' ), 10, 3 );
add_filter( 'as3cf_upload_object_key_as_private', array( $this, 'filter_upload_object_key_as_private' ), 10, 3 );
add_action( 'as3cf_pre_upload_object', array( $this, 'action_pre_upload_object' ), 10, 2 );
if ( self::wp_check_filetype_broken() ) {
add_filter( 'shortcode_atts_audio', array( $this, 'filter_shortcode_atts' ), 10, 4 );
add_filter( 'shortcode_atts_video', array( $this, 'filter_shortcode_atts' ), 10, 4 );
}
}
/**
* Is installed?
*
* @return bool
*/
public static function is_installed(): bool {
return true;
}
/**
* Handles the upload of the attachment to provider when an attachment is updated.
*
* @handles wp_update_attachment_metadata
*
* @param array $data meta data for attachment
* @param int $post_id
*
* @return array|WP_Error
* @throws Exception
*/
public function wp_update_attachment_metadata( $data, $post_id ) {
if ( ! $this->as3cf->is_plugin_setup( true ) ) {
return $data;
}
// Some other filter may already have corrupted $data
if ( is_wp_error( $data ) ) {
return $data;
}
// Protect against updates of partially formed metadata since WordPress 5.3.
// Checks whether new upload currently is expected to have subsizes during upload,
// and if so, are any of its currently missing sizes part of the set.
if (
! empty( $data ) &&
function_exists( 'wp_get_registered_image_subsizes' ) &&
function_exists( 'wp_get_missing_image_subsizes' ) &&
wp_attachment_is_image( $post_id )
) {
/**
* Plugin compat may require that we wait for wp_generate_attachment_metadata
* to be run before proceeding with uploading. I.e. Regenerate Thumbnails requires this.
*
* @param bool $wait True if we should wait AND generate_attachment_metadata hasn't run yet
*/
if ( apply_filters( 'as3cf_wait_for_generate_attachment_metadata', false ) ) {
return $data;
}
// There is no unified way of checking whether subsizes are expected, so we have to duplicate WordPress code here.
$new_sizes = wp_get_registered_image_subsizes();
$new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $data, $post_id );
// If an image has been rotated, remove original image from metadata so that
// `wp_get_missing_image_subsizes()` doesn't use non-rotated image for
// generating missing thumbnail sizes.
// Also, some images, particularly SVGs, don't create thumbnails but do have
// metadata for them. At the time `wp_get_missing_image_subsizes()` checks
// the saved metadata, it isn't there, but we already have it.
$func = function ( $value, $object_id, $meta_key, $single, $meta_type ) use ( $post_id, $data ) {
if ( ! empty( $value['image_meta']['orientation'] ) ) {
unset( $value['original_image'] );
}
if ( ! empty( $data['image_meta']['orientation'] ) ) {
unset( $data['original_image'] );
}
if (
is_null( $value ) &&
$object_id === $post_id &&
'_wp_attachment_metadata' === $meta_key &&
$single &&
'post' === $meta_type
) {
// For some reason the filter is expected return an array of values
// as if not doing a single record.
return array( $data );
}
return $value;
};
add_filter( 'get_post_metadata', $func, 10, 5 );
$missing_sizes = wp_get_missing_image_subsizes( $post_id );
remove_filter( 'get_post_metadata', $func );
// If any registered thumbnails smaller than the original are missing,
// and current filters still expect those sizes, wait until they're all ready.
if (
! empty( $new_sizes ) &&
! empty( $missing_sizes ) &&
! empty( array_intersect_key( $missing_sizes, $new_sizes ) )
) {
return $data;
}
}
// Is this a new item that we're already started working on in this request?
if ( ! empty( $this->items_in_progress[ $post_id ] ) ) {
$as3cf_item = $this->items_in_progress[ $post_id ];
}
// Is this an update for an existing item.
if ( empty( $as3cf_item ) || is_wp_error( $as3cf_item ) ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
}
// Abort if not already uploaded to provider and the copy setting is off.
if ( ! $as3cf_item && ! $this->as3cf->get_setting( 'copy-to-s3' ) ) {
return $data;
}
if ( empty( $as3cf_item ) ) {
$as3cf_item = null;
}
/**
* Allows implementors to cancel uploading a Media Library item for any reason.
*
* This filter is triggered by updates to an attachment's metadata.
* To potentially cancel an upload started by any method,
* please use the 'as3cf_pre_upload_item' filter.
*
* @param bool $cancel True if the upload should be cancelled
* @param array $data Array describing the object being uploaded
* @param int $post_id Attachment's ID
* @param Media_Library_Item $as3cf_item The Media Library Item object if previously offloaded
*
* @see as3cf_pre_upload_item
*/
$cancel = apply_filters( 'as3cf_pre_update_attachment_metadata', false, $data, $post_id, $as3cf_item );
if ( false !== $cancel ) {
return $data;
}
$offloaded_files = array();
// If we still don't have a valid item, create one from scratch.
if ( empty( $as3cf_item ) || is_wp_error( $as3cf_item ) ) {
$as3cf_item = Media_Library_Item::create_from_source_id( $post_id );
} else {
$offloaded_files = $as3cf_item->offloaded_files();
}
// Did we get a WP_Error?
if ( is_wp_error( $as3cf_item ) ) {
AS3CF_Error::Log( $as3cf_item->get_error_message() );
return $data;
}
// Or didn't we get anything at all?
if ( empty( $as3cf_item ) ) {
$message = sprintf( __( "Can't create item from media library item %d", 'amazon-s3-and-cloudfront' ), $post_id );
AS3CF_Error::Log( $message );
return $data;
}
// Update item's expected objects from attachment's new metadata.
$this->update_item_from_new_metadata( $as3cf_item, $data );
$this->upload_item( $as3cf_item, $offloaded_files );
$this->items_in_progress[ $post_id ] = $as3cf_item;
return $data;
}
/**
* Upload item.
*
* @param Media_Library_Item $as3cf_item
* @param array $offloaded_files An array of files previously offloaded for the item.
*/
protected function upload_item( Media_Library_Item $as3cf_item, array $offloaded_files ) {
$upload_handler = $this->as3cf->get_item_handler( Upload_Handler::get_item_handler_key_name() );
$upload_handler->handle( $as3cf_item, array( 'offloaded_files' => $offloaded_files ) );
}
/**
* Handle update_post_metadata for some media library related keys
*
* @handles update_post_metadata
*
* @param bool $check
* @param int $object_id
* @param string $meta_key
* @param mixed $meta_value
* @param mixed $prev_value
*/
public function update_post_metadata( $check, $object_id, $meta_key, $meta_value, $prev_value ) {
if ( '_wp_attachment_backup_sizes' === $meta_key ) {
if ( $this->as3cf->is_plugin_setup( true ) ) {
$this->update_attachment_backup_sizes( $object_id, $meta_value );
}
}
return $check;
}
/**
* Handle updated attachment_backup_sizes.
*
* @param int $post_id
* @param array $sizes
*/
protected function update_attachment_backup_sizes( $post_id, $sizes ) {
// This item should already be known in this request, if not bail out
if ( empty( $this->items_in_progress[ $post_id ] ) ) {
return;
}
// We should also have recorded some replaced keys in this request, if not bail
if ( empty( $this->replaced_object_keys[ $post_id ] ) ) {
return;
}
/** @var Media_Library_Item $as3cf_item */
$as3cf_item = $this->items_in_progress[ $post_id ];
$existing_objects = $as3cf_item->objects();
foreach ( array_keys( $sizes ) as $key ) {
if ( ! isset( $existing_objects[ $key ] ) ) {
$parts = explode( '-', $key );
$size = join( '-', array_slice( $parts, 0, -1 ) );
if ( 'full' === $size ) {
$size = Item::primary_object_key();
}
if ( isset( $this->replaced_object_keys[ $post_id ][ $size ] ) ) {
$existing_objects[ $key ] = $this->replaced_object_keys[ $post_id ][ $size ];
}
}
}
$as3cf_item->set_objects( $existing_objects );
$as3cf_item->save();
}
/**
* Filters the result when generating a unique file name.
*
* @param string $filename Unique file name.
* @param string $ext File extension, eg. ".png".
* @param string $dir Directory path.
*
* @return string
* @since 4.5.0
*/
public function wp_unique_filename( $filename, $ext, $dir ) {
// Get Post ID if uploaded in post screen.
$post_id = filter_input( INPUT_POST, 'post_id', FILTER_VALIDATE_INT );
return $this->filter_unique_filename( $filename, $ext, $dir, $post_id );
}
/**
* Create unique names for file to be uploaded to AWS.
* This only applies when the remove local file option is enabled.
*
* @param string $filename Unique file name.
* @param string $ext File extension, eg. ".png".
* @param string $dir Directory path.
* @param int $post_id Attachment's parent Post ID.
*
* @return string
*/
public function filter_unique_filename( $filename, $ext, $dir, $post_id = null ) {
if ( ! $this->as3cf->is_plugin_setup( true ) ) {
return $filename;
}
// sanitize the file name before we begin processing
$filename = sanitize_file_name( $filename );
$ext = strtolower( $ext );
$name = wp_basename( $filename, $ext );
// Edge case: if file is named '.ext', treat as an empty name.
if ( $name === $ext ) {
$name = '';
}
// Rebuild filename with lowercase extension as provider will have converted extension on upload.
$filename = $name . $ext;
$time = current_time( 'mysql' );
// Get time if uploaded in post screen.
if ( ! empty( $post_id ) ) {
$time = $this->get_post_time( $post_id );
}
if ( ! $this->as3cf->does_file_exist( $filename, $time ) ) {
// File doesn't exist locally or on provider, return it.
return $filename;
}
return $this->as3cf->generate_unique_filename( $name, $ext, $time );
}
/**
* Allow processes to update the file on provider via update_attached_file()
*
* @param string $file
* @param int $attachment_id
*
* @return string
*/
public function update_attached_file( $file, $attachment_id ) {
if ( ! $this->as3cf->is_plugin_setup( true ) ) {
return $file;
}
$as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
if ( ! $as3cf_item ) {
return $file;
}
/**
* Allow processes to update the file on provider via update_attached_file()
*
* @param string $file File name/path
* @param int $attachment_id Attachment id
* @param Media_Library_Item $as3cf_item The item object
*/
return apply_filters( 'as3cf_update_attached_file', $file, $attachment_id, $as3cf_item );
}
/**
* Removes an attachment and intermediate image size files from provider
*
* @param int $post_id
*/
public function delete_attachment( $post_id ) {
if ( ! $this->as3cf->is_plugin_setup( true ) ) {
return;
}
$as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
if ( ! $as3cf_item ) {
return;
}
if ( ! $as3cf_item->served_by_provider( true ) ) {
return;
}
// Remove the objects from the provider
$remove_provider_handler = $this->as3cf->get_item_handler( Remove_Provider_Handler::get_item_handler_key_name() );
$remove_provider_handler->handle( $as3cf_item, array( 'verify_exists_on_local' => false ) );
$as3cf_item->delete();
}
/**
* Update an existing item's expected objects from attachment's new metadata.
*
* @param Media_Library_Item $as3cf_item
* @param array $metadata
*/
protected function update_item_from_new_metadata( $as3cf_item, $metadata ) {
if ( empty( $metadata ) || ! is_array( $metadata ) ) {
return;
}
$files = AS3CF_Utils::get_attachment_file_paths( $as3cf_item->source_id(), false, $metadata );
$existing_basename = wp_basename( $as3cf_item->path() );
$existing_objects = $as3cf_item->objects();
if ( ! isset( $this->replaced_object_keys[ $as3cf_item->source_id() ] ) ) {
$this->replaced_object_keys[ $as3cf_item->source_id() ] = array();
}
foreach ( $files as $object_key => $file ) {
$new_filename = wp_basename( $file );
if ( ! empty( $existing_objects[ $object_key ]['source_file'] ) && $existing_objects[ $object_key ]['source_file'] !== $new_filename ) {
$this->replaced_object_keys[ $as3cf_item->source_id() ][ $object_key ] = $existing_objects[ $object_key ];
}
if ( Item::primary_object_key() === $object_key && $existing_basename !== $new_filename ) {
$as3cf_item->set_path( str_replace( $existing_basename, $new_filename, $as3cf_item->path() ) );
$as3cf_item->set_source_path( str_replace( $existing_basename, $new_filename, $as3cf_item->source_path() ) );
}
$existing_objects[ $object_key ] = array(
'source_file' => $new_filename,
'is_private' => isset( $existing_objects[ $object_key ]['is_private'] ) ? $existing_objects[ $object_key ]['is_private'] : false,
);
}
$extra_info = $as3cf_item->extra_info();
$extra_info['objects'] = $existing_objects;
$as3cf_item->set_extra_info( $extra_info );
}
/**
* Load media assets.
*/
public function load_media_assets() {
$this->as3cf->enqueue_style( 'as3cf-media-styles', 'assets/css/media', array( 'as3cf-modal' ) );
$this->as3cf->enqueue_script( 'as3cf-media-script', 'assets/js/media', array(
'jquery',
'media-views',
'media-grid',
'wp-util',
) );
wp_localize_script( 'as3cf-media-script', 'as3cf_media', array(
'strings' => $this->get_media_action_strings(),
'nonces' => array(
'get_attachment_provider_details' => wp_create_nonce( 'get-attachment-s3-details' ),
),
) );
}
/**
* Load the attachment assets only when editing an attachment
*
* @param string $hook_suffix
*/
public function load_attachment_assets( $hook_suffix ) {
global $post;
if ( 'post.php' !== $hook_suffix || 'attachment' !== $post->post_type ) {
return;
}
$this->as3cf->enqueue_style( 'as3cf-pro-attachment-styles', 'assets/css/attachment', array( 'as3cf-modal' ) );
do_action( 'as3cf_load_attachment_assets' );
}
/**
* Add the S3 meta box to the attachment screen
*/
public function attachment_provider_meta_box() {
add_meta_box(
's3-actions',
__( 'Offload', 'amazon-s3-and-cloudfront' ),
array( $this, 'attachment_provider_actions_meta_box' ),
'attachment',
'side',
'core'
);
}
/**
* Handle retrieving the provider details for attachment modals.
*/
public function ajax_get_attachment_provider_details() {
if ( ! isset( $_POST['id'] ) ) {
return;
}
check_ajax_referer( 'get-attachment-s3-details', '_nonce' );
$id = intval( $_POST['id'] );
$as3cf_item = Media_Library_Item::get_by_source_id( $id );
$served_by_provider = false;
if ( ! empty( $as3cf_item ) ) {
$served_by_provider = $as3cf_item->served_by_provider( true );
}
$this->render_context = 'grid';
// get the actions available for the attachment
$data = array(
'links' => $this->add_media_row_actions( array(), $id ),
'provider_object' => $this->get_formatted_provider_info( $id ),
'acl_toggle' => $this->verify_media_actions() && $served_by_provider,
);
wp_send_json_success( $data );
}
/**
* Conditionally adds copy, remove and download S3 action links for an
* attachment on the Media library list view
*
* @param array $actions
* @param WP_Post|int $post
*
* @return array
*/
public function add_media_row_actions( array $actions, $post ) {
return $actions;
}
/**
* Get a list of available media actions which can be performed according to plugin and user capability requirements.
*
* @param string|null $scope
*
* @return array
*/
public function get_available_media_actions( $scope = '' ) {
return array();
}
/**
* Render the S3 attachment meta box
*/
public function attachment_provider_actions_meta_box() {
global $post;
$file = get_attached_file( $post->ID, true );
$args = array(
'provider_object' => $this->get_formatted_provider_info( $post->ID ),
'post' => $post,
'local_file_exists' => file_exists( $file ),
'available_actions' => $this->get_available_media_actions( 'singular' ),
'sendback' => 'post.php?post=' . $post->ID . '&action=edit',
);
$this->as3cf->render_view( 'attachment-metabox', $args );
}
/**
* Get attachment url
*
* @param string $url
* @param int $post_id
*
* @return bool|mixed|WP_Error
*/
public function wp_get_attachment_url( $url, $post_id ) {
if ( $this->as3cf->plugin_compat->is_customizer_crop_action() ) {
return $url;
}
$as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
if ( empty( $as3cf_item ) || ! $as3cf_item->served_by_provider() ) {
return $url;
}
$size = $as3cf_item->get_object_key_from_filename( $url );
$new_url = $as3cf_item->get_provider_url( $size );
if ( is_wp_error( $new_url ) || false === $new_url ) {
return $url;
}
// Old naming convention, will be deprecated soon
$new_url = apply_filters( 'wps3_get_attachment_url', $new_url, $post_id, $this );
/**
* Filter the rewritten provider URL for a Media Library Item (attachment)
*
* @param string $url The URL
* @param int $post_id Attachment post id
*/
return apply_filters( 'as3cf_wp_get_attachment_url', $new_url, $post_id );
}
/**
* Return a formatted provider info array with display friendly defaults
*
* @param int $id
*
* @return bool|array
*/
public function get_formatted_provider_info( int $id ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $id );
if ( ! $as3cf_item ) {
return false;
}
$provider_object = $as3cf_item->key_values();
$storage_provider = $this->as3cf->get_storage_provider_instance( $provider_object['provider'] );
// Backwards compatibility.
$provider_object['key'] = $provider_object['path'];
$provider_object['url'] = $as3cf_item->get_provider_url();
$acl = $as3cf_item->is_private() ? $storage_provider->get_private_acl() : $storage_provider->get_default_acl();
$acl_info = array(
'acl' => $acl,
'name' => $this->as3cf->get_acl_display_name( $acl ),
'title' => $this->get_media_action_strings( 'change_to_private' ),
);
if ( $as3cf_item->is_private() ) {
$acl_info['title'] = $this->get_media_action_strings( 'change_to_public' );
}
$provider_object['acl'] = $acl_info;
$provider_object['region'] = $storage_provider->get_region_name( $provider_object['region'] );
$provider_object['provider_name'] = $this->as3cf->get_provider_service_name( $provider_object['provider'] );
return $provider_object;
}
/**
* Filters the list of attachment image attributes.
*
* @param array $attr Attributes for the image markup.
* @param WP_Post $attachment Image attachment post.
* @param string|array $size Requested size. Image size or array of width and height values (in that order).
*
* @return array
*/
public function wp_get_attachment_image_attributes( $attr, $attachment, $size ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $attachment->ID );
if ( ! $as3cf_item || ! $as3cf_item->served_by_provider() ) {
return $attr;
}
$size = $this->maybe_convert_size_to_string( $attachment->ID, $size );
// image_downsize incorrectly substitutes size filename into full URL for src attribute instead of clobbering.
// So we need to fix up the src attribute if a size is being used.
if ( ! empty( $size ) && ! empty( $attr['src'] ) ) {
$attr['src'] = $as3cf_item->get_provider_url( $size );
}
/**
* Filtered list of attachment image attributes.
*
* @param array $attr Attributes for the image markup.
* @param WP_Post $attachment Image attachment post.
* @param string $size Requested size.
* @param Media_Library_Item $as3cf_item
*/
return apply_filters( 'as3cf_wp_get_attachment_image_attributes', $attr, $attachment, $size, $as3cf_item );
}
/**
* Maybe encode attachment URLs when retrieving the image tag
*
* @param string $html
* @param int $id
* @param string $alt
* @param string $title
* @param string $align
* @param string $size
*
* @return string
*/
public function maybe_encode_get_image_tag( $html, $id, $alt, $title, $align, $size ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $id );
if ( ! $as3cf_item || ! $as3cf_item->served_by_provider() ) {
return $html;
}
if ( ! is_string( $html ) ) {
return $html;
}
preg_match( '@\ssrc=[\'\"]([^\'\"]*)[\'\"]@', $html, $matches );
if ( ! isset( $matches[1] ) ) {
// Can't establish img src
return $html;
}
$img_src = $matches[1];
$new_img_src = $this->maybe_sign_intermediate_size( $img_src, $id, $size, $as3cf_item );
$new_img_src = AS3CF_Utils::encode_filename_in_path( $new_img_src );
return str_replace( $img_src, $new_img_src, $html );
}
/**
* Maybe encode URLs for images that represent an attachment
*
* @param array|bool $image
* @param int $attachment_id
* @param string|array $size
* @param bool $icon
*
* @return array
*/
public function maybe_encode_wp_get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
if ( ! $as3cf_item || ! $as3cf_item->served_by_provider() ) {
return $image;
}
if ( isset( $image[0] ) ) {
$url = $this->maybe_sign_intermediate_size( $image[0], $attachment_id, $size, $as3cf_item );
$url = AS3CF_Utils::encode_filename_in_path( $url );
$image[0] = $url;
}
return $image;
}
/**
* Maybe encode URLs when outputting attachments in the media grid
*
* @param array $response
* @param int|object $attachment
* @param array $meta
*
* @return array
*/
public function maybe_encode_wp_prepare_attachment_for_js( $response, $attachment, $meta ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $attachment->ID );
if ( empty( $as3cf_item ) || ! $as3cf_item->served_by_provider() ) {
return $response;
}
if ( isset( $response['url'] ) ) {
$response['url'] = AS3CF_Utils::encode_filename_in_path( $response['url'] );
}
if ( isset( $response['sizes'] ) && is_array( $response['sizes'] ) ) {
foreach ( $response['sizes'] as $size => $value ) {
$url = $this->maybe_sign_intermediate_size( $value['url'], $attachment->ID, $size, $as3cf_item, true );
$url = AS3CF_Utils::encode_filename_in_path( $url );
$response['sizes'][ $size ]['url'] = $url;
}
}
return $response;
}
/**
* Maybe encode URLs when retrieving intermediate sizes.
*
* @param array $data
* @param int $post_id
* @param string|array $size
*
* @return array
*/
public function maybe_encode_image_get_intermediate_size( $data, $post_id, $size ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
if ( ! $as3cf_item || ! $as3cf_item->served_by_provider() ) {
return $data;
}
if ( isset( $data['url'] ) ) {
$url = $this->maybe_sign_intermediate_size( $data['url'], $post_id, $size, $as3cf_item );
$url = AS3CF_Utils::encode_filename_in_path( $url );
$data['url'] = $url;
}
return $data;
}
/**
* Sign intermediate size.
*
* @param string $url
* @param int $attachment_id
* @param string|array $size
* @param bool|Media_Library_Item $as3cf_item
* @param bool $force_rewrite If size not signed, make sure correct URL is being used anyway.
*
* @return string|WP_Error
*/
protected function maybe_sign_intermediate_size( $url, $attachment_id, $size, $as3cf_item = false, $force_rewrite = false ) {
if ( ! $as3cf_item ) {
$as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
}
$size = $this->maybe_convert_size_to_string( $attachment_id, $size );
if ( $force_rewrite || $as3cf_item->is_private( $size ) ) {
// Private file, add AWS signature if required
return $as3cf_item->get_provider_url( $size );
}
return $url;
}
/**
* Return the provider URL when the local file is missing
* unless we know who the calling process is, and we are happy
* to copy the file back to the server to be used.
*
* @handles get_attached_file
* @handles wp_get_original_image_path
*
* @param string $file
* @param int $attachment_id
*
* @return string
*/
public function get_attached_file( $file, $attachment_id ) {
// During the deletion of an attachment, stream wrapper URLs should not be returned.
if ( $this->deleting_attachment ) {
return $file;
}
$as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
if ( ! empty( $as3cf_item ) && ! $as3cf_item->served_by_provider() ) {
$as3cf_item = false;
}
if ( file_exists( $file ) || ! $as3cf_item ) {
if ( $as3cf_item ) {
/**
* This filter gives filter implementors a chance to copy back siblings for
* a local file even if the main already exists locally.
*
* @param string $url Item URL
* @param string $file Local file path
* @param int $attachment_id Attachment post id
* @param Media_Library_Item $as3cf_item The Item object
*/
return apply_filters( 'as3cf_get_attached_file_noop', $file, $file, $attachment_id, $as3cf_item );
} else {
return $file;
}
}
$url = $as3cf_item->get_provider_url();
if ( false === $url || is_wp_error( $url ) ) {
return $file;
}
/**
* This filter gives filter implementors a chance to copy back missing item files
* from the provider before WordPress returns the file name/path for it. Defaults to
* returning the remote URL.
*
* @param string $url Item URL
* @param string $file Local file path
* @param int $attachment_id Attachment post id
* @param Media_Library_Item $as3cf_item The Item object
*/
return apply_filters( 'as3cf_get_attached_file', $url, $file, $attachment_id, $as3cf_item );
}
/**
* Filters the audio & video shortcodes output to remove "&_=NN" params from source.src as it breaks signed URLs.
*
* @param string $html Shortcode HTML output.
* @param array $atts Array of shortcode attributes.
* @param string $media Media file.
* @param int $post_id Post ID.
* @param string $library Media library used for the shortcode.
*
* @return string
*
* Note: Depends on 30377.4.diff from https://core.trac.wordpress.org/ticket/30377
*/
public function wp_media_shortcode( $html, $atts, $media, $post_id, $library ) {
return preg_replace( '/&#038;_=[0-9]+/', '', $html );
}
/**
* Check we can do the media actions
*
* @return bool
*/
public function verify_media_actions() {
return false;
}
/**
* Get all strings or a specific string used for the media actions
*
* @param null|string $string
*
* @return array|string
*/
public function get_media_action_strings( $string = null ) {
$not_verified_value = __( 'No', 'amazon-s3-and-cloudfront' );
$not_verified_value .= '&nbsp;';
$not_verified_value .= $this->as3cf->more_info_link( '/wp-offload-media/doc/add-metadata-tool/', 'os3+attachment+metabox', 'analyze-and-repair', 'More Info', '(', ')' );
/**
* Returns all strings used to render meta boxes on the WordPress Media Library edit page
*
* @param array $strings Associative array of strings
*/
$strings = apply_filters( 'as3cf_media_action_strings', array(
'provider' => _x( 'Storage Provider', 'Storage provider key name', 'amazon-s3-and-cloudfront' ),
'provider_name' => _x( 'Storage Provider', 'Storage provider name', 'amazon-s3-and-cloudfront' ),
'bucket' => _x( 'Bucket', 'Bucket name', 'amazon-s3-and-cloudfront' ),
'key' => _x( 'Path', 'Path to file in bucket', 'amazon-s3-and-cloudfront' ),
'region' => _x( 'Region', 'Location of bucket', 'amazon-s3-and-cloudfront' ),
'acl' => _x( 'Access', 'Access control list of the file in bucket', 'amazon-s3-and-cloudfront' ),
'url' => __( 'URL', 'amazon-s3-and-cloudfront' ),
'is_verified' => _x( 'Verified', 'Whether or not metadata has been verified', 'amazon-s3-and-cloudfront' ),
'not_verified' => $not_verified_value,
) );
if ( ! is_null( $string ) ) {
return isset( $strings[ $string ] ) ? $strings[ $string ] : '';
}
return $strings;
}
/**
* Remove 'filesize' from attachment's metadata if appropriate, also our total filesize record.
*
* @param int $post_id Attachment's post_id.
* @param array $data Attachment's metadata.
* @param bool $update_metadata Update the metadata record now? Defaults to true.
*
* @return array Attachment's cleaned up metadata.
*/
public function maybe_cleanup_filesize_metadata( $post_id, $data, $update_metadata = true ) {
if ( ! is_int( $post_id ) || empty( $post_id ) || empty( $data ) || ! is_array( $data ) ) {
return $data;
}
/*
* Audio and video have a filesize added to metadata by default, but images and anything else don't.
* Note: Could have used `wp_generate_attachment_metadata` here to test whether default metadata has 'filesize',
* but it not only has side effects it also does a lot of work considering it's not a huge deal for this entry to hang around.
*/
if (
empty( $data['mime_type'] ) ||
0 === strpos( $data['mime_type'], 'image/' ) ||
! ( 0 === strpos( $data['mime_type'], 'audio/' ) || 0 === strpos( $data['mime_type'], 'video/' ) )
) {
unset( $data['filesize'] );
}
if ( $update_metadata ) {
if ( empty( $data ) ) {
delete_post_meta( $post_id, '_wp_attachment_metadata' );
} else {
update_post_meta( $post_id, '_wp_attachment_metadata', $data );
}
}
delete_post_meta( $post_id, 'as3cf_filesize_total' );
return $data;
}
/**
* Get ACL value string.
*
* @param array $acl
* @param int $post_id
*
* @return string
*/
public function get_acl_value_string( $acl, $post_id ) {
return $acl['name'];
}
/**
* Determines if the image metadata is for the image source file.
*
* @handles wp_image_file_matches_image_meta
*
* @param bool $match
* @param string $image_location
* @param array $image_meta
* @param int $source_id
*
* @return bool
*/
public function image_file_matches_image_meta( $match, $image_location, $image_meta, $source_id ) {
// If already matched or the URL is local, there's nothing for us to do.
if ( $match || $this->as3cf->filter_local->url_needs_replacing( $image_location ) ) {
return $match;
}
$item = array(
'id' => $source_id,
'source_type' => Media_Library_Item::source_type(),
);
return $this->as3cf->filter_provider->item_matches_src( $item, $image_location );
}
/**
* Get the local URL for a Media Library Item
*
* @handles as3cf_get_local_url_for_item_source
*
* @param string $url Url
* @param array $item_source The item source descriptor array
* @param string $size Name of requested size
*
* @return string|false
*/
public function filter_get_local_url_for_item_source( $url, $item_source, $size ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $url;
}
$as3cf_item = Media_Library_Item::get_by_source_id( $item_source['id'] );
if ( ! empty( $as3cf_item ) ) {
return $as3cf_item->get_local_url( $size );
}
return $url;
}
/**
* Get the remote URL for a Media Library Item
*
* @handles as3cf_get_provider_url_for_item_source
*
* @param string $url Url
* @param array $item_source The item source descriptor array
* @param string $size Name of requested size
*
* @return string|false
*/
public function filter_get_provider_url_for_item_source( $url, $item_source, $size ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $url;
}
$as3cf_item = Media_Library_Item::get_by_source_id( $item_source['id'] );
if ( empty( $as3cf_item ) || ! $as3cf_item->served_by_provider() ) {
return $url;
}
$url = $as3cf_item->get_provider_url( $size );
if ( is_wp_error( $url ) ) {
return false;
}
return $url;
}
/**
* Get the size from a URL for media library item types
*
* @handles as3cf_get_size_string_from_url_for_item_source
*
* @param string $size
* @param string $url
* @param array $item_source
*
* @return string
*/
public function get_size_string_from_url_for_item_source( $size, $url, $item_source ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $size;
}
$meta = get_post_meta( $item_source['id'], '_wp_attachment_metadata', true );
if ( empty( $meta['sizes'] ) ) {
// No alternative sizes available, return
return $size;
}
$basename = AS3CF_Utils::encode_filename_in_path( wp_basename( $this->as3cf->maybe_remove_query_string( $url ) ) );
foreach ( $meta['sizes'] as $size_name => $file ) {
if ( $basename === AS3CF_Utils::encode_filename_in_path( $file['file'] ) ) {
return $size_name;
}
}
return $size;
}
/**
* Get attachment id from remote URL.
*
* @param string $url
*
* @return bool|int
*/
public function get_attachment_id_from_provider_url( $url ) {
$item_source = $this->as3cf->filter_provider->get_item_source_from_url( $url );
if ( ! Item::is_empty_item_source( $item_source ) && Media_Library_Item::source_type() === $item_source['source_type'] ) {
return $item_source['id'];
}
return false;
}
/**
* Get attachment id from local URL.
*
* @param string $url
*
* @return bool|int
*/
public function get_attachment_id_from_local_url( $url ) {
$item_source = $this->as3cf->filter_local->get_item_source_from_url( $url );
if ( ! Item::is_empty_item_source( $item_source ) && Media_Library_Item::source_type() === $item_source['source_type'] ) {
return $item_source['id'];
}
return false;
}
/**
* Get post time
*
* @param int $post_id
*
* @return string
*/
private function get_post_time( $post_id ) {
$time = current_time( 'mysql' );
if ( ! $post = get_post( $post_id ) ) {
return $time;
}
if ( substr( $post->post_date, 0, 4 ) > 0 ) {
$time = $post->post_date;
}
return $time;
}
/**
* Maybe convert size to string
*
* @param int $attachment_id
* @param mixed $size
*
* @return null|string
*/
private function maybe_convert_size_to_string( $attachment_id, $size ) {
if ( is_array( $size ) ) {
return $this->convert_dimensions_to_size_name( $attachment_id, $size );
}
return $size;
}
/**
* Convert dimensions to size.
*
* @param int $attachment_id
* @param array $dimensions
*
* @return null|string
*/
private function convert_dimensions_to_size_name( int $attachment_id, array $dimensions ) {
$w = ( isset( $dimensions[0] ) && (int) $dimensions[0] > 0 ) ? (int) $dimensions[0] : 1;
$h = ( isset( $dimensions[1] ) && (int) $dimensions[1] > 0 ) ? (int) $dimensions[1] : 1;
$original_aspect_ratio = $w / $h;
$meta = wp_get_attachment_metadata( $attachment_id );
if ( ! isset( $meta['sizes'] ) || empty( $meta['sizes'] ) ) {
return null;
}
$sizes = $meta['sizes'];
uasort( $sizes, function ( $a, $b ) {
// Order by image area.
return ( (int) $a['width'] * (int) $a['height'] ) - ( (int) $b['width'] * (int) $b['height'] );
} );
$nearest_matches = array();
foreach ( $sizes as $size => $value ) {
if ( $w > (int) $value['width'] || $h > (int) $value['height'] ) {
continue;
}
$aspect_ratio = 0;
if ( (int) $value['height'] > 0 ) {
$aspect_ratio = (int) $value['width'] / (int) $value['height'];
}
if ( $aspect_ratio === $original_aspect_ratio ) {
return $size;
}
$nearest_matches[] = $size;
}
// Return nearest match.
if ( ! empty( $nearest_matches ) ) {
return $nearest_matches[0];
}
return null;
}
/**
* Has the given attachment been uploaded by this instance?
*
* @param int $source_id
*
* @return bool
*/
public function item_just_uploaded( $source_id ) {
if ( is_int( $source_id ) && isset( $this->items_in_progress[ $source_id ] ) ) {
return true;
}
return false;
}
/**
* Call legacy attachment specific version of the as3cf_get_item_secure_url filter.
*
* @param string $url The URL
* @param Item $as3cf_item The Item object
* @param array $item_source The item source descriptor array
* @param int $timestamp Expiry timestamp
* @param array $headers Optional extra http headers
*
* @handles as3cf_get_item_secure_url
*
* @return string|mixed
*/
public function get_item_secure_url( $url, $as3cf_item, $item_source, $timestamp, $headers ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $url;
}
/**
* Filters the secure url for an attachment
*
* @param string $url The URL
* @param Item $as3cf_item The Item object
* @param int $id The attachment id
* @param int $timestamp Expiry timestamp
* @param array $headers Optional extra http headers
*
* @deprecated 2.6.0 Please use filter "as3cf_get_item_secure_url" instead.
*/
return apply_filters( 'as3cf_get_attachment_secure_url', $url, $as3cf_item, $item_source['id'], $timestamp, $headers );
}
/**
* Call legacy attachment specific version of the as3cf_get_item_url filter.
*
* @param string $url The URL
* @param Item $as3cf_item The Item object
* @param array $item_source The item source descriptor array
* @param int $timestamp Expiry timestamp
* @param array $headers Optional extra http headers
*
* @handles as3cf_get_item_url
*
* @return string|mixed
*/
public function get_item_url( $url, $as3cf_item, $item_source, $timestamp, $headers ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $url;
}
/**
* Filters the url for an attachment
*
* @param string $url The URL
* @param Item $as3cf_item The Item object
* @param int $id The attachment id
* @param int $timestamp Expiry timestamp
* @param array $headers Optional extra http headers
*
* @deprecated 2.6.0 Please use filter "as3cf_get_item_url" instead.
*/
return apply_filters( 'as3cf_get_attachment_url', $url, $as3cf_item, $item_source['id'], $timestamp, $headers );
}
/**
* Call legacy attachment specific version of the as3cf_remove_source_files_from_provider filter.
*
* @param array $paths Array of local paths to be removed from provider
* @param Item $as3cf_item The Item object
* @param array $item_source The item source descriptor array
*
* @handles as3cf_remove_source_files_from_provider
*
* @return array|mixed
*/
public function filter_remove_source_files_from_provider( $paths, Item $as3cf_item, $item_source ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $paths;
}
/**
* Filters which provider files to remove
*
* @param array $paths Array of local paths to be removed from provider
* @param int $id Item attachment id
* @param Item $as3cf_item The Item
* @param bool $include_backups Also include backup files?
*
* @deprecated 2.6.0 Please use filter "as3cf_remove_source_files_from_provider" instead.
*/
return apply_filters( 'as3cf_remove_attachment_paths', $paths, $item_source['id'], $as3cf_item, true );
}
/**
* Calls legacy attachment specific version of as3cf_remove_local_files filter.
*
* @param array $files_to_remove Array of paths to be removed
* @param Item $as3cf_item The Item object
* @param array $item_source The item source descriptor array
*
* @handles as3cf_remove_local_files
*
* @return array|mixed
*/
public function filter_remove_local_files( $files_to_remove, $as3cf_item, $item_source ) {
if ( Media_Library_Item::source_type() !== $item_source['source_type'] ) {
return $files_to_remove;
}
/**
* Filters which local files should be removed.
*
* @param array $paths Paths that will be removed
* @param int $id Attachment id
* @param string $source_path Path to primary file
*
* @deprecated 2.6.0 Please use filter "as3cf_remove_local_files" instead.
*/
return apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $item_source['id'], $as3cf_item->full_source_path( Item::primary_object_key() ) );
}
/**
* Handle post upload duties if uploaded item is a media-library item.
*
* @handles as3cf_post_upload_item
*
* @param Media_Library_Item $as3cf_item
*/
public function post_upload_item( $as3cf_item ) {
if ( Media_Library_Item::source_type() !== $as3cf_item->source_type() ) {
return;
}
// Make sure duplicates are marked as offloaded too.
$as3cf_item->offload_duplicate_items();
/**
* Fires after an attachment has been uploaded to the provider.
*
* @param int $id Attachment id
* @param Item $as3cf_item The item that was just uploaded
*
* @deprecated 2.6.0 Please use action "as3cf_post_upload_item" instead.
*/
do_action( 'as3cf_post_upload_attachment', $as3cf_item->source_id(), $as3cf_item );
}
/**
* Call legacy media library specific filter for cancelling an upload.
*
* @param bool $cancel Should the action on the item be cancelled?
* @param Item $as3cf_item The item that the action is being handled for.
* @param array $options Handler dependent options that may have been set for the action.
*
* @handles as3cf_pre_handle_item_upload
*
* @return bool
*/
public function pre_handle_item_upload( $cancel, $as3cf_item, array $options ) {
if ( Media_Library_Item::source_type() !== $as3cf_item->source_type() ) {
return $cancel;
}
// Get unfiltered attachment metadata to pass into legacy filter.
$metadata = wp_get_attachment_metadata( $as3cf_item->source_id(), true );
if ( is_wp_error( $metadata ) ) {
return $metadata;
}
/**
* Allow provider upload to be cancelled for any reason.
*
* @param bool $cancel Should the upload for the attachment be cancelled?
* @param int $id Attachment id
* @param array $metadata Attachment metadata
*
* @deprecated 2.6.0 Please use filter "as3cf_pre_upload_item" instead.
*/
return apply_filters( 'as3cf_pre_upload_attachment', $cancel, $as3cf_item->source_id(), $metadata );
}
/**
* Call legacy filter for determining private status on an item's individual object_key.
*
* @param bool $is_private
* @param string $object_key
* @param Item $as3cf_item
*
* @handles as3cf_upload_object_key_as_private
*
* @return bool
*/
public function filter_upload_object_key_as_private( $is_private, $object_key, $as3cf_item ) {
if ( Media_Library_Item::source_type() !== $as3cf_item->source_type() ) {
return $is_private;
}
$metadata = wp_get_attachment_metadata( $as3cf_item->source_id(), true );
$default_acl = $this->as3cf->get_storage_provider()->get_default_acl();
$private_acl = $this->as3cf->get_storage_provider()->get_private_acl();
$acl = true === $is_private ? $private_acl : $default_acl;
if ( Item::primary_object_key() === $object_key ) {
$file_name = wp_basename( $as3cf_item->source_path() );
$file_type = wp_check_filetype_and_ext( $as3cf_item->source_path(), $file_name );
// Old naming convention, will be removed soon.
$acl = apply_filters( 'wps3_upload_acl', $acl, $file_type['type'], $metadata, $as3cf_item->source_id(), $this->as3cf );
/**
* Determine canned ACL for an item's original (full size) file about to be uploaded to provider.
*
* @param string $acl The canned ACL for the provider.
* @param array $metadata The attachment's metadata.
* @param int $id The attachment's ID.
*
* @deprecated 2.6.0 Please use filter "as3cf_upload_object_key_as_private" instead.
*/
$acl = apply_filters( 'as3cf_upload_acl', $acl, $metadata, $as3cf_item->source_id() );
} else {
/**
* Determine ACL for an item's individual thumbnail size about to be uploaded to provider.
*
* @param string $acl The canned ACL for the provider.
* @param string $size Size name for file (thumbnail, medium, large).
* @param int $id The attachment's ID.
* @param array $data The attachment's metadata.
*
* @deprecated 2.6.0 Please use filter "as3cf_upload_object_key_as_private" instead.
*/
$acl = apply_filters( 'as3cf_upload_acl_sizes', $acl, $object_key, $as3cf_item->source_id(), $metadata );
}
if ( ! empty( $acl ) && $private_acl === $acl ) {
return true;
}
return $is_private;
}
/**
* Fire legacy action just before a Media Library Item is offloaded.
*
* @handles as3cf_pre_upload_object
*
* @param Item $as3cf_item
* @param array $args
*/
public function action_pre_upload_object( $as3cf_item, $args ) {
if ( Media_Library_Item::source_type() !== $as3cf_item->source_type() ) {
return;
}
/**
* Actions fires when an Item's original file might be offloaded.
*
* This action gives notice that an Item is being processed for upload to a bucket,
* and the given arguments represent the original file's potential offload location.
* However, if the current process is for picking up extra files associated with the item,
* the indicated original file may not actually be offloaded if it does not exist
* on the server but has already been offloaded.
*
* @param int $id The attachment id.
* @param Media_Library_Item $as3cf_item The Item whose files are being offloaded.
* @param string $path The path to the item.
* @param array $args The arguments that could be used to offload the original file.
*
* @deprecated 2.6.0 Please use action "as3cf_pre_upload_object" instead.
*/
do_action( 'as3cf_upload_attachment_pre_remove', $as3cf_item->source_id(), $as3cf_item, $as3cf_item->normalized_path_dir(), $args );
}
/**
* Takes notice that an attachment is about to be deleted and prepares for it.
*
* @handles pre_delete_attachment
*
* @param bool|null $delete Whether to go forward with deletion.
*
* @return bool|null
*/
public function pre_delete_attachment( $delete ) {
if ( is_null( $delete ) ) {
$this->deleting_attachment = true;
}
return $delete;
}
/**
* Takes notice that an attachment has been deleted and undoes previous preparations for the event.
*
* @handles delete_post
*
* Note: delete_post is used as there is a potential that deleted_post is not reached.
*/
public function delete_post() {
$this->deleting_attachment = false;
}
/**
* Has WP Core fixed wp_check_filetype when URL has params yet?
*
* @see https://core.trac.wordpress.org/ticket/30377
* @see https://github.com/aaemnnosttv/fix-wp-media-shortcodes-with-params/blob/master/fix-wp-media-shortcodes-with-params.php
*
* @return bool
*/
public static function wp_check_filetype_broken() {
$normal_file = wp_check_filetype( 'file.mp4', array( 'mp4' => 'video/mp4' ) );
$querys_file = wp_check_filetype( 'file.mp4?param=1', array( 'mp4' => 'video/mp4' ) );
return $normal_file !== $querys_file;
}
/**
* Filters shortcode attributes to temporarily add file extension to end of URL params.
*
* The temporary extension is removed once wp_check_filetype has been used.
*
* The function compensates for when query args or fragments are included in the URL,
* which makes wp_check_filetype fail to see the extension of the file.
*
* @see https://core.trac.wordpress.org/ticket/30377
* @see https://github.com/aaemnnosttv/fix-wp-media-shortcodes-with-params/blob/master/fix-wp-media-shortcodes-with-params.php
*
* @param array $out The output array of shortcode attributes.
* @param array $pairs The supported attributes and their defaults.
* @param array $atts The user defined shortcode attributes.
* @param string $shortcode The shortcode name.
*
* @return array
*/
public function filter_shortcode_atts( $out, $pairs, $atts, $shortcode ) {
$get_media_extensions = "wp_get_{$shortcode}_extensions";
if ( ! function_exists( $get_media_extensions ) ) {
return $out;
}
$default_types = $get_media_extensions();
if ( empty( $default_types ) || ! is_array( $default_types ) ) {
return $out;
}
// URLs can be in src or type specific fallback attributes.
array_unshift( $default_types, 'src' );
$fixes = array();
foreach ( $default_types as $type ) {
if ( empty( $out[ $type ] ) ) {
continue;
}
if ( false !== strpos( $out[ $type ], '&as3cf-fix-wp-check-file-type-ext=.' ) ) {
continue;
}
if ( AS3CF_Utils::is_url( $out[ $type ] ) ) {
$url = $out[ $type ];
$parts = wp_parse_url( $url );
if (
empty( $parts['path'] ) ||
( empty( $parts['query'] ) && empty( $parts['fragment'] ) )
) {
continue;
}
$ext = pathinfo( $parts['path'], PATHINFO_EXTENSION );
if ( empty( $ext ) ) {
continue;
}
$scheme = empty( $parts['scheme'] ) ? '' : $parts['scheme'] . '://';
$user = empty( $parts['user'] ) ? '' : $parts['user'];
$pass = ! empty( $user ) && ! empty( $parts['pass'] ) ? ':' . $parts['pass'] : '';
$auth = ! empty( $user ) ? $user . $pass . '@' : '';
$host = empty( $parts['host'] ) ? '' : $parts['host'];
$port = ! empty( $host ) && ! empty( $parts['port'] ) ? ':' . $parts['port'] : '';
$path = $parts['path'];
$query = empty( $parts['query'] ) ? '?as3cf-fix-wp-check-file-type=true' : '?' . $parts['query'];
if ( ! empty( $parts['fragment'] ) ) {
$query .= '&as3cf-fix-wp-check-file-type-fragment=' . $parts['fragment'];
}
$query .= '&as3cf-fix-wp-check-file-type-ext=.' . $ext;
$out[ $type ] = $scheme . $auth . $host . $port . $path . $query;
$fixes[] = $ext;
}
}
if ( $fixes ) {
add_filter( "wp_{$shortcode}_shortcode", function ( $html ) use ( $fixes ) {
$html = str_replace( '?as3cf-fix-wp-check-file-type=true', '', $html );
foreach ( $fixes as $ext ) {
$html = str_replace( '&#038;as3cf-fix-wp-check-file-type-ext=.' . $ext, '', $html );
}
return str_replace( '&#038;as3cf-fix-wp-check-file-type-fragment=', '#', $html );
} );
}
return $out;
}
}