[ 'table' => 'posts', 'class' => 'MediaLibrary' ] ]; /** * Admin constructor. * @since 1.0.0 */ public function __construct() { $this->assets_url = WPMCS_ASSETS_URL; $this->version = WPMCS_VERSION; $this->token = WPMCS_TOKEN; $this->settings = Utils::get_settings(); // Initialize setup $this->registerActions(); } /** * Register integration access */ public function registerActions() { /** IMAGE EDITOR COMPATIBILITY */ add_action( 'attachment_submitbox_misc_actions', array( $this, 'attachment_submitbox_metadata'),99 ); //Media Modal Ajax add_action( 'wp_ajax_wpmcs_get_attachment_details', array( $this, 'ajax_get_attachment_details' ) ); /** URL RE-WRITING HOOKS */ add_filter( 'wp_get_attachment_url', [ $this, 'wp_get_attachment_url' ], 99, 2 ); add_filter( 'wp_get_attachment_image_attributes', [ $this, 'wp_get_attachment_image_attributes' ], 99, 3 ); add_filter( 'wp_calculate_image_srcset', [ $this, 'wp_calculate_image_srcset' ], 99, 5 ); add_filter( 'get_attached_file', [ $this, 'get_attached_file' ], 10, 2 ); add_filter( 'wp_get_original_image_path', [ $this, 'get_attached_file' ], 10, 2 ); add_filter( 'wp_video_shortcode', [ $this, 'wp_media_shortcode' ], 100, 5 ); add_filter( 'wp_audio_shortcode', [ $this, 'wp_media_shortcode' ], 100, 5 ); /** FILE MANAGEMENT HOOKS */ add_filter( 'wp_unique_filename', [ $this, 'wp_unique_filename' ], 10, 3 ); add_filter( 'wp_update_attachment_metadata', [ $this, 'update_attachment_metadata' ], 110, 2 ); add_filter( 'wp_generate_attachment_metadata', [ $this, 'wp_generate_attachment_metadata' ], 110, 3 ); add_filter( 'pre_delete_attachment', [ $this, 'pre_delete_attachment' ], 20 ); add_filter( 'delete_attachment', [ $this, 'delete_attachment' ], 20 ); add_action( 'delete_post', [$this, 'delete_post'] ); add_filter( 'update_attached_file', [ $this, 'update_attached_file' ], 100, 2 ); add_filter( 'load_image_to_edit_path', [ $this, 'load_image_to_edit_path' ], 10, 3 ); } /** * Function to execute on load_image_to_edit_path * @since 1.0.0 * @return string * */ public function load_image_to_edit_path($path, $attachment_id, $size) { $wpmcsItem = Item::instance(); if (!Utils::is_ok_to_serve($attachment_id) && !$wpmcsItem->is_available_from_provider($attachment, true, 'media_library') ) { return $path; } $server_file = false; //If operation is Image editor Image Save if( isset($_REQUEST['do']) && isset($_REQUEST['action']) && $_REQUEST['action'] === 'image-editor' && $_REQUEST['do']==='save' ) { $server_file = $wpmcsItem->moveToServer($attachment_id, $size, false, 'media_library'); } return $server_file ? $server_file : $path; } /** * Allow processes to update the file on provider via update_attached_file() * @since 1.0.0 * @param string $file * @param int $attachment_id * * @return string */ public function update_attached_file($file, $attachment_id) { if( !Utils::is_ok_to_upload($attachment_id) ) { return $file; } $item = Item::instance()->get($attachment_id, 'media_library'); if (Utils::is_empty($item)) { return $file; } $file = apply_filters('wpmcs_update_attached_file', $file, $attachment_id, $item); return $file; } /** * Function to remove data from provider by attachment ID * @since 1.0.0 * */ public function delete_attachment($attachment_id){ if (!Utils::is_service_enabled()) { return $attachment_id; } $wpmcsItem = Item::instance(); $item = $wpmcsItem->get($attachment_id, 'media_library'); if (Utils::is_empty($item)) { return $attachment_id; } $wpmcsItem->delete_attachments_by_item($item); $wpmcsItem->delete($attachment_id, 'media_library'); return $attachment_id; } /** * Function to execute on wp_update_attachment_metadata * @since 1.0.0 * */ public function update_attachment_metadata($attachment_meta, $attachment_id) { $wpmcsItem = Item::instance(); $attachment_id = (int)$attachment_id; $type = get_post_mime_type($attachment_id); $is_image = (0 === strpos($type, 'image/')); $is_image_edit = false; if (!Utils::is_ok_to_upload($attachment_id)) return $attachment_meta; //Generate attachment meta if empty if (empty($attachment_meta)) { $attachment_meta = wp_get_attachment_metadata($attachment_id); } // Get File Name/Path from Image meta $file = isset($attachment_meta) && !empty($attachment_meta) && isset($attachment_meta['file']) && !empty($attachment_meta['file']) ? $attachment_meta['file'] : Utils::get_post_meta((int) $attachment_id, '_wp_attached_file', true); if($file === basename($file)) { $file = Utils::get_post_meta((int) $attachment_id, '_wp_attached_file', true); } // Generate relative path of the file $source_path = Utils::get_attachment_source_path($file); if($source_path){ $existing = $wpmcsItem->get($attachment_id, 'media_library'); $backup = $wpmcsItem->get_backup($attachment_id, 'media_library'); if(Utils::is_empty($existing)) { // First Upload attempt $uploaded_data = $this->uploadMedia($attachment_meta, $attachment_id, $source_path); if(isset($uploaded_data['file'])) { $wpmcsItem->add( $attachment_id, $uploaded_data['file']['url'], $uploaded_data['file']['key'], $uploaded_data['file']['source_path'], $uploaded_data['extra'], 'media_library' ); } } else { if(Utils::is_empty($backup)) { // if no backup available $uploaded_data = $this->uploadMedia($attachment_meta, $attachment_id, $source_path); if( $existing['source_path'] != $source_path ) { // if existing item is different from new $uploaded_data['extra']['backup'] = serialize($existing); $is_image_edit = true; // if existing item is different from new it is image edit } if(isset($uploaded_data['file'])) { $wpmcsItem->update( $attachment_id, [ 'source_path' => $uploaded_data['file']['source_path'], 'url' => $uploaded_data['file']['url'], 'key' => $uploaded_data['file']['key'], 'extra' => maybe_serialize($uploaded_data['extra']), ], 'media_library' ); } } else { // if backup available if($backup['source_path'] != $source_path) { $uploaded_data = $this->uploadMedia($attachment_meta, $attachment_id, $source_path); $uploaded_data['extra']['backup'] = maybe_serialize($backup); if( $existing['source_path'] != $source_path ) { // if existing item is different from new /** * Since the backup is present and it is not the first edit * It is a re-edit, So we don't need intermediate edit state. * So removing cloud and server files immediately */ Item::instance()->delete_attachments_by_item($existing, false); Item::instance()->may_be_delete_server_files_by_item($existing, true); $is_image_edit = true; // if existing item is different from new it is image edit } if(isset($uploaded_data['file'])) { $wpmcsItem->update( $attachment_id, [ 'source_path' => $uploaded_data['file']['source_path'], 'url' => $uploaded_data['file']['url'], 'key' => $uploaded_data['file']['key'], 'extra' => maybe_serialize($uploaded_data['extra']), ], 'media_library' ); } } else { // Restore backup Item::instance()->delete_attachments_by_item($existing, false); $wpmcsItem->update( $attachment_id, [ 'source_path' => $backup['source_path'], 'url' => $backup['url'], 'key' => $backup['key'], 'extra' => $backup['extra'], ], 'media_library' ); } } } } // Remove files from server if enabled ---- Remove main file if it is not image or a new image source url (image edit) $delete_main_file = !$is_image || $is_image_edit; // Remove files from server for backup ---- Remove backup from server if it is image edit (To remove restored file) $delete_backup = $is_image_edit; Item::instance()->may_be_delete_server_files_by_id($attachment_id, 'media_library', $delete_main_file, $delete_backup); return $attachment_meta; } /** * Create unique names for files effects mainly on delete files from server settings * @since 1.0.0 * @return string */ public function wp_unique_filename($filename, $ext, $dir) { // Get Post ID if uploaded in post screen. $post_id = Utils::filter_input( 'post_id', INPUT_POST, FILTER_VALIDATE_INT ); $filename = $this->filter_unique_filename($filename, $ext, $dir, $post_id); return $filename; } /** * filter unique file names * @since 1.0.0 * @return string */ private function filter_unique_filename($filename, $ext, $dir, $post_id = null) { if (!Utils::is_service_enabled()) { 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; return $this->generate_unique_filename($name, $ext, $dir); } /** * Generate unique filename * @since 1.0.0 * @param string $name * @param string $ext * @param string $time * * @return string */ private function generate_unique_filename($name, $ext, $dir) { $upload_dir = wp_get_upload_dir(); $filename = $name . $ext; $no_ext_path = $dir . '/' . $name; $rel_no_ext_path = substr($no_ext_path, strlen(trailingslashit($upload_dir['basedir'])),strlen($no_ext_path)); $path = $dir . '/' . $name . $ext; $source_path = substr($path, strlen(trailingslashit($upload_dir['basedir'])),strlen($path)); $uploaded_files = Item::instance()->get_similar_files_by_path($rel_no_ext_path); if ($uploaded_files !== false) { if (Utils::check_existing_file_names($source_path, $uploaded_files) || file_exists($path)) { $count = 1; $new_file_name = ''; $found = true; while ($found) { $tmp_path = $dir . '/' . $name . '-' . $count . $ext; $rel_temp_path = substr($tmp_path, strlen(trailingslashit($upload_dir['basedir'])),strlen($tmp_path)); if (Utils::check_existing_file_names($rel_temp_path, $uploaded_files) || file_exists($tmp_path)) { $count++; } else { $found = false; $new_file_name = $name . '-' . $count . $ext; } } return $new_file_name; } } else { if (file_exists($path)) { $count = 1; $new_file_name = ''; $found = true; while ($found) { $tmp_path = $dir . '/' . $name . '-' . $count . $ext; if (file_exists($tmp_path)) { $count++; } else { $found = false; $new_file_name = $name . '-' . $count . $ext; } } return $new_file_name; } } return $filename; } /** * Function to execute on wp_generate_attachment_metadata * To delete files from server only after updating all meta * @since 1.0.0 * */ public function wp_generate_attachment_metadata( $attachment_meta, $attachment_id, $action) { if($action == 'create') { $attachment_id = (int)$attachment_id; if (!Utils::is_ok_to_upload($attachment_id)) return $attachment_meta; $item = Item::instance()->get($attachment_id, 'media_library'); if(!Utils::is_empty($item)) { Item::instance()->may_be_delete_server_files_by_id($attachment_id, 'media_library', true, true); } } return $attachment_meta; } /** * 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( '/&_=[0-9]+/', '', $html ); } /** * 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 * @since 1.0.0 * @param string $file * @param int $attachment_id * * @return string */ public function get_attached_file($file, $attachment_id) { $attachment_id = (int)$attachment_id; // During the deletion of an attachment, stream wrapper URLs should not be returned. if ( $this->deleting_attachment ) { return $file; } if (!Utils::is_ok_to_serve($attachment_id)) { return $file; } if( isset($_REQUEST['action']) && $_REQUEST['action'] === 'image-editor' ) { // Avoid rewriting URL when image restore in image editor if(isset($_REQUEST['do']) && $_REQUEST['do']==='restore') { return $file; } // Avoid rewriting URL when image save in image editor if(isset($_REQUEST['do']) && $_REQUEST['do'] === 'save') { return $file; } } $wpmcsItem = Item::instance(); $item = $wpmcsItem->get($attachment_id, 'media_library'); if ( file_exists( $file ) || Utils::is_empty($item)) { if(!Utils::is_empty($item)) { /** * Added filter to allow plugins to copy the pending size to the server * before the file is served. * * @param string $file * @param string $file * @param int $attachment_id */ return apply_filters( 'wpmcs_get_attached_file_noop', $file, $file, $attachment_id ); } else { return $file; } } $url = $wpmcsItem->get_url($attachment_id, 'full', 'media_library'); if(Utils::is_empty($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 ID * @param array $item Item data */ return apply_filters('wpmcs_get_attached_file', $url, $file, $attachment_id, $item); } /** * Change src attributes * @since 1.0.0 */ public function wp_calculate_image_srcset($sources, $size_array, $image_src, $attachment_meta, $attachment_id = 0) { $attachment_id = (int)$attachment_id; // Must need $attachment_id other wise not possible to get data from the table if (!Utils::is_ok_to_serve($attachment_id)) { return $sources; } $wpmcsItem = Item::instance(); $item = $wpmcsItem->get($attachment_id, 'media_library'); if (Utils::is_empty($item)) { return $sources; } $item_extra = $wpmcsItem->get_extras($attachment_id, false, 'media_library'); if (isset($item_extra['width']) && !empty($item_extra['width'])) { $sources[$item_extra['width']]=[ 'url' => $wpmcsItem->get_url($attachment_id, 'full', 'media_library'), 'descriptor' => 'w', 'value' => $item_extra['width'] ]; } if ($item_extra) { if (isset($item_extra['sizes']) && !empty($item_extra['sizes'])) { foreach ($item_extra['sizes'] as $size => $size_array) { if (isset($size_array['width']) && !empty($size_array['width'])) { $w = $size_array['width']; if (isset($sources[$w]) && !empty($sources[$w])) { $sources[$w]['url'] = $wpmcsItem->get_url($attachment_id, $size, 'media_library'); } } } } } return $sources; } /** * Filters the list of attachment image attributes. * * @since 1.0.0 * @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='thumbnail') { if ( !$attachment || !Utils::is_ok_to_serve($attachment->ID) ) { return $attr; } $wpmcsItem = Item::instance(); $item = $wpmcsItem->get($attachment->ID, 'media_library'); if (Utils::is_empty($item)) { return $attr; } $size = Utils::maybe_convert_size_to_string($attachment->ID, $size); if ($size === false) { return $attr; } if ( isset($size) && !empty($size) && isset($attr['src']) && !empty($attr['src']) ) { $source = $wpmcsItem->get_url($attachment->ID, $size, 'media_library'); if (isset($source) && !empty($source)) { $attr['src'] = $source; } } /** * 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. */ return apply_filters('wpmcs_wp_get_attachment_image_attributes', $attr, $attachment, $size, $item); } /** * Get attachment url * @since 1.0.0 * @param string $url * @param int $attachment_id * * @return bool|mixed|WP_Error */ public function wp_get_attachment_url($url, $attachment_id) { if (!Utils::is_ok_to_serve($attachment_id)) { return $url; } $new_url = Item::instance()->get_url($attachment_id, 'full', 'media_library'); if (Utils::is_empty($new_url)) { return $url; } $new_url = apply_filters('wpmcs_wp_get_attachment_url', $new_url, $url, $attachment_id); return $new_url; } /** * Upload media item */ public function uploadMedia($attachment_meta, $attachment_id, $source_path) { $wpmcsItem = Item::instance(); $upload_dir = wp_get_upload_dir(); $type = get_post_mime_type($attachment_id); $is_image = (0 === strpos($type, 'image/')); $existing = $wpmcsItem->get($attachment_id, 'media_library'); $existing_extras = $wpmcsItem->get_extras($attachment_id, false, 'media_library'); $has_existing = !Utils::is_empty($existing); $sizes = []; $uploaded = []; $extras = []; $prefix = ''; $file_path = ''; $file_dir = ''; $original_file_path = ''; $original_file_source_path = ''; // Add prefix if object versioning is ON if (isset($this->settings['object_versioning']) && $this->settings['object_versioning']) { $prefix = ($existing_extras && isset($existing_extras['prefix']) && !empty($existing_extras['prefix'])) ? $existing_extras['prefix'] : Utils::generate_object_versioning_prefix(); $extras['prefix'] = $prefix; } // Get width and height from image meta if (isset($attachment_meta) && !empty($attachment_meta)) { $extras['width'] = (isset($attachment_meta['width']) && !empty($attachment_meta['width'])) ? $attachment_meta['width'] : 0; $extras['height'] = (isset($attachment_meta['height']) && !empty($attachment_meta['height'])) ? $attachment_meta['height'] : 0; } // Get Original File Name/Path from Image meta $original_file = isset($attachment_meta) && !empty($attachment_meta) && isset($attachment_meta['original_image']) && !empty($attachment_meta['original_image']) ? $attachment_meta['original_image'] : ''; $file_path = trailingslashit($upload_dir['basedir']) . $source_path; $file_dir = isset(pathinfo($file_path)['dirname']) ? pathinfo($file_path)['dirname'] : ''; // Check whether the extension is enabled for uploading if (!Utils::is_extension_available($file_path)) { return $attachment_meta; } // Upload the Full Size file if( $has_existing && ( $existing['source_path'] == $source_path ) ) { $uploaded = [ 'success' => true, 'file_url' => $existing['url'], 'key' => $existing['key'] ]; } else if(file_exists($file_path)){ $uploaded = Service::instance()->uploadSingle($file_path, $source_path, $prefix); } if( isset($uploaded) && !empty($uploaded) && isset($uploaded['success']) && $uploaded['success'] ) { if(isset($original_file) && !empty($original_file)){ // Find original image relative and absolute path $original_file_path = trailingslashit($file_dir) . $original_file; $original_file_source_path = Utils::get_attachment_source_path($original_file_path); $uploaded_original = []; // Upload Original File if( !Utils::is_empty($existing_extras) && isset($existing_extras['original']) && !Utils::is_empty($existing_extras['original']) && $existing_extras['original']['source_path'] == $original_file_source_path ) { $uploaded_original = [ 'success' => true, 'file_url' => $existing_extras['original']['url'], 'key' => $existing_extras['original']['key'] ]; } else if(file_exists($original_file_path)) { $uploaded_original = Service::instance()->uploadSingle($original_file_path, $original_file_source_path, $prefix); } if( isset($uploaded_original) && !empty($uploaded_original) && isset($uploaded_original['success']) && $uploaded_original['success'] ) { $extras['original'] = array( 'source_path' => $original_file_source_path, 'url' => $uploaded_original['file_url'], 'key' => $uploaded_original['key'] ); } } if ( $is_image && isset($attachment_meta['sizes']) && !empty($attachment_meta['sizes']) && isset($file_dir) && !empty($file_dir) ) { foreach ($attachment_meta['sizes'] as $size => $sub_image) { $sub_size = []; $sub_file = isset($sub_image['file']) ? $sub_image['file'] : false; $sub_file_path = ''; $sub_file_source_path = ''; $uploaded_sub_image = []; if ($sub_file) { $sub_file_path = $file_dir . '/' . $sub_file; $sub_file_source_path = Utils::get_attachment_source_path($sub_file_path); } // Upload Image size if( !Utils::is_empty($existing_extras) && isset($existing_extras['sizes']) && !Utils::is_empty($existing_extras['sizes']) && isset($existing_extras['sizes'][$size]) && !Utils::is_empty($existing_extras['sizes'][$size]) && $existing_extras['sizes'][$size]['source_path'] == $sub_file_source_path ) { $uploaded_sub_image = [ 'success' => true, 'file_url' => $existing_extras['sizes'][$size]['url'], 'key' => $existing_extras['sizes'][$size]['key'] ]; } else if(file_exists($sub_file_path)) { $uploaded_sub_image = Service::instance()->uploadSingle($sub_file_path, $sub_file_source_path, $prefix); } if ( isset($uploaded_sub_image) && !empty($uploaded_sub_image) && isset($uploaded_sub_image['success']) && $uploaded_sub_image['success'] ) { $sub_size['source_path'] = $sub_file_source_path; $sub_size['url'] = $uploaded_sub_image['file_url']; $sub_size['key'] = $uploaded_sub_image['key']; $sub_size['width'] = isset($sub_image['width']) ? $sub_image['width']: 0; $sub_size['height'] = isset($sub_image['height']) ? $sub_image['height']: 0; } $sizes[$size] = $sub_size; } } if (isset($sizes) && !empty($sizes)) { $extras['sizes'] = $sizes; } return [ 'file' => [ 'source_path' => $source_path, 'url' => $uploaded['file_url'], 'key' => $uploaded['key'], ], 'extra' => $extras ]; } return false; } /** * Get Total Media Count (queried) * * Param added because need a generic function name, * if the function calls by source_type */ public static function get_total_media($source_type) { global $wpdb; // Fetch the count of attachments with the 'inherit' status $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s", 'attachment', 'inherit' ) ); return (int)$count; } /** * Edit Image Meta In View Image * @since 1.0.0 */ function attachment_submitbox_metadata( ) { $post = get_post(); $attachment_id = $post->ID; if (!Utils::is_ok_to_serve($attachment_id)) { return; } $wpmcsItem = Item::instance(); $item = $wpmcsItem->get($attachment_id, 'media_library'); if (Utils::is_empty($item)) { return; } $provider = $wpmcsItem->get_field($attachment_id, 'provider', 'media_library'); if($provider) { $label = Schema::getServiceLabels($provider); ?>