jsSuffix = '.js'; //use unminified versions for easier debugging } } public function shortPixelJS(){ $upload_dir = wp_upload_dir(); wp_enqueue_script('stateless-short-pixel', ud_get_stateless_media()->path( 'lib/classes/compatibility/js/shortpixel.js', 'url'), array('shortpixel' . $this->jsSuffix), '', true); $image_host = ud_get_stateless_media()->get_gs_host(); $bucketLink = apply_filters('wp_stateless_bucket_link', $image_host); wp_localize_script( 'stateless-short-pixel', '_stateless_short_pixel', array( 'baseurl' => $upload_dir[ 'baseurl' ], 'bucketLink' => $bucketLink, )); } /** * Return Path of ShortPixel backup path. * Bypass the checking whether a backup file exist when returning backup path. * @todo check on GCS if backup really exist. Maybe we can implement transient caching for performance. */ public function getBackupFolderAny( $ret, $file, $thumbs ) { if($ret == false){ $fullSubDir = $this->returnSubDir($file); $ret = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir; } return $ret; } /** * Check whether image exist on GCS. * Only when image is not available on server. */ public function shortpixel_image_exists( $return, $path, $id = null ) { if($return) return $return; $key = "stateless_url_to_postid_" . md5($path); $return = get_transient( $key ); // echo "\npath: $path \nKey: $key\nReturn: $return\nID: $id\n "; if(!$return){ // Checking by matching file name in gs_name and $path. if(!empty($id)){ $metadata = wp_get_attachment_metadata($id); $basename = basename($path); if(!empty($metadata['gs_name'])){ $gs_basename = basename($metadata['gs_name']); if($gs_basename == $basename){ $return = true; } if(is_array($metadata['sizes'])){ foreach ($metadata['sizes'] as $key => &$data) { if(empty($data['gs_name'])) continue; $gs_basename = basename($data['gs_name']); if($gs_basename == $basename){ $return = true; } } } } } // Directly check on GCS if image exist. else if(empty($id)){ $wp_uploads_dir = wp_get_upload_dir(); $gs_name = str_replace(trailingslashit($wp_uploads_dir['basedir']), '', $path); $gs_name = str_replace(trailingslashit($wp_uploads_dir['baseurl']), '', $gs_name); $gs_name = str_replace(trailingslashit(ud_get_stateless_media()->get_gs_host()), '', $gs_name); $gs_name = apply_filters( 'wp_stateless_file_name', $gs_name); if ( $media = ud_get_stateless_media()->get_client()->media_exists( $gs_name ) ) { $return = true; } } set_transient( $key, $return, 10 * MINUTE_IN_SECONDS ); } return $return; } /** * Short-circuit filter. * We need to remove backup image from GCS when shortpixel tries to delete from server. * We are returning true to short-circuit in stateless mood, because file not exist in server. */ public function shortpixel_skip_delete_backups_and_webps($return, $paths){ if(empty($paths) || !is_array($paths)) return $return; $sp__uploads = wp_upload_dir(); $fullSubDir = $this->returnSubDir($paths[0]); $backup_path = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir; foreach ($paths as $key => $path) { // Removing backup $name = apply_filters( 'wp_stateless_file_name', SHORTPIXEL_BACKUP . '/' . $fullSubDir . basename($path)); do_action( 'sm:sync::deleteFile', $name); // Removing WebP $backup_images = \WPShortPixelSettings::getOpt('wp-short-create-webp'); if($backup_images){ $name = str_replace($sp__uploads['basedir'], '', $path); $name = apply_filters( 'wp_stateless_file_name', $name . '.webp'); do_action( 'sm:sync::deleteFile', $name); } } if ( ud_get_stateless_media()->get( 'sm.mode' ) == 'stateless' ) { return true; } // When stateless mode isn't set backup files will be available in server. So no short-circuit. return $return; } /** * Skip the original backup process in stateless mode. */ public function shortpixel_skip_backup( $return, $mainPath, $PATHs ){ if ( ud_get_stateless_media()->get( 'sm.mode' ) == 'stateless' ) { return true; } return $return; } /** * In stateless mode we need to take backup directly from original location. * Because we will skip the backup process of shortpixel. */ public function wp_update_attachment_metadata( $metadata, $attachment_id ){ if ( ud_get_stateless_media()->get( 'sm.mode' ) == 'stateless' ) { $backup_images = \WPShortPixelSettings::getOpt('wp-short-backup_images'); if($backup_images){ $this->sync_backup_file($attachment_id, $metadata, false, array('before_optimization' => true)); } } return $metadata; } /** * Sync image after optimization. * */ public function shortpixel_image_optimised($id){ $metadata = wp_get_attachment_metadata( $id ); ud_get_stateless_media()->add_media( $metadata, $id, true ); // Sync the webp to GCS $create_webp = \WPShortPixelSettings::getOpt('wp-short-create-webp'); if($create_webp){ add_filter( 'upload_mimes', array($this, 'add_webp_mime'), 10, 2 ); ud_get_stateless_media()->add_media( $metadata, $id, true, array('is_webp' => '.webp') ); } // Don't needed in stateless mode. In stateless mode the back will be sync once on wp_update_attachment_metadata filter. if ( ud_get_stateless_media()->get( 'sm.mode' ) !== 'stateless' ) { $this->sync_backup_file($id, $metadata, true); } } /** * Before shortpixel tries to restore from backup we need to make files available on server. */ public function shortpixel_before_restore_image($id, $metadata = null){ $this->sync_backup_file($id, $metadata, true, array('download' => true)); } /** * Disable default shortpixel restore and directly update image on GCS from backup copy in GCS. */ public function shortpixel_skip_restore_image($return, $id = null){ if(ud_get_stateless_media()->get( 'sm.mode' ) === 'stateless'){ $this->client = ud_get_stateless_media()->get_client(); $this->client->copy_media('localhost/ShortpixelBackups/wp-content/uploads/2019/04/htpps.png', 'localhost/2019/04/htpps.png'); return true; } return $return; } /** * Sync backup image * * @param $before_optimization: pass true if you want to sync directly from original path instead of backup path. */ public function sync_backup_file($id, $metadata = null, $force = false, $args = array()){ $args = wp_parse_args($args, array( 'download' => false, // whether to only download. 'before_optimization' => false, // whether to delete local file in stateless mode. )); /* Get metadata in case if method is called directly. */ if( empty($metadata) ) { $metadata = wp_get_attachment_metadata( $id ); } /* Now we go through all available image sizes and upload them to Google Storage */ if( !empty( $metadata[ 'sizes' ] ) && is_array( $metadata[ 'sizes' ] ) ) { // Sync backup file with GCS $file_path = get_attached_file( $id ); $fullSubDir = $this->returnSubDir($file_path); $backup_path = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir; if($args['before_optimization']){ $upload_dir = wp_upload_dir(); $backup_path = $upload_dir['basedir'] . '/' . dirname($metadata[ 'file' ]); $args = array( 'stateless' => false ); } $absolutePath = trailingslashit($backup_path) . basename($metadata[ 'file' ]); $name = apply_filters( 'wp_stateless_file_name', SHORTPIXEL_BACKUP . '/' . $fullSubDir . basename($metadata[ 'file' ])); do_action( 'sm:sync::syncFile', $name, $absolutePath, $force, $args); foreach( (array) $metadata[ 'sizes' ] as $image_size => $data ) { $absolutePath = trailingslashit($backup_path) . $data[ 'file' ]; $name = apply_filters( 'wp_stateless_file_name', SHORTPIXEL_BACKUP . '/' . $fullSubDir . $data[ 'file' ]); do_action( 'sm:sync::syncFile', $name, $absolutePath, $force, $args); } } } /** * Sync from sync tab * */ public function sync_webp_file($id, $metadata = null){ /* Get metadata in case if method is called directly. */ if( empty($metadata) ) { $metadata = wp_get_attachment_metadata( $id ); } add_filter( 'upload_mimes', array($this, 'add_webp_mime'), 10, 2 ); // Sync the webp to GCS ud_get_stateless_media()->add_media( $metadata, $id, true, array('is_webp' => '.webp') ); } /** * return subdir for that particular attached file - if it's media library then last 3 path items, otherwise substract the uploads path * Has trailing directory separator (/) * * @copied from shortpixel-image-optimiser\class\db\shortpixel-meta-facade.php * @param type $file * @return string */ public function returnSubDir($file){ $hp = wp_normalize_path(get_home_path()); $file = wp_normalize_path($file); $sp__uploads = wp_upload_dir(); if(strstr($file, $hp)) { $path = str_replace( $hp, "", $file); } elseif( strstr($file, dirname( WP_CONTENT_DIR ))) { //in some situations the content dir is not inside the root, check this also (ex. single.shortpixel.com) $path = str_replace( trailingslashit(dirname( WP_CONTENT_DIR )), "", $file); } elseif( (strstr(realpath($file), realpath($hp)))) { $path = str_replace( realpath($hp), "", realpath($file)); } elseif( strstr($file, trailingslashit(dirname(dirname( $sp__uploads['basedir'] )))) ) { $path = str_replace( trailingslashit(dirname(dirname( $sp__uploads['basedir'] ))), "", $file); } else { $path = (substr($file, 1)); } $pathArr = explode('/', $path); unset($pathArr[count($pathArr) - 1]); return implode('/', $pathArr) . '/'; } /** * Sync images after shortpixel restore them from backup. */ public function handleRestoreBackup($attachmentID){ $metadata = wp_get_attachment_metadata( $attachmentID ); $this->add_media( $metadata, $attachmentID ); } /** * Customized version of wpCloud\StatelessMedia\Utility::add_media() * to satisfied our need in restore backup * If a image isn't restored from backup then ignore it. */ public static function add_media( $metadata, $attachment_id ) { $upload_dir = wp_upload_dir(); $client = ud_get_stateless_media()->get_client(); if( !is_wp_error( $client ) ) { $fullsizepath = wp_normalize_path( get_attached_file( $attachment_id ) ); // Make non-images uploadable. if( empty( $metadata['file'] ) && $attachment_id ) { $metadata = array( "file" => str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', get_attached_file( $attachment_id ) ) ); } $file = wp_normalize_path( $metadata[ 'file' ] ); $image_host = ud_get_stateless_media()->get_gs_host(); $bucketLink = apply_filters('wp_stateless_bucket_link', $image_host); $_cacheControl = \wpCloud\StatelessMedia\Utility::getCacheControl( $attachment_id, $metadata, null ); $_contentDisposition = \wpCloud\StatelessMedia\Utility::getContentDisposition( $attachment_id, $metadata, null ); $_metadata = array( "width" => isset( $metadata[ 'width' ] ) ? $metadata[ 'width' ] : null, "height" => isset( $metadata[ 'height' ] ) ? $metadata[ 'height' ] : null, 'object-id' => $attachment_id, 'source-id' => md5( $attachment_id . ud_get_stateless_media()->get( 'sm.bucket' ) ), 'file-hash' => md5( $metadata[ 'file' ] ) ); if(file_exists($fullsizepath)){ $file = apply_filters( 'wp_stateless_file_name', $file); /* Add default image */ $media = $client->add_media( $_mediaOptions = array_filter( array( 'force' => true, 'name' => $file, 'absolutePath' => wp_normalize_path( get_attached_file( $attachment_id ) ), 'cacheControl' => $_cacheControl, 'contentDisposition' => $_contentDisposition, 'mimeType' => get_post_mime_type( $attachment_id ), 'metadata' => $_metadata ) )); // Stateless mode: we don't need the local version. if(ud_get_stateless_media()->get( 'sm.mode' ) === 'stateless'){ unlink($fullsizepath); } } /* Now we go through all available image sizes and upload them to Google Storage */ if( !empty( $metadata[ 'sizes' ] ) && is_array( $metadata[ 'sizes' ] ) ) { $path = wp_normalize_path( dirname( get_attached_file( $attachment_id ) ) ); $mediaPath = apply_filters( 'wp_stateless_file_name', trim( dirname( $metadata[ 'file' ] ), '\/\\' ) ); foreach( (array) $metadata[ 'sizes' ] as $image_size => $data ) { $absolutePath = wp_normalize_path( $path . '/' . $data[ 'file' ] ); if( !file_exists($absolutePath)){ continue; } /* Add 'image size' image */ $media = $client->add_media( array( 'force' => true, 'name' => $file_path = trim($mediaPath . '/' . $data[ 'file' ], '/'), 'absolutePath' => $absolutePath, 'cacheControl' => $_cacheControl, 'contentDisposition' => $_contentDisposition, 'mimeType' => $data[ 'mime-type' ], 'metadata' => array_merge( $_metadata, array( 'width' => $data['width'], 'height' => $data['height'], 'child-of' => $attachment_id, 'file-hash' => md5( $data[ 'file' ] ) )) )); /* Break if we have errors. */ if( !is_wp_error( $media ) ) { // Stateless mode: we don't need the local version. if(ud_get_stateless_media()->get( 'sm.mode' ) === 'stateless'){ unlink($absolutePath); } } } } } } // End add_media /** * modifying gs_name and absolutePath so that we can upload webp image using the same Utility::add_media function. */ public function wp_stateless_add_media_args($args){ if(!empty($args['is_webp']) && $args['is_webp']){ if(\file_exists($args['absolutePath'] . '.webp')){ $args['name'] = $args['name'] . '.webp'; $args['absolutePath'] = $args['absolutePath'] . '.webp'; } else{ $pathinfo = pathinfo($args['absolutePath']); $absolutePath = trailingslashit($pathinfo['dirname']) . $pathinfo['filename'] . '.webp'; if(file_exists($absolutePath)){ $args['name'] = $args['name'] . '.webp'; $args['absolutePath'] = $absolutePath; } } $args['mimeType'] = 'image/webp'; } return $args; } /** * Bypass server url check and return base url for GCS image. */ public function shortpixel_webp_image_base($imageBase, $src){ $gs_link = \ud_get_stateless_media()->convert_to_gs_link($src, true); if($gs_link){ $imageBase = trailingslashit(dirname($gs_link)); } return $imageBase; } public function shortpixel_image_urls($URLs, $id){ foreach ($URLs as $key => $url) { $url_parts = wp_parse_url( $url ); if($url_parts['host'] == 'storage.googleapis.com'){ if(preg_match("@(^/?.*?/)(.*)@", $url_parts['path'], $matches)){ $bucket = trim($matches[1], '/'); $url_parts['path'] = $matches[2]; $url_parts['host'] = $bucket . '.' . $url_parts['host']; $URLs[$key] = Utility::join_url($url_parts); } } } return $URLs; } /** * add_webp_mime * */ public function add_webp_mime($t, $user){ $t['webp'] = 'image/webp'; return $t; } } } }