diff --git a/README.md b/README.md index 3d36eff..d44e618 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ So `[file_modified id=870]` would display the last time the file with ID 870 was If you want more control over the format used to display the time, you can use the format option, so `[file_modified id=870 format=Y-m-d]` would display the file modification date but not the time. The format string uses [standard PHP date() formatting tags](http://php.net/manual/en/function.date.php). +## Disable background removal +If you don't want to utilize the background removal feature, add `add_filter('emr/feature/background', '__return_false' );` to your functions.php + *** See [Enable Media Replace](http://wordpress.org/plugins/enable-media-replace/) at WordPress.org for more information. diff --git a/build/shortpixel/composer.json b/build/shortpixel/composer.json index 91d9576..aaa99ea 100644 --- a/build/shortpixel/composer.json +++ b/build/shortpixel/composer.json @@ -1 +1 @@ -{"name":"EnableMediaReplace\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"EnableMediaReplace\\ShortPixelLogger":"log\/src","EnableMediaReplace\\Notices":"notices\/src","EnableMediaReplace\\FileSystem":"filesystem\/src"}}} \ No newline at end of file +{"name":"EnableMediaReplace\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"EnableMediaReplace\\ShortPixelLogger":"log\/src","EnableMediaReplace\\Notices":"notices\/src","EnableMediaReplace\\Replacer":"replacer\/src","EnableMediaReplace\\FileSystem":"filesystem\/src"}}} \ No newline at end of file diff --git a/build/shortpixel/filesystem/src/Model/File/FileModel.php b/build/shortpixel/filesystem/src/Model/File/FileModel.php index 1a6784a..a5e88dc 100644 --- a/build/shortpixel/filesystem/src/Model/File/FileModel.php +++ b/build/shortpixel/filesystem/src/Model/File/FileModel.php @@ -416,6 +416,15 @@ public function getMime() if ($this->exists() && ! $this->is_virtual() ) { $this->mime = wp_get_image_mime($this->fullpath); + if (false === $this->mime) + { + $image_data = wp_check_filetype_and_ext($this->getFullPath(), $this->getFileName()); + if (is_array($image_data) && isset($image_data['type']) && strlen($image_data['type']) > 0) + { + $this->mime = $image_data['type']; + } + + } } else $this->mime = false; @@ -591,7 +600,7 @@ private function relativeToFullPath($path) public function getPermissions() { if (is_null($this->permissions)) - $this->permissions = fileperms($this->fullpath) & 0777; + $this->permissions = fileperms($this->getFullPath()) & 0777; return $this->permissions; } diff --git a/build/shortpixel/notices/src/css/notices.css b/build/shortpixel/notices/src/css/notices.css index dc257fb..2cbb148 100644 --- a/build/shortpixel/notices/src/css/notices.css +++ b/build/shortpixel/notices/src/css/notices.css @@ -1,12 +1,29 @@ .shortpixel.shortpixel-notice { + min-height: 75px; padding: 8px; + display: flex; + align-items: center; background: #fff; + padding: 1px 12px; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); border: 1px solid #c3c4c7; + margin: 15px 0; border-left-width: 4px; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); - margin: 5px 15px 2px; + border-left-color: #72aee6; position: relative; } +.shortpixel.shortpixel-notice span { + vertical-align: middle; +} +.shortpixel.shortpixel-notice span.icon { + margin: 0 25px 0 0; + width: 80px; +} +.shortpixel.shortpixel-notice span.content { + padding: 8px 0; + word-wrap: break-word; + overflow: hidden; +} .shortpixel.shortpixel-notice img { display: inline-block; margin: 0 25px 0 0; diff --git a/build/shortpixel/notices/src/css/notices.css.map b/build/shortpixel/notices/src/css/notices.css.map index 637fcd8..0182154 100644 --- a/build/shortpixel/notices/src/css/notices.css.map +++ b/build/shortpixel/notices/src/css/notices.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["notices.scss"],"names":[],"mappings":"AACA;EAIG;EACF;EACA;EACA;EACA;EACA;EACA;;AAEE;EAEE;EACA;EACA;;AAEF;EAEE;;AAGH;EAEC;;AAED;EAEC;;AAED;EAEC;;AAED;EAEE;;;AAIJ;AACA;EAGE;EACA;EAEA;;AACA;EACE;;AAEF;EAEE;;;AAIJ;EAEE","file":"notices.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["notices.scss"],"names":[],"mappings":"AACA;EAGC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEC;;AACA;EACC;EACA;;AAED;EAEC;EACA;EACA;;AAKA;EAEE;EACA;EACA;;AAEF;EAEE;;AAGH;EAEC;;AAED;EAEC;;AAED;EAEC;;AAED;EAEE;;;AAIJ;AACA;EAGE;EACA;EAEA;;AACA;EACE;;AAEF;EAEE;;;AAIJ;EAEE","file":"notices.css"} \ No newline at end of file diff --git a/build/shortpixel/notices/src/css/notices.scss b/build/shortpixel/notices/src/css/notices.scss index 912028c..e851d24 100644 --- a/build/shortpixel/notices/src/css/notices.scss +++ b/build/shortpixel/notices/src/css/notices.scss @@ -1,16 +1,36 @@ .shortpixel.shortpixel-notice { - //padding: 18px; - //min-height: 50px; - padding: 8px; + + min-height: 75px; + padding: 8px; + display: flex; + align-items: center; background: #fff; + padding: 1px 12px; + box-shadow: 0 1px 1px rgba(0,0,0,0.04); border: 1px solid #c3c4c7; + margin: 15px 0; border-left-width: 4px; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); - margin: 5px 15px 2px; + border-left-color: #72aee6; position: relative; + span + { + vertical-align: middle; + &.icon { + margin: 0 25px 0 0; + width: 80px; + } + &.content + { + padding: 8px 0; + word-wrap: break-word; + overflow: hidden; + //display: flex; // magically fixes verticality issues + } + } + img { display:inline-block; diff --git a/build/shortpixel/replacer/composer.json b/build/shortpixel/replacer/composer.json new file mode 100644 index 0000000..dad4e96 --- /dev/null +++ b/build/shortpixel/replacer/composer.json @@ -0,0 +1,18 @@ +{ + "name": "shortpixel/replacer", + "description": "Content Replacer", + "version": 1.1, + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Bas", + "email": "bas@weblogmechanic.com" + } + ], + "minimum-stability": "dev", + "require": {}, + "autoload": { + "psr-4": { "ShortPixel\\Replacer\\" : "src" } + } +} diff --git a/build/shortpixel/replacer/src/Modules/Elementor.php b/build/shortpixel/replacer/src/Modules/Elementor.php new file mode 100644 index 0000000..ffdc32f --- /dev/null +++ b/build/shortpixel/replacer/src/Modules/Elementor.php @@ -0,0 +1,61 @@ +elementor_is_active()) // elementor is active + { + add_filter('shortpixel/replacer/custom_replace_query', array($this, 'addElementor'), 10, 4); // custom query for elementor \ // problem + // @todo Fix this for SPIO + //add_action('enable-media-replace-upload-done', array($this, 'removeCache') ); + } + } + + public function addElementor($items, $base_url, $search_urls, $replace_urls) + { + $base_url = $this->addSlash($base_url); + $el_search_urls = $search_urls; //array_map(array($this, 'addslash'), $search_urls); + $el_replace_urls = $replace_urls; //array_map(array($this, 'addslash'), $replace_urls); + $items[$this->queryKey] = array('base_url' => $base_url, 'search_urls' => $el_search_urls, 'replace_urls' => $el_replace_urls); + return $items; + } + + public function addSlash($value) + { + global $wpdb; + $value= ltrim($value, '/'); // for some reason the left / isn't picked up by Mysql. + $value= str_replace('/', '\/', $value); + $value = $wpdb->esc_like(($value)); //(wp_slash) / str_replace('/', '\/', $value); + + return $value; + } + + protected function elementor_is_active() + { + $bool = false; + + if (defined('ELEMENTOR_VERSION')) + $bool = true; + + return apply_filters('emr/externals/elementor_is_active', $bool); // manual override + } + + public function removeCache() + { + \Elementor\Plugin::$instance->files_manager->clear_cache(); + } +} diff --git a/build/shortpixel/replacer/src/Modules/WpBakery.php b/build/shortpixel/replacer/src/Modules/WpBakery.php new file mode 100644 index 0000000..d543e9f --- /dev/null +++ b/build/shortpixel/replacer/src/Modules/WpBakery.php @@ -0,0 +1,50 @@ +bakery_is_active()) // elementor is active + { + add_filter('shortpixel/replacer/custom_replace_query', array($this, 'addURLEncoded'), 10, 4); // custom query for elementor \ // problem + } + } + + public function addUrlEncoded($items, $base_url, $search_urls, $replace_urls) + { + $base_url = $this->addEncode($base_url); + $el_search_urls = array_map(array($this, 'addEncode'), $search_urls); + $el_replace_urls = array_map(array($this, 'addEncode'), $replace_urls); + $items[$this->queryKey] = array('base_url' => $base_url, 'search_urls' => $el_search_urls, 'replace_urls' => $el_replace_urls); + return $items; + } + + public function addEncode($value) + { + return urlencode($value); + } + + protected function bakery_is_active() + { + $bool = false; + + // did_action -> wpbakery , VCV_version -> detect Visual Composer + if (did_action('vc_plugins_loaded') || defined('VCV_VERSION')) + $bool = true; + + return apply_filters('emr/externals/urlencode_is_active', $bool); // manual override + } +} diff --git a/build/shortpixel/replacer/src/Modules/YoastSeo.php b/build/shortpixel/replacer/src/Modules/YoastSeo.php new file mode 100644 index 0000000..2d7f97d --- /dev/null +++ b/build/shortpixel/replacer/src/Modules/YoastSeo.php @@ -0,0 +1,71 @@ +yoast_is_active()) // elementor is active + { + global $wpdb; + $this->yoastTable = $wpdb->prefix . 'yoast_indexable'; + + add_action('shortpixel/replacer/replace_urls', array($this, 'removeIndexes'),10,2); + } + } + + public function removeIndexes($search_urls, $replace_urls) + { + global $wpdb; + + $sql = 'DELETE FROM ' . $this->yoastTable . ' WHERE '; + $prepare = array(); + + $base = isset($search_urls['base']) ? $search_urls['base'] : null; + $file = isset($search_urls['file']) ? $search_urls['file'] : null; + + if (! is_null($base)) + { + $querySQL = $sql . ' twitter_image like %s or open_graph_image like %s '; + $querySQL = $wpdb->prepare($querySQL, '%' . $base . '%', '%' . $base . '%'); + + $wpdb->query($querySQL); + } + + if (! is_null($file)) + { + $querySQL = $sql . ' twitter_image like %s or open_graph_image like %s '; + $querySQL = $wpdb->prepare($querySQL, '%' . $file . '%', '%' . $file . '%'); + + $wpdb->query($querySQL); + } + + } + + protected function yoast_is_active() + { + if (defined('WPSEO_VERSION')) + { + return true; + } + return false; + } + + + + +} diff --git a/build/shortpixel/replacer/src/Replacer.php b/build/shortpixel/replacer/src/Replacer.php new file mode 100644 index 0000000..d4b570f --- /dev/null +++ b/build/shortpixel/replacer/src/Replacer.php @@ -0,0 +1,488 @@ +source_url = $source_url; + ///$this->target_url = $target_url; + $this->loadFormats(); + } + + // Load classes that handle alternative formats that can occur in the metadata / post data. + protected function loadFormats() + { + Modules\Elementor::getInstance(); + Modules\WpBakery::getInstance(); + Modules\YoastSeo::getInstance(); + } + + public function setSource($url) + { + $this->source_url = $url; + } + + public function getSource() + { + return $this->source_url; + } + + public function setTarget($url) + { + $this->target_url = $url; + } + + public function getTarget() + { + return $this->target_url; + } + + public function setSourceMeta($meta) + { + $this->source_metadata = $meta; + } + + public function setTargetMeta($meta) + { + $this->target_metadata = $meta; + } + + public function replace($args = array()) + { + if (is_null($this->source_url) || is_null($this->target_url)) + { + Log::addWarn('Replacer called without source or target '); + return false; + } + $defaults = array( + 'thumbnails_only' => false, + ); + + $errors = array(); + $args = wp_parse_args($args, $defaults); + + // Search-and-replace filename in post database + // @todo Check this with scaled images. + $base_url = parse_url($this->source_url, PHP_URL_PATH);// emr_get_match_url( $this->source_url); + $base_url = str_replace('.' . pathinfo($base_url, PATHINFO_EXTENSION), '', $base_url); + + /** Fail-safe if base_url is a whole directory, don't go search/replace */ + if (is_dir($base_url)) + { + Log::addError('Search Replace tried to replace to directory - ' . $base_url); + $errors[] = __('Fail Safe :: Source Location seems to be a directory.', 'enable-media-replace'); + return $errors; + } + + if (strlen(trim($base_url)) == 0) + { + Log::addError('Current Base URL emtpy - ' . $base_url); + $errors[] = __('Fail Safe :: Source Location returned empty string. Not replacing content','enable-media-replace'); + return $errors; + } + + // get relurls of both source and target. + $urls = $this->getRelativeURLS(); + + + if ($args['thumbnails_only']) + { + foreach($urls as $side => $data) + { + if (isset($data['base'])) + { + unset($urls[$side]['base']); + } + if (isset($data['file'])) + { + unset($urls[$side]['file']); + } + } + } + + $search_urls = $urls['source']; + $replace_urls = $urls['target']; + + /* If the replacement is much larger than the source, there can be more thumbnails. This leads to disbalance in the search/replace arrays. + Remove those from the equation. If the size doesn't exist in the source, it shouldn't be in use either */ + foreach($replace_urls as $size => $url) + { + if (! isset($search_urls[$size])) + { + Log::addDebug('Dropping size ' . $size . ' - not found in source urls'); + unset($replace_urls[$size]); + } + } + + Log::addDebug('Source', $this->source_metadata); + Log::addDebug('Target', $this->target_metadata); + /* If on the other hand, some sizes are available in source, but not in target, try to replace them with something closeby. */ + foreach($search_urls as $size => $url) + { + if (! isset($replace_urls[$size])) + { + $closest = $this->findNearestSize($size); + if ($closest) + { + $sourceUrl = $search_urls[$size]; + $baseurl = trailingslashit(str_replace(wp_basename($sourceUrl), '', $sourceUrl)); + Log::addDebug('Nearest size of source ' . $size . ' for target is ' . $closest); + $replace_urls[$size] = $baseurl . $closest; + } + else + { + Log::addDebug('Unset size ' . $size . ' - no closest found in source'); + } + } + } + + /* If source and target are the same, remove them from replace. This happens when replacing a file with same name, and +/- same dimensions generated. + + After previous loops, for every search there should be a replace size. + */ + foreach($search_urls as $size => $url) + { + $replace_url = isset($replace_urls[$size]) ? $replace_urls[$size] : false; + if ($url == $replace_url) // if source and target as the same, no need for replacing. + { + unset($search_urls[$size]); + unset($replace_urls[$size]); + } + } + + // If the two sides are disbalanced, the str_replace part will cause everything that has an empty replace counterpart to replace it with empty. Unwanted. + if (count($search_urls) !== count($replace_urls)) + { + Log::addError('Unbalanced Replace Arrays, aborting', array($search_urls, $replace_urls, count($search_urls), count($replace_urls) )); + $errors[] = __('There was an issue with updating your image URLS: Search and replace have different amount of values. Aborting updating thumbnails', 'enable-media-replace'); + return $errors; + } + + Log::addDebug('Doing meta search and replace -', array($search_urls, $replace_urls) ); + Log::addDebug('Searching with BaseuRL ' . $base_url); + + do_action('shortpixel/replacer/replace_urls', $search_urls, $replace_urls); + $updated = 0; + + $updated += $this->doReplaceQuery($base_url, $search_urls, $replace_urls); + + $replaceRuns = apply_filters('shortpixel/replacer/custom_replace_query', array(), $base_url, $search_urls, $replace_urls); + Log::addDebug("REPLACE RUNS", $replaceRuns); + foreach($replaceRuns as $component => $run) + { + Log::addDebug('Running additional replace for : '. $component, $run); + $updated += $this->doReplaceQuery($run['base_url'], $run['search_urls'], $run['replace_urls']); + } + //do_action('') + + Log::addDebug("Updated Records : " . $updated); + return $updated; + } + + private function doReplaceQuery($base_url, $search_urls, $replace_urls) + { + global $wpdb; + /* Search and replace in WP_POSTS */ + // Removed $wpdb->remove_placeholder_escape from here, not compatible with WP 4.8 + $posts_sql = $wpdb->prepare( + "SELECT ID, post_content FROM $wpdb->posts WHERE post_status = 'publish' AND post_content LIKE %s", + '%' . $base_url . '%'); + + $rs = $wpdb->get_results( $posts_sql, ARRAY_A ); + $number_of_updates = 0; + + if ( ! empty( $rs ) ) { + foreach ( $rs AS $rows ) { + $number_of_updates = $number_of_updates + 1; + // replace old URLs with new URLs. + + $post_content = $rows["post_content"]; + $post_id = $rows['ID']; + $replaced_content = $this->replaceContent($post_content, $search_urls, $replace_urls); + + if ($replaced_content !== $post_content) + { + + // $result = wp_update_post($post_ar); + $sql = 'UPDATE ' . $wpdb->posts . ' SET post_content = %s WHERE ID = %d'; + $sql = $wpdb->prepare($sql, $replaced_content, $post_id); + + $result = $wpdb->query($sql); + + if ($result === false) + { + Notice::addError('Something went wrong while replacing' . $result->get_error_message() ); + Log::addError('WP-Error during post update', $result); + } + } + + } + } + + $number_of_updates += $this->handleMetaData($base_url, $search_urls, $replace_urls); + return $number_of_updates; + } + + private function handleMetaData($url, $search_urls, $replace_urls) + { + global $wpdb; + + $meta_options = apply_filters('shortpixel/replacer/metadata_tables', array('post', 'comment', 'term', 'user')); + $number_of_updates = 0; + + foreach($meta_options as $type) + { + switch($type) + { + case "post": // special case. + $sql = 'SELECT meta_id as id, meta_key, meta_value FROM ' . $wpdb->postmeta . ' + WHERE post_id in (SELECT ID from '. $wpdb->posts . ' where post_status = "publish") AND meta_value like %s'; + $type = 'post'; + + $update_sql = ' UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d'; + break; + default: + $table = $wpdb->{$type . 'meta'}; // termmeta, commentmeta etc + + $meta_id = 'meta_id'; + if ($type == 'user') + $meta_id = 'umeta_id'; + + $sql = 'SELECT ' . $meta_id . ' as id, meta_value FROM ' . $table . ' + WHERE meta_value like %s'; + + $update_sql = " UPDATE $table set meta_value = %s WHERE $meta_id = %d "; + break; + } + + $sql = $wpdb->prepare($sql, '%' . $url . '%'); + + // This is a desparate solution. Can't find anyway for wpdb->prepare not the add extra slashes to the query, which messes up the query. + // $postmeta_sql = str_replace('[JSON_URL]', $json_url, $postmeta_sql); + $rsmeta = $wpdb->get_results($sql, ARRAY_A); + + if (! empty($rsmeta)) + { + foreach ($rsmeta as $row) + { + $number_of_updates++; + $content = $row['meta_value']; + + + $id = $row['id']; + + $content = $this->replaceContent($content, $search_urls, $replace_urls); //str_replace($search_urls, $replace_urls, $content); + + $prepared_sql = $wpdb->prepare($update_sql, $content, $id); + + Log::addDebug('Update Meta SQl' . $prepared_sql); + $result = $wpdb->query($prepared_sql); + + } + } + } // foreach + + return $number_of_updates; + } // function + + + + /** + * Replaces Content across several levels of possible data + * @param $content String The Content to replace + * @param $search String Search string + * @param $replace String Replacement String + * @param $in_deep Boolean. This is use to prevent serialization of sublevels. Only pass back serialized from top. + */ + private function replaceContent($content, $search, $replace, $in_deep = false) + { + //$is_serial = false; + $content = maybe_unserialize($content); + $isJson = $this->isJSON($content); + + if ($isJson) + { + $content = json_decode($content); + Log::addDebug('JSon Content', $content); + } + + if (is_string($content)) // let's check the normal one first. + { + $content = apply_filters('shortpixel/replacer/content', $content, $search, $replace); + + $content = str_replace($search, $replace, $content); + } + elseif (is_wp_error($content)) // seen this. + { + //return $content; // do nothing. + } + elseif (is_array($content) ) // array metadata and such. + { + foreach($content as $index => $value) + { + $content[$index] = $this->replaceContent($value, $search, $replace, true); //str_replace($value, $search, $replace); + if (is_string($index)) // If the key is the URL (sigh) + { + $index_replaced = $this->replaceContent($index, $search,$replace, true); + if ($index_replaced !== $index) + $content = $this->change_key($content, array($index => $index_replaced)); + } + } + } + elseif(is_object($content)) // metadata objects, they exist. + { + foreach($content as $key => $value) + { + $content->{$key} = $this->replaceContent($value, $search, $replace, true); //str_replace($value, $search, $replace); + } + } + + if ($isJson && $in_deep === false) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically. + { + Log::addDebug('Value was found to be JSON, encoding'); + // wp-slash -> WP does stripslashes_deep which destroys JSON + $content = json_encode($content, JSON_UNESCAPED_SLASHES); + Log::addDebug('Content returning', array($content)); + } + elseif($in_deep === false && (is_array($content) || is_object($content))) + $content = maybe_serialize($content); + + return $content; + } + + private function change_key($arr, $set) { + if (is_array($arr) && is_array($set)) { + $newArr = array(); + foreach ($arr as $k => $v) { + $key = array_key_exists( $k, $set) ? $set[$k] : $k; + $newArr[$key] = is_array($v) ? $this->change_key($v, $set) : $v; + } + return $newArr; + } + return $arr; + } + + private function getRelativeURLS() + { + $dataArray = array( + 'source' => array('url' => $this->source_url, 'metadata' => $this->getFilesFromMetadata($this->source_metadata) ), + 'target' => array('url' => $this->target_url, 'metadata' => $this->getFilesFromMetadata($this->target_metadata) ), + ); + + // Log::addDebug('Source Metadata', $this->source_metadata); + // Log::addDebug('Target Metadata', $this->target_metadata); + + $result = array(); + + foreach($dataArray as $index => $item) + { + $result[$index] = array(); + $metadata = $item['metadata']; + + $baseurl = parse_url($item['url'], PHP_URL_PATH); + $result[$index]['base'] = $baseurl; // this is the relpath of the mainfile. + $baseurl = trailingslashit(str_replace( wp_basename($item['url']), '', $baseurl)); // get the relpath of main file. + + foreach($metadata as $name => $filename) + { + $result[$index][$name] = $baseurl . wp_basename($filename); // filename can have a path like 19/08 etc. + } + + } + // Log::addDebug('Relative URLS', $result); + return $result; + } + + + private function getFilesFromMetadata($meta) + { + $fileArray = array(); + if (isset($meta['file'])) + $fileArray['file'] = $meta['file']; + + if (isset($meta['sizes'])) + { + foreach($meta['sizes'] as $name => $data) + { + if (isset($data['file'])) + { + $fileArray[$name] = $data['file']; + } + } + } + return $fileArray; + } + + /** FindNearestsize + * This works on the assumption that when the exact image size name is not available, find the nearest width with the smallest possible difference to impact the site the least. + */ + private function findNearestSize($sizeName) + { + + if (! isset($this->source_metadata['sizes'][$sizeName]) || ! isset($this->target_metadata['width'])) // This can happen with non-image files like PDF. + { + // Check if metadata-less item is a svg file. Just the main file to replace all thumbnails since SVG's don't need thumbnails. + if (strpos($this->target_url, '.svg') !== false) + { + $svg_file = wp_basename($this->target_url); + return $svg_file; // this is the relpath of the mainfile. + } + + return false; + } + $old_width = $this->source_metadata['sizes'][$sizeName]['width']; // the width from size not in new image + $new_width = $this->target_metadata['width']; // default check - the width of the main image + + $diff = abs($old_width - $new_width); + // $closest_file = str_replace($this->relPath, '', $this->newMeta['file']); + $closest_file = wp_basename($this->target_metadata['file']); // mainfile as default + + foreach($this->target_metadata['sizes'] as $sizeName => $data) + { + $thisdiff = abs($old_width - $data['width']); + + if ( $thisdiff < $diff ) + { + $closest_file = $data['file']; + if(is_array($closest_file)) { $closest_file = $closest_file[0];} // HelpScout case 709692915 + if(!empty($closest_file)) { + $diff = $thisdiff; + $found_metasize = true; + } + } + } + + if(empty($closest_file)) return false; + + return $closest_file; + } + + /* Check if given content is JSON format. */ + private function isJSON($content) + { + if (is_array($content) || is_object($content)) + return false; // can never be. + + $json = json_decode($content); + return $json && $json != $content; + } + + +} // class diff --git a/classes/Controller/RemoteNoticeController.php b/classes/Controller/RemoteNoticeController.php new file mode 100644 index 0000000..f371a79 --- /dev/null +++ b/classes/Controller/RemoteNoticeController.php @@ -0,0 +1,118 @@ +doRemoteNotices(); + } + + public static function getInstance() + { + if ( is_null(self::$instance)) + { + self::$instance = new RemoteNoticeController(); + } + + return self::$instance; + } + + protected function doRemoteNotices() + { + $notices = $this->get_remote_notices(); + + if (! is_array($notices)) + return; + + foreach($notices as $remoteNotice) + { + if (! isset($remoteNotice->id) && ! isset($remoteNotice->message)) + return; + + if (! isset($remoteNotice->type)) + $remoteNotice->type = 'notice'; + + $message = esc_html($remoteNotice->message); + $id = sanitize_text_field($remoteNotice->id); + + $noticeController = Notices::getInstance(); + $noticeObj = $noticeController->getNoticeByID($id); + + // not added to system yet + if ($noticeObj === false) + { + switch ($remoteNotice->type) + { + case 'warning': + $new_notice = Notices::addWarning($message); + break; + case 'error': + $new_notice = Notices::addError($message); + break; + case 'notice': + default: + $new_notice = Notices::addNormal($message); + break; + } + + Notices::makePersistent($new_notice, $id, MONTH_IN_SECONDS); + } + + } + } + + private function get_remote_notices() + { + $transient_name = 'emr_remote_notice'; + $transient_duration = DAY_IN_SECONDS; + + + // $keyControl = new apiKeyController(); + //$keyControl->loadKey(); + + $notices = get_transient($transient_name); + $url = $this->remote_message_endpoint; + $url = add_query_arg(array( // has url + 'version' => EMR_VERSION, + 'plugin' => 'enable-media-replace', + 'target' => 4, + + ), $url); + + + if ( $notices === false || $notices == 'none' ) { + $notices_response = wp_safe_remote_request( $url ); + + $content = false; + if (! is_wp_error( $notices_response ) ) + { + $notices = json_decode($notices_response['body']); + + if (! is_array($notices)) + $notices = 'none'; + + // Save transient anywhere to prevent over-asking when nothing good is there. + set_transient( $transient_name, 'true', $transient_duration ); + } + else + { + set_transient( $transient_name, false, $transient_duration ); + } + } + + return $notices; + } + +} // class diff --git a/classes/Controller/ReplaceController.php b/classes/Controller/ReplaceController.php new file mode 100644 index 0000000..08f8472 --- /dev/null +++ b/classes/Controller/ReplaceController.php @@ -0,0 +1,529 @@ +post_id = $post_id; + $this->setupSource(); + } + + /* getSourceFile + * + * @param $untranslated boolean if file is offloaded, this indicates to return remote variant. Used for displaying preview + */ + public function getSourceFile($untranslated = false) + { + + if (true === $untranslated && ! is_null($this->sourceFileUntranslated)) + { + return $this->sourceFileUntranslated; + } + return $this->sourceFile; + } + + public function setupParams($params) + { + $this->replaceType = ($params['replace_type'] === 'replace_and_search') ? self::MODE_SEARCHREPLACE : self::MODE_REPLACE; + + if ($this->replaceType == self::MODE_SEARCHREPLACE && true === $params['new_location'] && ! is_null($params['location_dir'])) + { + $this->new_location = $params['location_dir']; + } + + $this->timeMode = $params['timestamp_replace']; + $this->newDate = $params['new_date']; + + $this->new_filename = $params['new_filename']; + $this->tmpUploadPath = $params['uploadFile']; + + $targetFile = $this->setupTarget(); + if (is_null($targetFile)) + { + return false; + } + $this->targetFile = $this->fs()->getFile($targetFile); + + return true; + } + + public function returnLastError() + { + return $this->lastError; + } + + public function run() + { + do_action('wp_handle_replace', array('post_id' => $this->post_id)); + + // Set Source / and Source Metadata + $Replacer = new Replacer(); + $source_url = $this->getSourceUrl(); + $Replacer->setSource($source_url); + $Replacer->setSourceMeta(wp_get_attachment_metadata( $this->post_id )); + + $targetFileObj = $this->fs()->getFile($this->targetFile); + + $directoryObj = $targetFileObj->getFileDir(); + $result = $directoryObj->check(); + + if ($result === false) + { + Log::addError('Directory creation for targetFile failed'); + } + + $permissions = ($this->sourceFile->exists() ) ? $this->sourceFile->getPermissions() : -1; + + $this->removeCurrent(); // tries to remove the current files. + + $fileObj = $this->fs()->getFile($this->tmpUploadPath); + $copied = $fileObj->copy($targetFileObj); + + if (false === $copied) + { + if ($targetFileObj->exists()) + { + Log::addDebug('Copy declared failed, but target available'); + } + else { + $this->lastError = self::ERROR_COPY_FAILED; + } + } + + $deleted = $fileObj->delete(); + if (false === $deleted) + { + Log::addWarn('Temp file could not be removed. Permission issues?'); + } + + $this->targetFile->resetStatus(); // reinit target file because it came into existence. + + if ($permissions > 0) + chmod( $this->targetFile->getFullPath(), $permissions ); // restore permissions + else { + Log::addWarn('Setting permissions failed'); + } + + // Uspdate the file attached. This is required for wp_get_attachment_url to work. + // Using RawFullPath because FullPath does normalize path, which update_attached_file doesn't so in case of windows / strange Apspaths it fails. + $updated = update_attached_file($this->post_id, $this->targetFile->getRawFullPath() ); + if (! $updated) + { + Log::addError('Update Attached File reports as not updated or same value'); + } + + // Run the filter, so other plugins can hook if needed. + $filtered = apply_filters( 'wp_handle_upload', array( + 'file' => $this->targetFile->getFullPath(), + 'url' => $this->getTargetURL(), + 'type' => $this->targetFile->getMime(), + ), 'sideload'); + + // check if file changed during filter. Set changed to attached file meta properly. + if (isset($filtered['file']) && $filtered['file'] != $this->targetFile->getFullPath() ) + { + update_attached_file($this->post_id, $filtered['file'] ); + $this->targetFile = $this->fs()->getFile($filtered['file']); // handle as a new file + Log::addInfo('WP_Handle_upload filter returned different file', $filtered); + } + + $target_url = $this->getTargetURL(); + $Replacer->setTarget($target_url); + + // Check and update post mimetype, otherwise badly coded plugins cry. + $post_mime = get_post_mime_type($this->post_id); + $target_mime = $this->targetFile->getMime(); + + // update DB post mime type, if somebody decided to mess it up, and the target one is not empty. + if ($target_mime !== $post_mime && strlen($target_mime) > 0) + { + \wp_update_post(array('post_mime_type' => $this->targetFile->getMime(), 'ID' => $this->post_id)); + } + + do_action('emr/converter/prevent-offload', $this->post_id); + $target_metadata = wp_generate_attachment_metadata( $this->post_id, $this->targetFile->getFullPath() ); + do_action('emr/converter/prevent-offload-off', $this->post_id); + + wp_update_attachment_metadata( $this->post_id, $target_metadata ); + + $Replacer->setTargetMeta($target_metadata); + //$this->target_metadata = $metadata; + + /** If author is different from replacer, note this */ + $post_author = get_post_field( 'post_author', $this->post_id ); + $author_id = get_post_meta($this->post_id, '_emr_replace_author', true); + + if ( intval($post_author) !== get_current_user_id()) + { + update_post_meta($this->post_id, '_emr_replace_author', get_current_user_id()); + } + elseif ($author_id) + { + delete_post_meta($this->post_id, '_emr_replace_author'); + } + + + if ($this->replaceType == self::MODE_SEARCHREPLACE) + { + // Write new image title. + $title = $this->getNewTitle($target_metadata); + $excerpt = $this->getNewExcerpt($target_metadata); + $update_ar = array('ID' => $this->post_id); + $update_ar['post_title'] = $title; + $update_ar['post_name'] = sanitize_title($title); + if ($excerpt !== false) + { + $update_ar['post_excerpt'] = $excerpt; + } + $update_ar['guid'] = $target_url; //wp_get_attachment_url($this->post_id); + + $post_id = \wp_update_post($update_ar, true); + + global $wpdb; + // update post doesn't update GUID on updates. + $wpdb->update( $wpdb->posts, array( 'guid' => $target_url), array('ID' => $this->post_id) ); + //enable-media-replace-upload-done + + // @todo This error in general ever happens? + if (is_wp_error($post_id)) + { + $this->lastError = self::ERROR_UPDATE_POST; + } + + } + + /// Here run the Replacer Module + $args = array( + 'thumbnails_only' => ($this->replaceType == self::MODE_SEARCHREPLACE) ? false : true, + ); + $Replacer->replace($args); + + // Here Updatedata and a ffew others. + $this->updateDate(); + + // Give the caching a kick. Off pending specifics. + $cache_args = array( + 'flush_mode' => 'post', + 'post_id' => $this->post_id, + ); + + $cache = new emrCache(); + $cache->flushCache($cache_args); + + do_action("enable-media-replace-upload-done", $target_url, $source_url, $this->post_id); + + return true; + } // run + + + protected function setupSource() + { + $source_file = false; + if (function_exists('wp_get_original_image_path')) // WP 5.3+ + { + $source_file = wp_get_original_image_path($this->post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )); + // For offload et al to change path if wrong. + $source_file = apply_filters('emr/replace/original_image_path', $source_file, $this->post_id); + } + + if (false === $source_file) + { + $source_file = trim(get_attached_file($this->post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ))); + } + + $sourceFileObj = $this->fs()->getFile($source_file); + if ($sourceFileObj->is_virtual()) + { + $this->sourceFileUntranslated = $this->fs()->getFile($source_file); + $sourcePath = apply_filters('emr/file/virtual/translate', $sourceFileObj->getFullPath(), $sourceFileObj); + + if ($sourceFileObj->getFullPath() !== $sourcePath) + { + $sourceFileObj = $this->fs()->getFile($sourcePath); + $source_file = $sourcePath; + } + } + /* It happens that the SourceFile returns relative / incomplete when something messes up get_upload_dir with an error something. + This case shoudl be detected here and create a non-relative path anyhow.. + */ + + if (! file_exists($source_file) && $source_file && 0 !== strpos( $source_file, '/' ) && ! preg_match( '|^.:\\\|', $source_file ) ) + { + $file = get_post_meta( $this->post_id, '_wp_attached_file', true ); + $uploads = wp_get_upload_dir(); + $source_file = $uploads['basedir'] . "/$source_file"; + } + + Log::addDebug('SourceFile ' . $source_file); + $this->sourceFile = $this->fs()->getFile($source_file); + } + + /** Returns a full target path to place to new file. Including the file name! **/ + protected function setupTarget() + { + $targetPath = null; + if ($this->replaceType == self::MODE_REPLACE) + { + $targetFile = $this->getSourceFile()->getFullPath(); // overwrite source + } + elseif ($this->replaceType == self::MODE_SEARCHREPLACE) + { + $path = (string) $this->getSourceFile()->getFileDir(); + $targetLocation = $this->getNewTargetLocation(); + if (false === $targetLocation) + { + return null; + } + + if (false === is_null($this->new_location)) // Replace to another path. + { + $otherTarget = $this->fs()->getFile($targetLocation . $this->new_filename); + // Halt if new target exists, but not if it's the same ( overwriting itself ) + if ($otherTarget->exists() && $otherTarget->getFullPath() !== $this->getSourceFile()->getFullPath() ) + { + $this->lastError = self::ERROR_TARGET_EXISTS; + return null; + } + + $path = $targetLocation; // $this->target_location; // if all went well. + } + //if ($this->sourceFile->getFileName() == $this->targetName) + $targetpath = $path . $this->new_filename; + + // If the source and target path AND filename are identical, user has wrong mode, just overwrite the sourceFile. + if ($targetpath == $this->sourceFile->getFullPath()) + { + $unique = $this->sourceFile->getFileName(); + $this->replaceType == self::MODE_REPLACE; + } + else + { + $unique = wp_unique_filename($path, $this->new_filename); + } + $new_filename = apply_filters( 'emr_unique_filename', $unique, $path, $this->post_id ); + $targetFile = trailingslashit($path) . $new_filename; + } + if (is_dir($targetFile)) // this indicates an error with the source. + { + Log::addWarn('TargetFile is directory ' . $targetFile ); + $upload_dir = wp_upload_dir(); + if (isset($upload_dir['path'])) + { + $targetFile = trailingslashit($upload_dir['path']) . wp_unique_filename($targetFile, $this->new_filename); + } + else { + + $this->lastError = self::ERROR_DESTINATION_FAIL; + return null; + } + } + return $targetFile; + } + + protected function getNewTitle($meta) + { + // get basename without extension + $title = basename($this->targetFile->getFileName(), '.' . $this->targetFile->getExtension()); + // $meta = $this->target_metadata; + + if (isset($meta['image_meta'])) + { + if (isset($meta['image_meta']['title'])) + { + if (strlen($meta['image_meta']['title']) > 0) + { + $title = $meta['image_meta']['title']; + } + } + } + + // Thanks Jonas Lundman (http://wordpress.org/support/topic/add-filter-hook-suggestion-to) + $title = apply_filters( 'enable_media_replace_title', $title ); + + return $title; + } + + protected function getNewExcerpt($meta) + { + // $meta = $this->target_metadata; + $excerpt = false; + + if (isset($meta['image_meta'])) + { + if (isset($meta['image_meta']['caption'])) + { + if (strlen($meta['image_meta']['caption']) > 0) + { + $excerpt = $meta['image_meta']['caption']; + } + } + } + + return $excerpt; + } + + protected function getSourceUrl() + { + if (function_exists('wp_get_original_image_url')) // WP 5.3+ + { + $source_url = wp_get_original_image_url($this->post_id); + if ($source_url === false) // not an image, or borked, try the old way + $source_url = wp_get_attachment_url($this->post_id); + + $source_url = $source_url; + } + else + $source_url = wp_get_attachment_url($this->post_id); + + return $source_url; + } + + /** Handle new dates for the replacement */ + protected function updateDate() + { + global $wpdb; + $post_date = $this->newDate; + $post_date_gmt = get_gmt_from_date($post_date); + + $update_ar = array('ID' => $this->post_id); + if ($this->timeMode == static::TIME_UPDATEALL || $this->timeMode == static::TIME_CUSTOM) + { + $update_ar['post_date'] = $post_date; + $update_ar['post_date_gmt'] = $post_date_gmt; + } + else { + + } + $update_ar['post_modified'] = $post_date; + $update_ar['post_modified_gmt'] = $post_date_gmt; + + $updated = $wpdb->update( $wpdb->posts, $update_ar , array('ID' => $this->post_id) ); + + wp_cache_delete($this->post_id, 'posts'); + + } + + /** Tries to remove all of the old image, without touching the metadata in database + * This might fail on certain files, but this is not an indication of success ( remove might fail, but overwrite can still work) + */ + protected function removeCurrent() + { + $meta = \wp_get_attachment_metadata( $this->post_id ); + $backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true ); + + // this must be -scaled if that exists, since wp_delete_attachment_files checks for original_files but doesn't recheck if scaled is included since that the one 'that exists' in WP . $this->source_file replaces original image, not the -scaled one. + $file = $this->sourceFile->getFullPath(); + $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $file ); + + // If Attached file is not the same path as file, this indicates a -scaled images is in play. + // Also plugins like Polylang tend to block delete image while there is translation / duplicate item somewhere + // 10/06/22 : Added a hard delete if file still exists. Be gone, hard way. + $attached_file = get_attached_file($this->post_id); + if (file_exists($attached_file)) + { + @unlink($attached_file); + } + + do_action( 'emr_after_remove_current', $this->post_id, $meta, $backup_sizes, $this->sourceFile, $this->targetFile ); + } + + /** Since WP functions also can't be trusted here in certain cases, create the URL by ourselves */ + protected function getTargetURL() + { + if (is_null($this->targetFile)) + { + Log::addError('TargetFile NULL ', debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10)); + return false; + } + //$uploads['baseurl'] + $url = wp_get_attachment_url($this->post_id); + $url_basename = basename($url); + + // Seems all worked as normal. + if (strpos($url, '://') >= 0 && $this->targetFile->getFileName() == $url_basename) + return $url; + + // Relative path for some reason + if (strpos($url, '://') === false) + { + $uploads = wp_get_upload_dir(); + $url = str_replace($uploads['basedir'], $uploads['baseurl'], $this->targetFile->getFullPath()); + } + // This can happen when WordPress is not taking from attached file, but wrong /old GUID. Try to replace it to the new one. + elseif ($this->targetFile->getFileName() != $url_basename) + { + $url = str_replace($url_basename, $this->targetFile->getFileName(), $url); + } + + return $url; + + } + + protected function getNewTargetLocation() + { + $uploadDir = wp_upload_dir(); + $new_rel_location = $this->new_location; + $newPath = trailingslashit($uploadDir['basedir']) . $new_rel_location; + + $realPath = realpath($newPath); + + // Detect traversal by making sure the canonical path starts with uploads' basedir. + if ( strpos($realPath, $uploadDir['basedir']) !== 0) + { + $this->lastError = self::ERROR_DIRECTORY_SECURITY; + return false; + } + + if (! is_dir($newPath)) + { + $this->lastError = self::ERROR_DIRECTORY_NOTEXIST; + return false; + } + return trailingslashit($newPath); + } + + + private function fs() + { + return emr()->filesystem(); + } +} diff --git a/classes/ViewController.php b/classes/ViewController.php new file mode 100644 index 0000000..0d7a72f --- /dev/null +++ b/classes/ViewController.php @@ -0,0 +1,158 @@ +view = new \stdClass; + } + + protected function loadView($template = null, $unique = true) + { + if (is_null($template) ) + { + return false; + } + elseif (strlen(trim($template)) == 0) + { + return false; + } + + $view = $this->view; + $controller = $this; + $template_path = emr()->plugin_path('views/' . $template . '.php'); + + if (file_exists($template_path) === false) + { + Log::addError("View $template could not be found in " . $template_path, + array('class' => get_class($this))); + } + elseif ($unique === false || ! in_array($template, self::$viewsLoaded)) + { + include($template_path); + self::$viewsLoaded[] = $template; + } + } + + protected function viewError($errorCode) + { + $message = $description = false; + switch($errorCode) + { + case self::ERROR_UPLOAD_PERMISSION: + $message = __('You don\'t have permission to upload images. Please refer to your administrator', 'enable-media-replace'); + break; + case self::ERROR_IMAGE_PERMISSION: + $message = __('You don\'t have permission to edit this image', 'enable-media-replace'); + break; + case self::ERROR_FORM: + $message = __('The form submitted is missing various fields', 'enable-media-replace'); + break; + case self::ERROR_TIME: + $message = __('The custom time format submitted is invalid', 'enable-media-replace'); + break; + case self::ERROR_UPDATE_FAILED: + $message = __('Updating the WordPress attachment failed', 'enable-media-replace'); + break; + case self::ERROR_SECURITY: + $message = __('The file upload has been rejected for security reason. WordPress might not allow uploading this extension or filetype', 'enable-media-replace'); + break; + case self::ERROR_UPLOAD_FAILED: + $message = __('The upload from your browser seem to have failed', 'enable-media-replace'); + break; + case self::ERROR_TARGET_EXISTS: + $message = __('The target file already exists in this directory. Please try another name / directory', 'enable-media-replace'); + break; + case self::ERROR_DESTINATION_FAIL: + $message = __('Something went wrong while writing the file or directory', 'enable-media-replace'); + break; + case self::ERROR_COPY_FAILED: + $message = __('Copying replacement file to destination failed', 'enable-media-replace'); + break; + case self::ERROR_UPDATE_POST: + $message = __('Error updating WordPress post in the database', 'enable-media-replace'); + break; + case self::ERROR_DIRECTORY_SECURITY: + $message = __('Specificed directory is outside the upload directory. This is not allowed for security reasons', 'enable-media-replace'); + break; + case self::ERROR_DIRECTORY_NOTEXIST: + $message = __('Specificed new directory does not exist. Path must be a relative path from the upload directory and exist', 'enable-media-replace'); + break; + + case self::ERROR_NONCE: + $message = __('Fail to validate form nonce. Please try again', 'enable-media-replace'); + $description = __('This can happen when the window is open for a long time and/or there has been a timeout. You can go back to previous screen and try again. If this happens each time when replacing, contact us', 'enable-media-replace'); + break; + + // Remove Background + case self::ERROR_DOWNLOAD_FAILED: + $message = __('Replacement Image could not be downloaded or does not exist', 'enable-media-replace'); + break; + + default: + $message = __('An unknown error has occured', 'enable-media-replace'); + break; + } + + if( false !== $message) + $this->view->errorMessage = $message; + + if (false !== $description) + { + $this->view->errorDescription = $description; + } + + + $this->loadView('error'); + exit(); + } + + + protected function viewSuccess() + { + wp_enqueue_script('emr_success'); + $this->loadView('success'); + exit(); + } + +} diff --git a/classes/ViewController/RemoveBackgroundViewController.php b/classes/ViewController/RemoveBackgroundViewController.php new file mode 100644 index 0000000..1b4b886 --- /dev/null +++ b/classes/ViewController/RemoveBackgroundViewController.php @@ -0,0 +1,155 @@ +viewError(self::ERROR_UPLOAD_PERMISSION); + // wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace')); + } + + $attachment_id = intval($_REQUEST['attachment_id']); + $attachment = get_post($attachment_id); + + $uiHelper = \emr()->uiHelper(); + $uiHelper->setPreviewSizes(); + $uiHelper->setSourceSizes($attachment_id); + + $replacer = new ReplaceController($attachment_id); + $file = $replacer->getSourceFile(true); // for display only + + $defaults = array( + 'bg_type' => 'transparent', + 'bg_color' => '#ffffff', + 'bg_transparency' => 100, + ); + $settings = get_option('enable_media_replace', $defaults); + $settings = array_merge($defaults, $settings); // might miss some + + $this->view->attachment = $attachment; + $this->view->settings = $settings; + $this->view->sourceFile = $file; + + $this->loadView('prepare-remove-background'); + + } + + // When the background has been posted - process. + public function loadPost() + { + if ( ! isset( $_POST['emr_nonce'] ) + || ! wp_verify_nonce( $_POST['emr_nonce'], 'media_remove_background' )) + { + $this->viewError(self::ERROR_NONCE); + } + + $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : null; + if (is_null($key) || strlen($key) == 0) + { + $this->viewError(self::ERROR_KEY); + //wp_die(esc_html__('Error while sending form (no key). Please try again.', 'enable-media-replace')); + } + + $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id. + if (is_null($post_id)) { + $this->viewError(self::ERROR_FORM); +// wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace')); + } + + $this->setView($post_id); + $result = $this->replaceBackground($post_id, $key); + + if (false === $result->success) + { + $this->view->errorMessage = $result->message; + $this->viewError(); + } + elseif (! file_exists($result->image)) + { + $this->viewError(self::ERROR_DOWNLOAD_FAILED); + } + +// $result = $replacer->replaceWith($result->image, $source->getFileName() , true); +//$params = array(); + $replaceController = new ReplaceController($post_id); + $sourceFile = $replaceController->getSourceFile(); + + $datetime = current_time('mysql'); + + $params = array( + 'post_id' => $post_id, + 'replace_type' => ReplaceController::MODE_REPLACE, + 'timestamp_replace' => ReplaceController::TIME_UPDATEMODIFIED, + 'new_date' => $datetime, + 'is_custom_date' => false, + 'remove_background' => true, + 'uploadFile' => $result->image, + 'new_filename' => $sourceFile->getFileName(), + ); + + + $check = $replaceController->setupParams($params); + $this->setView($post_id, $params); + + if (false === $check) + { + $error = $replaceController->returnLastError(); + $this->viewError($error); + } + + $result = $replaceController->run(); + if (true == $result) + { + $this->viewSuccess(); + } + + } + + // Low init might only be w/ post_id ( error handling et al ), most advanced / nicer with params. + protected function setView($post_id, $params = array()) + { + $uiHelper = \emr()->uiHelper(); + $this->view->post_id = $post_id; + $this->view->postUrl = $uiHelper->getSuccesRedirect($post_id); + $this->view->emrUrl = $uiHelper->getFailedRedirect($post_id); + + } + + + protected function replaceBackground($post_id, $key) + { + $api = new Api(); + $result = $api->handleDownload($key); + + return $result; + } + + + +} // class diff --git a/classes/ViewController/ReplaceViewController.php b/classes/ViewController/ReplaceViewController.php new file mode 100644 index 0000000..368b39d --- /dev/null +++ b/classes/ViewController/ReplaceViewController.php @@ -0,0 +1,80 @@ +checkImagePermission($attachment)) + { + $this->viewError(self::ERROR_IMAGE_PERMISSION); + wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') ); + } + + $replacer = new ReplaceController($attachment_id); + + $file = $replacer->getSourceFile(true); + /* $filepath = $file->getFullPath(); + $filename = $file->getFileName(); + $filetype = $file->getExtension(); */ + $source_mime = get_post_mime_type($attachment_id); + + $uiHelper = \emr()->uiHelper(); + $uiHelper->setPreviewSizes(); + $uiHelper->setSourceSizes($attachment_id); + + $defaults = array( + 'replace_type' => 'replace', + 'timestamp_replace' => \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED, + 'custom_date' => date("Y-m-d H:i:s"), + 'new_location' => false, + 'new_location_dir' => false, + ); + $settings = get_option('enable_media_replace', $defaults); + + $this->view->attachment = $attachment; + $this->view->sourceFile = $file; + $this->view->sourceMime = $source_mime; + $this->view->settings = array_merge($defaults, $settings); // might miss some + + // Indicates if file can be moved to other location. Can't be done when offloaded. + $this->view->is_movable = apply_filters('emr/replace/file_is_movable', true, $attachment_id); + + $uploadDir = wp_upload_dir(); + $basedir = trailingslashit($uploadDir['basedir']); + + $this->view->custom_basedir = $basedir; + + + $this->loadView('screen'); + + } + +} diff --git a/classes/ViewController/UploadViewController.php b/classes/ViewController/UploadViewController.php new file mode 100644 index 0000000..c6e71ce --- /dev/null +++ b/classes/ViewController/UploadViewController.php @@ -0,0 +1,221 @@ +setView($post_id); + + if (isset($_GET['emr_success'])) + { + $this->viewSuccess(); + } + + } + + if ( ! isset( $_POST['emr_nonce'] ) + || ! wp_verify_nonce( $_POST['emr_nonce'], 'media_replace_upload' )) + { + $this->viewError(self::ERROR_NONCE); + } + + if (!current_user_can('upload_files')) { + $this->viewError(self::ERROR_UPLOAD_PERMISSION); + // wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace')); + } + + $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id. + if (is_null($post_id)) { + $this->viewError(self::ERROR_FORM); +// wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace')); + } + $attachment = get_post($post_id); + + if (! emr()->checkImagePermission($attachment)) { + $this->viewError(self::ERROR_IMAGE_PERMISSION); +// wp_die(esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace')); + } + + $params = $this->getPost(); + + // UploadController here / replacerController here with save Settings as well? s + $this->updateSettings($params); + $this->setView($post_id, $params); // set variables needed for view. + + $replaceController = new ReplaceController($post_id); + $check = $replaceController->setupParams($params); + + if (false === $check) + { + $error = $replaceController->returnLastError(); + $this->viewError($error); + } + + $result = $replaceController->run(); + + if (true == $result) + { + $this->viewSuccess(); + } + } + + + protected function getPost() + { + $ID = intval($_POST["ID"]); // legacy + $replace_type = isset($_POST["replace_type"]) ? sanitize_text_field($_POST["replace_type"]) : false; + $timestamp_replace = isset($_POST['timestamp_replace']) ? intval($_POST['timestamp_replace']) : Replacer::TIME_UPDATEMODIFIED; + + $remove_background = ( isset( $_POST['remove_after_progress'] ) ) ? true : false; + + $do_new_location = isset($_POST['new_location']) ? true : false; + $do_new_location = apply_filters('emr/replace/file_is_movable', true, $ID); + $new_location_dir = isset($_POST['location_dir']) ? sanitize_text_field($_POST['location_dir']) : null; + + $is_custom_date = false; + + switch ($timestamp_replace) { + case \EnableMediaReplace\Replacer::TIME_UPDATEALL: + case \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED: + $datetime = current_time('mysql'); + break; + case \EnableMediaReplace\Replacer::TIME_CUSTOM: + $custom_date = $_POST['custom_date_formatted']; + $custom_hour = str_pad($_POST['custom_hour'], 2, 0, STR_PAD_LEFT); + $custom_minute = str_pad($_POST['custom_minute'], 2, 0, STR_PAD_LEFT); + + // create a mysql time representation from what we have. + Log::addDebug('Custom Date - ' . $custom_date . ' ' . $custom_hour . ':' . $custom_minute); + $custom_date = \DateTime::createFromFormat('Y-m-d G:i', $custom_date . ' ' . $custom_hour . ':' . $custom_minute); + if ($custom_date === false) { + /*wp_safe_redirect($redirect_error); + $errors = \DateTime::getLastErrors(); + $error = ''; + if (isset($errors['errors'])) { + $error = implode(',', $errors['errors']); + } */ + //Notices::addError(sprintf(__('Invalid Custom Date. Please check the custom date values: (%s)', 'enable-media-replace'), $error)); + $this->viewError(self::ERROR_TIME); + //exit(); + } + $datetime = $custom_date->format("Y-m-d H:i:s"); + $is_custom_date = true; + break; + } + + list($uploadFile, $new_filename) = $this->getUpload(); + + return array( + 'post_id' => $ID, + 'replace_type' => $replace_type, + 'timestamp_replace' => $timestamp_replace, + 'new_date' => $datetime, + 'new_location' => $do_new_location, + 'location_dir' => $new_location_dir, + 'is_custom_date' => $is_custom_date, + 'remove_background' => $remove_background, + 'uploadFile' => $uploadFile, + 'new_filename' => $new_filename, + ); + + } + + // Low init might only be w/ post_id ( error handling et al ), most advanced / nicer with params. + protected function setView($post_id, $params = array()) + { + $uiHelper = \emr()->uiHelper(); + $this->view->post_id = $post_id; + $this->view->postUrl = $uiHelper->getSuccesRedirect($post_id); + $this->view->emrUrl = $uiHelper->getFailedRedirect($post_id); + + if (isset($params['remove_background']) && true === $params['remove_background']) + { + $this->view->postUrl = $uiHelper->getBackgroundRemoveRedirect($post_id); + } + } + + + protected function updateSettings($params) + { + $settings = get_option('enable_media_replace', array()); // save settings and show last loaded. + $settings['replace_type'] = $params['replace_type']; + $settings['timestamp_replace'] = $params['timestamp_replace']; + $settings['new_location'] = $params['new_location']; + $settings['new_location_dir'] = $params['location_dir']; + + if (true === $params['is_custom_date']) + { + $settings['custom_date'] = $params['new_date']; + } + update_option('enable_media_replace', $settings, false); + + } + + protected function getUpload() + { + if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) { + Log::addDebug('Uploaded Files', $_FILES['userfile']); + + // New method for validating that the uploaded file is allowed, using WP:s internal wp_check_filetype_and_ext() function. + $filedata = wp_check_filetype_and_ext($_FILES["userfile"]["tmp_name"], $_FILES["userfile"]["name"]); + + Log::addDebug('Data after check', $filedata); + if (isset($_FILES['userfile']['error']) && $_FILES['userfile']['error'] > 0) { + //$e = new RunTimeException('File Uploaded Failed'); + //Notices::addError($e->getMessage()); + // wp_safe_redirect($redirect_error); + $this->viewError(self::ERROR_UPDATE_FAILED); + // exit(); + } + + if ($filedata["ext"] == false && ! current_user_can('unfiltered_upload')) { +// Log::addWarn('Uploaded File type does not meet security guidelines, aborting'); + $this->viewError(self::ERROR_SECURITY); + } + + // Here we have the uploaded file + $new_filename = $_FILES["userfile"]["name"]; + $new_filetype = $filedata["type"] ? $filedata["type"] : $_FILES['userfile']['type']; + + return array($_FILES["userfile"]["tmp_name"], $new_filename); + // Execute hook actions - thanks rubious for the suggestion! + } + $this->viewError(self::ERROR_UPLOAD_FAILED); + } + + + + +} // class diff --git a/classes/api.php b/classes/api.php index a772eba..2df7418 100644 --- a/classes/api.php +++ b/classes/api.php @@ -56,6 +56,8 @@ public function request( array $posted_data ) { $compression_level = 0; // intval($posted_data['compression_level']); // off for now. $attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : null; + $attachment = get_post($attachment_id); + if (is_null($attachment_id)) { @@ -65,6 +67,13 @@ public function request( array $posted_data ) { return $result; } + if (! emr()->checkImagePermission($attachment)) { + $result = $this->getResponseObject(); + $result->success = false; + $result->message = __('No permission for user', 'enable-media-replace'); + return $result; + } + $replacer = new Replacer($attachment_id); $url = $replacer->getSourceUrl(); diff --git a/classes/emr-plugin.php b/classes/emr-plugin.php index 3459d45..fe62f0d 100644 --- a/classes/emr-plugin.php +++ b/classes/emr-plugin.php @@ -4,6 +4,7 @@ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log; use EnableMediaReplace\Notices\NoticeController as Notices; use EnableMediaReplace\FileSystem\Controller\FileSystemController as FileSystem; +use EnableMediaReplace\Controller\RemoteNoticeController as RemoteNoticeController; use EnableMediaReplace\Ajax; // Does what a plugin does. @@ -46,7 +47,7 @@ public function runtime() return; } - + new Externals(); $this->plugin_actions(); // init } @@ -55,6 +56,11 @@ public function adminInit() { $this->features['replace'] = true; // does nothing just for completeness $this->features['background'] = apply_filters('emr/feature/background', true); + + load_plugin_textdomain('enable-media-replace', false, basename(dirname(EMR_ROOT_FILE)) . '/languages'); + + // Load Submodules + new Ajax(); } public function filesystem() @@ -110,6 +116,7 @@ protected function nopriv_plugin_actions() add_shortcode('file_modified', array($this, 'get_modified_date')); } + public function plugin_actions() { $this->plugin_path = plugin_dir_path(EMR_ROOT_FILE); @@ -121,7 +128,7 @@ public function plugin_actions() // init plugin add_action('admin_menu', array($this,'menu')); add_action('submenu_file', array($this, 'hide_sub_menu')); - add_action('admin_init', array($this,'init')); + add_action( 'current_screen', array($this, 'setScreen') ); // annoying workaround for notices in edit-attachment screen add_action('admin_enqueue_scripts', array($this,'admin_scripts')); @@ -177,20 +184,6 @@ public function hide_sub_menu($submenu_file) return $submenu_file; } - /** - * Initialize this plugin. Called by 'admin_init' hook. - * - */ - public function init() - { - load_plugin_textdomain('enable-media-replace', false, basename(dirname(EMR_ROOT_FILE)) . '/languages'); - - // Load Submodules - - - new Externals(); - new Ajax(); - } public function setScreen() { @@ -200,11 +193,17 @@ public function setScreen() if ( in_array($screen->id, $notice_pages) && true === emr()->useFeature('background')) { + RemoteNoticeController::getInstance(); // check for remote stuff $notices = Notices::getInstance(); - add_action('admin_notices', array($notices, 'admin_notices')); // previous page / init time + $notices->loadIcons(array( + 'normal' => '', + 'success' => '', + 'warning' => '', + 'error' => '', + )); + add_action('admin_notices', array($notices, 'admin_notices')); // previous page / init time } - // var_dump($screen); } /** Load EMR views based on request */ @@ -221,23 +220,24 @@ public function route() $this->uiHelper()->featureNotice(); - if (! check_admin_referer($action, '_wpnonce')) { - die('Invalid Nonce'); - } - - // @todo Later this should be move to it's own controller, and built view from there. if ($action == 'media_replace') { if (array_key_exists("attachment_id", $_GET) && intval($_GET["attachment_id"]) > 0) { wp_enqueue_script('emr_upsell'); - require_once($this->plugin_path . "views/popup.php"); // warning variables like $action be overwritten here. + + $controller = \EnableMediaReplace\ViewController\ReplaceViewController::getInstance(); + $controller->load(); +// require_once($this->plugin_path . "views/popup.php"); // warning variables like $action be overwritten here. } } elseif ($action == 'media_replace_upload') { - require_once($this->plugin_path . 'views/upload.php'); + + $controller = \EnableMediaReplace\ViewController\UploadViewController::getInstance(); + $controller->load(); + // require_once($this->plugin_path . 'views/upload.php'); } elseif ('emr_prepare_remove' === $action && $this->useFeature('background')) { - $attachment_id = intval($_GET['attachment_id']); - $attachment = get_post($attachment_id); +// $attachment_id = intval($_GET['attachment_id']); +// $attachment = get_post($attachment_id); //We're adding a timestamp to the image URL for cache busting wp_enqueue_script('emr_remove_bg'); @@ -245,19 +245,26 @@ public function route() wp_enqueue_style('emr_style'); wp_enqueue_style('emr-remove-background'); wp_enqueue_script('emr_upsell'); - require_once($this->plugin_path . "views/prepare-remove-background.php"); + + $controller = \EnableMediaReplace\ViewController\RemoveBackgroundViewController::getInstance(); + $controller->load(); + + // require_once($this->plugin_path . "views/prepare-remove-background.php"); } elseif ('do_background_replace' === $action && - check_admin_referer($action, '_wpnonce') && $this->useFeature('background') ) { - require_once($this->plugin_path . 'views/do-replace-background.php'); + $controller = \EnableMediaReplace\ViewController\RemoveBackgroundViewController::getInstance(); + $controller->loadPost(); + + // require_once($this->plugin_path . 'views/do-replace-background.php'); } else { + exit('Something went wrong loading page, please try again'); } break; - } + } // Route } public function getPluginURL($path = '') @@ -265,6 +272,15 @@ public function getPluginURL($path = '') return plugins_url($path, EMR_ROOT_FILE); } + public function plugin_path($path = '') + { + $plugin_path = trailingslashit(plugin_dir_path(EMR_ROOT_FILE)); + if ( strlen( $path ) > 0 ) { + $plugin_path .= $path; + } + return $plugin_path; + } + /** register styles and scripts * * Nothing should ever by -enqueued- here, just registered. @@ -314,6 +330,8 @@ public function admin_scripts() } wp_localize_script('emr_admin', 'emr_options', $emr_options); + + wp_register_script('emr_success', plugins_url('js/emr_success.js', EMR_ROOT_FILE), array(), EMR_VERSION, true); } /** Utility function for the Jquery UI Datepicker */ @@ -483,7 +501,6 @@ public function attachment_editor($form_fields, $post) if (function_exists('get_current_screen')) { $screen = get_current_screen(); - if (! is_null($screen) && $screen->id == 'attachment') { // hide on edit attachment screen. return $form_fields; } @@ -535,9 +552,9 @@ public function add_media_action($actions, $post) return $actions; } - $url = $this->getMediaReplaceURL($post->ID); + $media_replace_editurl = $this->getMediaReplaceURL($post->ID); $media_replace_action = "media_replace"; - $media_replace_editurl = wp_nonce_url($url, $media_replace_action); + // $media_replace_editurl = wp_nonce_url($url, $media_replace_action); $url = $this->getRemoveBgURL($post->ID); $background_remove_action = "emr_prepare_remove"; $background_remove_editurl = wp_nonce_url($url, $background_remove_action); diff --git a/classes/external/upgrader_skin.php b/classes/external/upgrader_skin.php index 6e92cce..579c9d5 100644 --- a/classes/external/upgrader_skin.php +++ b/classes/external/upgrader_skin.php @@ -81,7 +81,6 @@ public function error( $errors ) { echo wp_json_encode( array( 'error' => __( 'There was an error installing the addon. Please try again.', 'envira-gallery' ) ) ); /* log this for API issues */ - error_log( 'Envira: There was an error installing the addon' ); error_log( print_r( $errors, true ) ); die; diff --git a/classes/external/upsell_installer.php b/classes/external/upsell_installer.php index 97d9e38..3f23ca9 100644 --- a/classes/external/upsell_installer.php +++ b/classes/external/upsell_installer.php @@ -9,6 +9,13 @@ function emr_plugin_install() { $plugin = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : null; + if ( ! current_user_can('install_plugins')) + { + // Send back a response. + wp_send_json(array('result'=> false)); + die; + } + switch($plugin) { case "envira": @@ -103,6 +110,14 @@ function emr_activate() { $plugin = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : null; +if ( ! current_user_can('activate_plugins')) +{ + // Send back a response. + wp_send_json(array('result'=> false)); + die; +} + + switch($plugin) { case "envira": diff --git a/classes/external/wp-offload.php b/classes/external/wp-offload.php new file mode 100644 index 0000000..aad93e2 --- /dev/null +++ b/classes/external/wp-offload.php @@ -0,0 +1,331 @@ + source_id lookup, to prevent duplicate queries. + + private static $offloadPrevented = array(); + + + public function __construct() + { + add_action('as3cf_init', array($this, 'init')); + add_filter('shortpixel/image/urltopath', array($this, 'checkIfOffloaded'), 10,2); + + + add_action('emr/converter/prevent-offload', array($this, 'preventOffload'), 10); + add_action('emr/converter/prevent-offload-off', array($this, 'preventOffloadOff'), 10); + add_filter('as3cf_pre_update_attachment_metadata', array($this, 'preventUpdateMetaData'), 10,4); + + + } + + public static function getInstance() + { + if (is_null(self::$instance)) + { + self::$instance = new WPOffload(); + } + + return self::$instance; + } + + public function init($as3cf) + { + if (! class_exists('\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item')) + { + Notices::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'enable-media-replace'), true); + return false; + } + + $this->as3cf = $as3cf; + + if (method_exists($as3cf, 'get_item_handler')) + { + } + else { + Notices::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'enable-media-replace'), true); + return false; + } + + add_action('emr_after_remove_current', array($this, 'removeRemote'), 10, 5); + add_filter('emr/file/virtual/translate', array($this, 'getLocalPathByURL')); + add_filter('emr/replace/file_is_movable', array($this, 'isFileMovable'), 10, 2); + + add_filter('emr/replace/original_image_path', array($this, 'checkScaledUrl'), 10,2); + } + + /* + * @param $post_id int The post ID + * @param $meta array Old Metadata before remove + * @param $backup_sizes array WP Backup sizes + * @param $sourceFile Object Source File + * @param $targetFile Object Target File + */ + public function removeRemote($post_id, $meta, $backup_sizes, $sourceFile, $targetFile ) + { +// Always remove because also thumbnails can be different. + $a3cfItem = $this->getItemById($post_id); // MediaItem is AS3CF Object + if ($a3cfItem === false) + { + Log::addDebug('S3-Offload MediaItem not remote - ' . $post_id); + return false; + } + + $remove = \DeliciousBrains\WP_Offload_Media\Items\Remove_Provider_Handler::get_item_handler_key_name(); + $itemHandler = $this->as3cf->get_item_handler($remove); + + $result = $itemHandler->handle($a3cfItem, array( 'verify_exists_on_local' => false)); //handle it then. + + } + + // @param s3 based URL that which is needed for finding local path + // @return String Filepath. Translated file path + public function getLocalPathByURL($url) + { + $source_id = $this->getSourceIDByURL($url); + + if ($source_id == false) + { + return false; + } + $item = $this->getItemById($source_id); + + $original_path = $item->original_source_path(); // $values['original_source_path']; + + if (wp_basename($url) !== wp_basename($original_path)) // thumbnails translate to main file. + { + $original_path = str_replace(wp_basename($original_path), wp_basename($url), $original_path); + } + + $fs = emr()->filesystem(); + $base = $fs->getWPUploadBase(); + + $file = $base . $original_path; + return $file; + } + + public function isFileMovable($bool, $attach_id) + { + $item = $this->getItemById($attach_id); + if ($item === false) + { + return $bool; + } + + // Can't move offloaded items. + if (is_object($item)) + { + return false; + } + + } + + public function checkIfOffloaded($bool, $url) + { + + $source_id = $this->sourceCache($url); + + + if (false === $source_id) + { + $extension = substr($url, strrpos($url, '.') + 1); + // If these filetypes are not in the cache, they cannot be found via geSourceyIDByUrl method ( not in path DB ), so it's pointless to try. If they are offloaded, at some point the extra-info might load. + if ($extension == 'webp' || $extension == 'avif') + { + return false; + } + + $source_id = $this->getSourceIDByURL($url); + } + + if ($source_id !== false) + { + return true; + } + else + { + return false; + } + } + + // This is used in the converted. Might be deployed elsewhere for better control. + public function preventOffload($attach_id) + { + self::$offloadPrevented[$attach_id] = true; + } + + public function preventOffloadOff($attach_id) + { + unset(self::$offloadPrevented[$attach_id]); + } + + // When Offload is not offloaded but is created during the process of generate metadata in WP, wp_create_image_subsizes fires an update metadata after just moving the upload, before making any thumbnails. If this is the case and the file has an -scaled / original image setup, the original_source_path becomes the same as the source_path which creates issue later on when dealing with optimizing it, if the file is deleted on local server. Prevent this, and lean on later update metadata. + public function preventUpdateMetaData($bool, $data, $post_id, $old_provider_object) + { + if (isset(self::$offloadPrevented[$post_id])) + { + return true ; // return true to cancel. + } + + return $bool; + + } + + // WP Offload -for some reason - returns the same result of get_attached_file and wp_get_original_image_path , which are different files (one scaled) which then causes a wrong copy action after optimizing the image ( wrong destination download of the remote file ). This happens if offload with delete is on. Attempt to fix the URL to reflect the differences between -scaled and not. + public function checkScaledUrl($filepath, $id) + { + // Original filepath can never have a scaled in there. + // @todo This should probably check -scaled. as string end preventing issues. + if (strpos($filepath, '-scaled') !== false) + { + $filepath = str_replace('-scaled', '', $filepath); + } + return $filepath; + } + + /** @return Returns S3Ofload MediaItem, or false when this does not exist */ + protected function getItemById($id, $create = false) + { + $class = $this->getMediaClass(); + $mediaItem = $class::get_by_source_id($id); + + if (true === $create && $mediaItem === false) + { + $mediaItem = $class::create_from_source_id($id); + } + + return $mediaItem; + } + + protected function getSourceIDByURL($url) + { + $source_id = $this->sourceCache($url); // check cache first. + + if (false === $source_id) // check on the raw url. + { + $class = $this->getMediaClass(); + + $parsedUrl = parse_url($url); + + if (! isset($parsedUrl['scheme']) || ! in_array($parsedUrl['scheme'], array('http','https'))) + { + $url = 'http://' . $url; //str_replace($parsedUrl['scheme'], 'https', $url); + } + + $source = $class::get_item_source_by_remote_url($url); + $source_id = isset($source['id']) ? intval($source['id']) : false; + } + + if (false === $source_id) // check now via the thumbnail hocus. + { + $pattern = '/(.*)-\d+[xX]\d+(\.\w+)/m'; + $url = preg_replace($pattern, '$1$2', $url); + + $source_id = $this->sourceCache($url); // check cache first. + + if (false === $source_id) + { + $source = $class::get_item_source_by_remote_url($url); + $source_id = isset($source['id']) ? intval($source['id']) : false; + } + + } + + // Check issue with double extensions. If say double webp/avif is on, the double extension causes the URL not to be found (ie .jpg) + if (false === $source_id) + { + if (substr_count($parsedUrl['path'], '.') > 1) + { + // Get extension + $ext = substr(strrchr($url, '.'), 1); + + // Remove all extensions from the URL + $checkurl = substr($url, 0, strpos($url,'.')) ; + + // Add back the last one. + $checkurl .= '.' . $ext; + + // Retry + $source_id = $this->sourceCache($checkurl); // check cache first. + + if (false === $source_id) + { + $source = $class::get_item_source_by_remote_url($url); + $source_id = isset($source['id']) ? intval($source['id']) : false; + } + + + } + } + + if ($source_id !== false) + { + + $this->sourceCache($url, $source_id); // cache it. + + // get item + $item = $this->getItemById($source_id); + if (is_object($item) && method_exists($item, 'extra_info')) + { + $baseUrl = str_replace(basename($url),'', $url); + $extra_info = $item->extra_info(); + + if (isset($extra_info['objects'])) + { + foreach($extra_info['objects'] as $extraItem) + { + if (is_array($extraItem) && isset($extraItem['source_file'])) + { + // Add source stuff into cache. + $this->sourceCache($baseUrl . $extraItem['source_file'], $source_id); + } + } + } + } + + return $source_id; + } + + return false; + } + + private function sourceCache($url, $source_id = null) + { + if ($source_id === null && isset($this->sources[$url])) + { + $source_id = $this->sources[$url]; + return $source_id; + } + elseif ($source_id !== null) + { + if (! isset($this->sources[$url])) + { + $this->sources[$url] = $source_id; + } + return $source_id; + } + + return false; + } + + + private function getMediaClass() + { + $class = $this->as3cf->get_source_type_class('media-library'); + return $class; + } + +} diff --git a/classes/externals.php b/classes/externals.php index 6d71d0c..fcc10cd 100644 --- a/classes/externals.php +++ b/classes/externals.php @@ -6,6 +6,7 @@ use EnableMediaReplace\Externals\Elementor as Elementor; use EnableMediaReplace\Externals\WpBakery as WpBakery; use EnableMediaReplace\Externals\SiteOrigin as SiteOrigin; +use EnableMediaReplace\Externals\WPOffload as WPOffload; class Externals @@ -28,6 +29,7 @@ public function __construct() $this->loadElementor(); $this->loadBakery(); // in case of urlencoded issues, this class should be used probably. $this->loadSiteOrigins(); + $this->loadWpOffload(); } protected function check() // check if any of the options should be disabled due to conflicts @@ -78,4 +80,9 @@ public function loadSiteOrigins() SiteOrigin::getInstance(); } + public function loadWPOffload() + { + WPOffload::getInstance(); + } + } // class diff --git a/classes/replacer.php b/classes/replacer.php index 5302e72..cc3fd4b 100644 --- a/classes/replacer.php +++ b/classes/replacer.php @@ -50,7 +50,7 @@ public function __construct($post_id) if (false === $source_file) { - $source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ))); + $source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ))); } /* It happens that the SourceFile returns relative / incomplete when something messes up get_upload_dir with an error something. @@ -211,6 +211,7 @@ public function replaceWith($file, $fileName) delete_post_meta($this->post_id, '_emr_replace_author'); } + if ($this->replaceMode == self::MODE_SEARCHREPLACE) { // Write new image title. @@ -243,6 +244,8 @@ public function replaceWith($file, $fileName) } // SEARCH REPLACE MODE + +// FROM HERE STATS THE MODULE $args = array( 'thumbnails_only' => ($this->replaceMode == self::MODE_SEARCHREPLACE) ? false : true, ); @@ -433,7 +436,6 @@ protected function getTargetURL() } return $url; - //$this->targetFile } diff --git a/classes/uihelper.php b/classes/uihelper.php index c609189..a5e50b2 100644 --- a/classes/uihelper.php +++ b/classes/uihelper.php @@ -43,7 +43,7 @@ public function getFormUrl($attach_id, $action = null) $url = admin_url('upload.php'); $url = add_query_arg(array( 'page' => 'enable-media-replace/enable-media-replace.php', - 'noheader' => true, + // 'noheader' => true, 'action' => $action, 'attachment_id' => $attach_id, )); @@ -64,10 +64,10 @@ public function getFormUrl($attach_id, $action = null) } - public function getSuccesRedirect($post_id) + public function getSuccesRedirect($attach_id) { $url = admin_url('post.php'); - $url = add_query_arg(array('action' => 'edit', 'post' => $post_id, 'emr_replaced' => '1'), $url); + $url = add_query_arg(array('action' => 'edit', 'post' => $attach_id, 'emr_replaced' => '1'), $url); if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) { @@ -87,6 +87,21 @@ public function getSuccesRedirect($post_id) } + public function getBackgroundRemoveRedirect($attach_id) + { + //if ( isset( $_POST['remove_after_progress'] ) ) { + $url = admin_url("upload.php"); + $url = add_query_arg(array( + 'page' => 'enable-media-replace/enable-media-replace.php', + 'action' => 'emr_prepare_remove', + 'attachment_id' => $attach_id, + ), $url); + +// $redirect_success = $url; + return $url; + // } + } + public function getFailedRedirect($attach_id) { $url = admin_url('upload.php'); @@ -124,102 +139,6 @@ public function setSourceSizes($attach_id) } - // Returns Preview Image HTML Output. - public function getPreviewImage($attach_id,$file, $args = array()) - { - $data = false; - - if ($attach_id > 0) - { - $data = $this->getImageSizes($attach_id, $this->preview_size); //wp_get_attachment_image_src($attach_id, $this->preview_size); - /*$file = get_attached_file($attach_id); - - - // If the file is relative, prepend upload dir. - if (! file_exists($file) && $file && 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ) - { - $file = get_post_meta( $attach_id, '_wp_attached_file', true ); - $uploads = wp_get_upload_dir(); - $file = $uploads['basedir'] . "/$file"; - } - */ - } - - $mime_type = get_post_mime_type($attach_id); - - if (! is_array($data) || ! $file->exists() ) - { - // if attachid higher than zero ( exists ) but not the image, fail, that's an error state. - $icon = ($attach_id < 0) ? '' : 'dashicons-no'; - $is_document = false; - - $defaults = array( - 'width' => $this->preview_width, - 'height' => $this->preview_height, - 'is_image' => false, - 'is_document' => $is_document, - 'icon' => $icon, - 'mime_type' => null, - ); - - $args = wp_parse_args($args, $defaults); - - // failed, it might be this server doens't support PDF thumbnails. Fallback to File preview. - if ($mime_type == 'application/pdf') - { - return $this->getPreviewFile($attach_id, $file); - } - - return $this->getPlaceHolder($args); - } - - $url = $data[0]; - $width = $data[1]; - $height = $data[2]; - - // width - $width_ratio = $height_ratio = 0; - if ($width > $this->preview_max_width) - { - $width_ratio = $width / $this->preview_max_width; - } - if ($height > $this->preview_max_height) // height - { - $height_ratio = $height / $this->preview_max_height; - } - - $ratio = ($width_ratio > $height_ratio) ? $width_ratio : $height_ratio; - - if ($ratio > 0) - { - - $width = floor($width / $ratio); - $height = floor($height / $ratio); - } - - // SVG's without any helpers return around 0 for width / height. Fix preview. - - - // preview width, if source if found, should be set to source. - $this->preview_width = $width; - $this->preview_height = $height; - - $image = ""; - - $defaults = array( - 'width' => $width, - 'height' => $height, - 'image' => $image, - 'mime_type' => $mime_type, - 'file_size' => $file->getFileSize(), - ); - - $args = wp_parse_args($args, $defaults); - - $output = $this->getPlaceHolder($args); - return $output; - } - protected function getImageSizes($attach_id, $size = 'thumbnail') { // We are not using this function, because depending on the theme, it can mess with the dimensions - https://wordpress.stackexchange.com/questions/167525/why-is-wp-get-attachment-image-src-returning-wrong-dimensions @@ -282,6 +201,93 @@ protected function fixSVGSize($data, $file) return $data; } + // Returns Preview Image HTML Output. + public function getPreviewImage($attach_id,$file, $args = array()) + { + $data = false; + + if ($attach_id > 0) + { + $data = $this->getImageSizes($attach_id, $this->preview_size); //wp_get_attachment_image_src($attach_id, $this->preview_size); + } + + $mime_type = get_post_mime_type($attach_id); + + if (! is_array($data) || (! $file->exists() && ! $file->is_virtual()) ) + { + // if attachid higher than zero ( exists ) but not the image, fail, that's an error state. + $icon = ($attach_id < 0) ? '' : 'dashicons-no'; + $is_document = false; + + $defaults = array( + 'width' => $this->preview_width, + 'height' => $this->preview_height, + 'is_image' => false, + 'is_document' => $is_document, + 'is_upload' => false, + 'icon' => $icon, + 'mime_type' => null, + ); + $args = wp_parse_args($args, $defaults); + + // failed, it might be this server doens't support PDF thumbnails. Fallback to File preview. + if ($mime_type == 'application/pdf') + { + return $this->getPreviewFile($attach_id, $file); + } + + return $this->getPlaceHolder($args); + } + + $url = $data[0]; + $width = $data[1]; + $height = $data[2]; + + // width + $width_ratio = $height_ratio = 0; + if ($width > $this->preview_max_width) + { + $width_ratio = $width / $this->preview_max_width; + } + if ($height > $this->preview_max_height) // height + { + $height_ratio = $height / $this->preview_max_height; + } + + $ratio = ($width_ratio > $height_ratio) ? $width_ratio : $height_ratio; + + if ($ratio > 0) + { + + $width = floor($width / $ratio); + $height = floor($height / $ratio); + } + + // SVG's without any helpers return around 0 for width / height. Fix preview. + + + // preview width, if source if found, should be set to source. + $this->preview_width = $width; + $this->preview_height = $height; + + $image = ""; + // $image = " "; + + $defaults = array( + 'width' => $width, + 'height' => $height, + 'image' => $image, + 'mime_type' => $mime_type, + 'is_upload' => false, + 'file_size' => $file->getFileSize(), + ); + + $args = wp_parse_args($args, $defaults); + + $output = $this->getPlaceHolder($args); + return $output; + } + public function getPreviewError($attach_id) { $args = array( @@ -294,7 +300,7 @@ public function getPreviewError($attach_id) return $output; } - public function getPreviewFile($attach_id, $file) + public function getPreviewFile($attach_id, $file, $args = array()) { if ($attach_id > 0) { @@ -311,15 +317,19 @@ public function getPreviewFile($attach_id, $file) $mime_type = get_post_mime_type($attach_id); } - $args = array( + $defaults = array( 'width' => 300, 'height' => 300, 'is_image' => false, 'is_document' => true, + 'is_upload' => false, 'layer' => $filename, 'mime_type' => $mime_type, 'file_size' => $file->getFileSize(), ); + $args = wp_parse_args($args, $defaults); + + $output = $this->getPlaceHolder($args); return $output; } @@ -356,6 +366,7 @@ public function getPlaceHolder($args) 'layer' => $this->full_width . ' x ' . $this->full_height, 'is_image' => true, 'is_document' => false, + 'is_upload' => false, // indicating right-side upload interface for image. 'mime_type' => false, 'file_size' => false, 'remove_bg_ui' => false, // In process icons et al when removing background, for preview pane. @@ -387,6 +398,11 @@ public function getPlaceHolder($args) $placeholder_class .= ' is_document'; } + if (true == $args['is_upload']) + { + $placeholder_class .= ' upload-file-action'; + } + $filetype = ''; if ($args['mime_type']) { @@ -395,18 +411,30 @@ public function getPlaceHolder($args) $filesize = ($args['file_size']) ? $args['file_size'] : ''; - $background_remove_ui = (isset($args['remove_bg_ui']) && $args['remove_bg_ui'] == true) ? $this->getBgremoveUI() : ''; + $is_backgroundremove_ui = (isset($args['remove_bg_ui']) && $args['remove_bg_ui'] == true); + $background_remove_ui = ($is_backgroundremove_ui) ? $this->getBgremoveUI() : ''; $output = "
"; - $output .= $args['image']; + if (true === $args['is_upload']) + { + $output .= "" . __('New', 'enable-media-replacer') . ""; + $output .= ''; + $output .= '
+ ' . __('Click here to upload or drop file in area', 'enable-media-replace') . ' +
'; + } + else + { + $title = (true === $is_backgroundremove_ui ) ? __('Preview', 'enable-media-replace') : __('Current', 'enable-media-replace'); + $output .= "" . $title . ""; + $output .= $args['image']; + } $output .= "
 
"; $output .= "" . $args['layer'] . ""; $output .= $background_remove_ui; $output .= "
" . $this->convertFileSize($filesize). "
"; $output .= "
"; - - return $output; } diff --git a/composer.json b/composer.json index 43fd0b9..8623d06 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "require": { "shortpixel/notices":"@dev", "shortpixel/build" : "@dev", - "shortpixel/filesystem" : "@dev" + "shortpixel/filesystem" : "@dev", + "shortpixel/replacer" : ">=1.0" }, "require-dev": { "phpunit/phpunit": "^9", diff --git a/css/admin.css b/css/admin.css index 6566f22..ac3c281 100644 --- a/css/admin.css +++ b/css/admin.css @@ -984,6 +984,62 @@ border-bottom-right-radius: 3px; } +.enable-media-replace.emr-screen { + width: 90%; + background: #fff; + border: 1px solid #fff; + position: relative; + margin: 30px 0; +} +.enable-media-replace.emr-screen h3 { + font-size: 24px; + position: absolute; + top: -15px; + left: 30px; + padding: 16px 8px; + border: 1px solid #000; + background: #fff; + margin: 0; +} +.enable-media-replace.emr-screen .content { + padding: 32px; +} +.enable-media-replace.emr-screen h1 { + font-weight: 700; +} +.enable-media-replace.emr-screen p { + font-size: 14px; +} +.enable-media-replace.emr-screen .hide { + display: none; +} +.enable-media-replace.emr-screen.error-screen h1 { + color: #ff0000; +} + +#emr-drop-area-active { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 9999; + display: block; + background: rgba(0, 0, 0, 0.5); + border: 6px dashed #AAA; +} +#emr-drop-area-active h3 { + font-size: 36px; + color: #ddd; + width: 200px; + left: 50%; + top: 50%; + position: absolute; +} +#emr-drop-area-active .drop-wrapper { + margin: 0 auto; +} + .emr_upload_form form { display: flex; } @@ -993,6 +1049,9 @@ .emr_upload_form .editor-wrapper { width: 100%; } +.emr_upload_form .emr_drop_area { + display: none; +} .emr_upload_form .wrapper { padding: 18px; border: 1px solid #ccc; @@ -1003,50 +1062,22 @@ padding: 6px 0; margin: 0 0 15px 0; } -.emr_upload_form .image_chooser.wrapper { - min-height: 350px; -} -.emr_upload_form .image_chooser.wrapper .emr_drop_area { - border: 4px dashed #b4b9be; - max-width: 600px; - padding: 28px 14px; - text-align: center; - position: relative; +.emr_upload_form .explainer { + font-size: 14px; + color: #555; + max-width: 800px; + margin: 20px 0; } -.emr_upload_form .image_chooser.wrapper .emr_drop_area h1 { - display: none; -} -.emr_upload_form .image_chooser.wrapper .emr_drop_area .drop-wrapper { - margin: 0 auto; +.emr_upload_form .explainer .underline { + text-decoration-style: dotted; + text-decoration-line: underline; } -.emr_upload_form .image_chooser.wrapper .emr_drop_area.drop_breakout { - position: fixed; - left: 0; - right: 0; - bottom: 0; - top: 0; - max-width: none; - border-color: #83b4d8; - border-width: 10px; - z-index: 999999; - background-color: rgba(68, 68, 68, 0.7); -} -.emr_upload_form .image_chooser.wrapper .emr_drop_area.drop_breakout h1 { - color: #fff; - position: absolute; - font-size: 50px; - line-height: 50px; - margin-top: -25px; - top: 50%; - width: 100%; - text-align: center; - display: block; -} -.emr_upload_form .image_chooser.wrapper .emr_drop_area.drop_breakout .drop-wrapper { - display: none; +.emr_upload_form .image_chooser.wrapper { + min-height: 350px; } .emr_upload_form .image_chooser.wrapper .image_previews { margin: 15px 0; + position: relative; } .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder { position: relative; @@ -1055,6 +1086,25 @@ margin-bottom: 10px; border: 1px solid #ddd; vertical-align: top; + box-sizing: border-box; + background: #fff; + max-width: calc(50% - 25px); +} +.emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder img { + object-fit: contain; +} +.emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .upload-title { + text-align: center; + position: absolute; + padding-top: 8px; + line-height: 20px; + font-weight: 700; + width: 100%; + left: 0; + top: 0; +} +.emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder #upload-file { + display: none; } .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .textlayer { font-size: 25px; @@ -1068,6 +1118,7 @@ border: 1px dashed #eee; background-color: #333; padding: 8px; + z-index: 5; } .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .dashicons { font-size: 60px; @@ -1084,6 +1135,25 @@ bottom: -25px; width: 100%; } +.emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .drag-and-drop-title { + text-align: center; + font-weight: 400; + font-size: 14px; + width: 100%; + height: 100%; + position: absolute; + cursor: pointer; + top: 0; + left: 0; + z-index: 1; +} +.emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .drag-and-drop-title span { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + padding: 5px 10px; + width: 100%; +} .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.is_image .dashicons::before, .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.is_image .dashicons { display: none; } diff --git a/css/admin.css.map b/css/admin.css.map index 26b57c0..d5d06e4 100644 --- a/css/admin.css.map +++ b/css/admin.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../scss/_datepicker.scss","../scss/admin.scss"],"names":[],"mappings":"AAEA;EACC;EACA;EACA;;;AAID;EACC;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;EACA;EACA;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAGD;AACA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAGD;AACA;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;;;AAED;AAAA;EAEC;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;EAEC;;;AAED;AAAA;EAEC;;;AAGD;AACA;EAAiB;;;AACjB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA4B;;;AAC5B;EAA8B;;;AAC9B;EAA4B;;;AAC5B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAAmB;;;AACnB;EAAwB;;;AACxB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAwB;;;AACxB;EAA6B;;;AAC7B;EAA4B;;;AAC5B;EAAuB;;;AACvB;EAAoB;;;AACpB;EAAsB;;;AACtB;EAAgB;;;AAChB;EAAuB;;;AACvB;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAoB;;;AACpB;EAAoB;;;AACpB;EAAe;;;AACf;EAAgB;;;AAChB;EAAgB;;;AAChB;EAAoB;;;AACpB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAsB;;;AACtB;EAAkB;;;AAClB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAe;;;AACf;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAqB;;;AACrB;EAAgB;;;AAChB;EAAmB;;;AACnB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAoB;;;AACpB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAsB;;;AACtB;AACA;EAAsB;;;AACtB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAuB;;;AACvB;EAAkB;;;AAClB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAuB;;;AACvB;EAAwB;;;AACxB;EAAwB;;;AACxB;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAAwB;;;AACxB;EAA4B;;;AAC5B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA4B;;;AAC5B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAAgC;;;AAChC;EAAkC;;;AAClC;EAA+B;;;AAC/B;EAAiC;;;AACjC;EAAiC;;;AACjC;EAA4B;;;AAE5B;AACA;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AC/ZC;EAEE;;AACA;EAEE;;AAIL;EAEC;;AAGD;EAGG;EACA;;AAEA;EAEE;EAEA;EACA;EACA;;AAIJ;EAEE;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AACA;EAAK;;AACL;EAEE;;AAGF;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF;EAAgB;;AAGtB;EAEE;;AACA;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEG;EACA;EACA;EACA;;AAID;EAAiC;;AAMjC;EAAM;;AACN;EAAa;;AAEX;EACE;EACA;EACA;;AASZ;EAEE;EACA;EACA;EAEA;EACA;;AACA;EACI;EACA;EACA;;AAIN;EAEE;;AAGF;EAEE;;AAGF;EAEE;EACA;EACA;;AACA;EAEE;EACA;;AAKA;EAEE;;AAEF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAOR;EAEE;EACA;EACA;EACA;;AAGE;EACE;;AAEF;EAEE;;AAGN;EAEE;;AACA;EACC;EACA;;AAOG;EAEE;;AAMJ;EAAQ;;AAEV;EAEE;EACA;EACA;;AAEF;EAEE;EACA;EACA;;AACA;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;;AAIN;EAEE;EACA;;AACA;EAEE;EACA;;AAKN;EAEE;EACA;EACA;EACA;;AACA;EAEE;EACA;;AAIJ;EAEG;;AAIH;EAEE;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACF;;AACE;EACE;EACA;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;AAEF;EAAO;;AACP;EAAQ;;AACR;EAAQ;;AACR;EAAS;;AACR;EACE;;AAED;EAEE;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACJ;;AAGF;EAAU;;AAER;EAEE;EACA;;AACA;EAAM;EAAkB;EAAmB;;AAU/C;EAEE;;AAEF;EAEE;;AACA;EACC;;AAKH;EAEI;IAEG;;EAEH;IACE;;EACA;IACE;;;AAGR;EAEE;IAAkB;;;AAErB;EAEE;IACE;IACA;IACA;;EAIA;IACE;IACD","file":"admin.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../scss/_datepicker.scss","../scss/_screens.scss","../scss/admin.scss"],"names":[],"mappings":"AAEA;EACC;EACA;EACA;;;AAID;EACC;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;EACA;EACA;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAGD;AACA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAGD;AACA;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;;;AAED;AAAA;EAEC;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;EAEC;;;AAED;AAAA;EAEC;;;AAGD;AACA;EAAiB;;;AACjB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA4B;;;AAC5B;EAA8B;;;AAC9B;EAA4B;;;AAC5B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAAmB;;;AACnB;EAAwB;;;AACxB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAwB;;;AACxB;EAA6B;;;AAC7B;EAA4B;;;AAC5B;EAAuB;;;AACvB;EAAoB;;;AACpB;EAAsB;;;AACtB;EAAgB;;;AAChB;EAAuB;;;AACvB;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAoB;;;AACpB;EAAoB;;;AACpB;EAAe;;;AACf;EAAgB;;;AAChB;EAAgB;;;AAChB;EAAoB;;;AACpB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAsB;;;AACtB;EAAkB;;;AAClB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAe;;;AACf;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAqB;;;AACrB;EAAgB;;;AAChB;EAAmB;;;AACnB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAoB;;;AACpB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAsB;;;AACtB;AACA;EAAsB;;;AACtB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAuB;;;AACvB;EAAkB;;;AAClB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAuB;;;AACvB;EAAwB;;;AACxB;EAAwB;;;AACxB;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAAwB;;;AACxB;EAA4B;;;AAC5B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA4B;;;AAC5B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAAgC;;;AAChC;EAAkC;;;AAClC;EAA+B;;;AAC/B;EAAiC;;;AACjC;EAAiC;;;AACjC;EAA4B;;;AAE5B;AACA;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AClaD;EAGC;EACA;EACA;EACA;EACA;;AAEA;EACE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;;AAGD;EACE;;AAGF;EAAI;;AAGJ;EAAQ;;AAGN;EAAK;;;AChCR;EAIC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;;AAGD;EAEC;;;AAOA;EAEE;;AACA;EAEE;;AAIL;EAEC;;AAED;EACC;;AAGD;EAGG;EACA;;AAEA;EAEE;EAEA;EACA;EACA;;AAIL;EACC;EACA;EACA;EACA;;AACA;EACC;EACA;;AAMD;EAEE;;AAGA;EAEE;EACH;;AAEG;EAEE;EACA;EACA;EACA;EACA;EACA;EACJ;EACA;EACA;;AAEA;EAAM;;AAEN;EAEC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EACC;;AAEG;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACL;;AAGG;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEG;EACA;EACA;EACA;;AAEP;EAEC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EAEA;EACA;EACA;EACA;;AAMG;EAAiC;;AAMjC;EAAM;;AACN;EAAa;;AAEX;EACE;EACA;EACA;;AASZ;EAEE;EACA;EACA;EAEA;EACA;;AACA;EACI;EACA;EACA;;AAIN;EAEE;;AAGF;EAEE;;AAGF;EAEE;EACA;EACA;;AACA;EAEE;EACA;;AAKA;EAEE;;AAEF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAOR;EAEE;EACA;EACA;EACA;;AAGE;EACE;;AAEF;EAEE;;AAGN;EAEE;;AACA;EACC;EACA;;AAOG;EAEE;;AAMJ;EAAQ;;AAEV;EAEE;EACA;EACA;;AAEF;EAEE;EACA;EACA;;AACA;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;;AAIN;EAEE;EACA;;AACA;EAEE;EACA;;AAKN;EAEE;EACA;EACA;EACA;;AACA;EAEE;EACA;;AAIJ;EAEG;;AAIH;EAEE;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACF;;AACE;EACE;EACA;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;AAEF;EAAO;;AACP;EAAQ;;AACR;EAAQ;;AACR;EAAS;;AACR;EACE;;AAED;EAEE;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACJ;;AAGF;EAAU;;AAER;EAEE;EACA;;AACA;EAAM;EAAkB;EAAmB;;AAU/C;EAEE;;AAEF;EAEE;;AACA;EACC;;AAKH;EAEI;IAEG;;EAEH;IACE;;EACA;IACE;;;AAGR;EAEE;IAAkB;;;AAErB;EAEE;IACE;IACA;IACA;;EAIA;IACE;IACD","file":"admin.css"} \ No newline at end of file diff --git a/enable-media-replace.php b/enable-media-replace.php index cc37764..59dcfd3 100644 --- a/enable-media-replace.php +++ b/enable-media-replace.php @@ -3,7 +3,7 @@ * Plugin Name: Enable Media Replace * Plugin URI: https://wordpress.org/plugins/enable-media-replace/ * Description: Enable replacing media files by uploading a new file in the "Edit Media" section of the WordPress Media Library. - * Version: 4.0.3 + * Version: 4.1.0 * Author: ShortPixel * Author URI: https://shortpixel.com * GitHub Plugin URI: https://github.com/short-pixel-optimizer/enable-media-replace @@ -25,9 +25,7 @@ * */ -namespace EnableMediaReplace; - -define( 'EMR_VERSION', '4.0.3' ); +define( 'EMR_VERSION', '4.1.0' ); if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. @@ -76,17 +74,27 @@ require_once( $plugin_path . 'classes/emr-plugin.php' ); require_once( $plugin_path . 'classes/installHelper.php' ); +// @todo Needs replacing with PSR-4 +require_once( $plugin_path . 'classes/Controller/ReplaceController.php'); +require_once( $plugin_path . 'classes/Controller/RemoteNoticeController.php'); + +require_once( $plugin_path . 'classes/ViewController.php'); +require_once( $plugin_path . 'classes/ViewController/UploadViewController.php'); +require_once( $plugin_path . 'classes/ViewController/ReplaceViewController.php'); +require_once( $plugin_path . 'classes/ViewController/RemoveBackgroundViewController.php'); + require_once( $plugin_path . 'classes/externals.php' ); require_once( $plugin_path . 'classes/external/elementor.php' ); require_once( $plugin_path . 'classes/external/wpbakery.php' ); require_once( $plugin_path . 'classes/external/upsell_installer.php' ); require_once( $plugin_path . 'classes/external/siteorigin.php' ); +require_once( $plugin_path . 'classes/external/wp-offload.php' ); require_once( $plugin_path . 'thumbnail_updater.php' ); function emr() { - return EnableMediaReplacePlugin::get(); + return EnableMediaReplace\EnableMediaReplacePlugin::get(); } emr(); // runtime. diff --git a/img/notices/robo-cool.png b/img/notices/robo-cool.png new file mode 100644 index 0000000..b30d49f Binary files /dev/null and b/img/notices/robo-cool.png differ diff --git a/img/notices/robo-scared.png b/img/notices/robo-scared.png new file mode 100644 index 0000000..97f1f35 Binary files /dev/null and b/img/notices/robo-scared.png differ diff --git a/img/notices/slider.png b/img/notices/slider.png new file mode 100644 index 0000000..a3650ed Binary files /dev/null and b/img/notices/slider.png differ diff --git a/js/emr_admin.js b/js/emr_admin.js index a7e8837..0cb2d39 100644 --- a/js/emr_admin.js +++ b/js/emr_admin.js @@ -8,7 +8,6 @@ var target_is_image; var is_debug = false; - var is_dragging = false; this.init = function() @@ -26,9 +25,17 @@ $('.replace_custom_date').on('click', $.proxy(this.updateCustomDate, this)); // DragDrop - $('.wrap.emr_upload_form').on('dragover', $.proxy(this.dragOverArea, this)); - $('.wrap.emr_upload_form').on('dragleave', $.proxy(this.dragOutArea, this)); + //$(document).on('dragover', $.proxy(this.dragOverArea, this)); + //$(document).on('dragleave', $.proxy(this.dragOutArea, this)); + document.addEventListener('dragover', this.dragOverArea.bind(this), false ); + document.addEventListener('dragleave', this.dragOutArea.bind(this), false ); + + + $('.emr_drop_area').on('drop', $.proxy(this.fileDrop, this)); + $('.upload-file-action').on('click', function () { + var input = document.getElementById('upload-file').click(); + }); this.checkCustomDate(); this.loadDatePicker(); @@ -52,6 +59,7 @@ } + this.loadDatePicker = function() { $('#emr_datepicker').datepicker({ @@ -68,6 +76,7 @@ }, }); } + this.checkCustomDate = function() { if ($('input[name="timestamp_replace"]:checked').val() == 3) @@ -138,16 +147,19 @@ img.src = window.URL.createObjectURL(file); self = this; - img.setAttribute('style', 'max-width:100%; max-height: 100%;'); img.addEventListener("load", function () { // with formats like svg it can be rough. - var width = img.naturalWidth; - var height = img.naturalHeight; - if (width == 0) + + var width = img.naturalWidth; + var height = img.naturalHeight; + + if (width == 0) width = img.width; if (height == 0) height = img.height; - // $(preview).find('.textlayer').text(img.naturalWidth + ' x ' + img.naturalHeight ); + + img.setAttribute('style', 'z-index:2; position: relative; max-width:100%; max-height: 100%; width: ' + width + 'px; height: ' + height + 'px;'); + self.updateTextLayer(preview, width + ' x ' + height); self.updateFileSize(preview, file); }); @@ -314,34 +326,57 @@ { e.preventDefault(); e.stopPropagation(); + console.log(e); - if ( this.is_dragging) + if (true == this.is_dragging) return; - //this.debug('dragover'); - //$('.emr_drop_area').css('border-color', '#83b4d8'); - $('.emr_drop_area').addClass('drop_breakout'); + var el = document.getElementById('emr-drop-area'); + var showEl = el.cloneNode(true); + showEl.id = 'emr-drop-area-active'; + + var child = document.body.appendChild(showEl); + + child.addEventListener('drop', this.fileDrop.bind(this), false); + + child.addEventListener('dragover', function(event){ + event.preventDefault(); + }) + + this.is_dragging = true; } + this.dragOutArea = function(e) { - e.preventDefault(); - e.stopPropagation(); - // this.debug('dragout'); - //$('.emr_drop_area').css('border-color', '#b4b9be'); - $('.emr_drop_area').removeClass('drop_breakout'); + + // event is not passed on filedrop. remove overlay then. + if (typeof e !== 'undefined') + { + e.preventDefault(); + e.stopPropagation(); + + if (e.clientX != 0 || e.clientY != 0) { + return false; + } + } + var removeEl = document.getElementById('emr-drop-area-active'); + if (removeEl !== null) + document.getElementById('emr-drop-area-active').remove(); + this.is_dragging = false; } this.fileDrop = function (e) { - var ev = e.originalEvent; - this.dragOutArea(e); - ev.preventDefault(); + // var ev = e.originalEvent; + this.dragOutArea(); + //ev.preventDefault(); + e.stopPropagation(); e.preventDefault(); - if (ev.dataTransfer.items) { + if (e.dataTransfer.items) { // Use DataTransferItemList interface to access the file(s) - document.getElementById('userfile').files = ev.dataTransfer.files; + document.getElementById('upload-file').files = e.dataTransfer.files; $('input[name="userfile"]').trigger('change'); } } diff --git a/js/emr_success.js b/js/emr_success.js new file mode 100644 index 0000000..bac8cf6 --- /dev/null +++ b/js/emr_success.js @@ -0,0 +1,24 @@ +window.addEventListener('load', function(event) { + + var url = new URL(window.location.href); + url.searchParams.set('emr_success', 1); + + var timeout = 10; + var counter = document.getElementById('redirect_counter'); + var redirectUrl = document.getElementById('redirect_url'); + var redirected = false; + + counter.textContent = timeout; + + var t = window.setInterval(function () { + counter.textContent = timeout; + timeout--; + if (timeout <= 0 && false == redirected) + { + window.location.href = redirectUrl; + redirected = true; + window.clearInterval(t); + } + }, 1000); + +}); diff --git a/js/remove_bg.js b/js/remove_bg.js index 8a30571..fbb92a9 100644 --- a/js/remove_bg.js +++ b/js/remove_bg.js @@ -19,8 +19,8 @@ jQuery(document).ready(function ($) { const nonce = emrObject.nonce; const attachment_id = $('input[name="ID"]').val(); const action = 'emr_remove_background'; - const bgType = $('input[type=radio][name=background_type]:checked').val(); - const cLvl = $('input[type=radio][name=compression_level]:checked').val(); + const bgType = $('input[type=radio][name="background_type"]:checked').val(); + const cLvl = $('input[type=radio][name="compression_level"]:checked').val(); let background = { type: "transparent" } @@ -45,9 +45,10 @@ jQuery(document).ready(function ($) { $('html, body').animate({ scrollTop: $(".emr_upload_form").offset().top }, 1000); - $('input[type=radio][name=background_type]').attr('disabled', 'disabled') - $('input[type=radio][name=compression_level]').attr('disabled', 'disabled') - $('#remove_background_button').attr('disabled', 'disabled') + $('input[type=radio][name=background_type]').attr('disabled', 'disabled'); + $('input[type=radio][name=compression_level]').attr('disabled', 'disabled'); + $('#remove_background_button').attr('disabled', 'disabled'); + $('h1.response').remove(); $('#overlay').css('visibility', 'visible'); var preview = $('.image_placeholder').last(); preview.find('img').remove(); @@ -97,7 +98,7 @@ jQuery(document).ready(function ($) { // initComparisons(); }else{ - preview.prepend(`

${response.message}

`); + preview.prepend(`

${response.message}

`); $('#remove_background_button').attr('disabled', false) $('input[type=radio][name=background_type]').attr('disabled', false) $('input[type=radio][name=compression_level]').attr('disabled', false) diff --git a/js/upsell.js b/js/upsell.js index eba58ca..40bfb88 100644 --- a/js/upsell.js +++ b/js/upsell.js @@ -2,7 +2,6 @@ jQuery(document).ready(function($) { $('.emr-installer').on('click', function(e){ e.preventDefault(); - // var $this = $(this); var button = $(this); var plugin = button.data('plugin'); var nonce = $('#upsell-nonce').val(); diff --git a/readme.txt b/readme.txt index 819bcd8..c535c3d 100644 --- a/readme.txt +++ b/readme.txt @@ -5,13 +5,13 @@ Tags: replace, attachment, media, files, replace image, remove background, repla Requires at least: 4.9.7 Tested up to: 6.2 Requires PHP: 5.6 -Stable tag: 4.0.3 +Stable tag: 4.1.0 Easily replace any attached image/file by simply uploading a new file in the Media Library edit view - a real time saver! == Description == -**A free, lightweight and easy to use plugin that allows you to seamlessly replace an image or file in your Media Library by uploading a new file in its place. No more deleting, renaming and re-uploading files! +**A free, lightweight and easy to use plugin that allows you to seamlessly replace an image or file in your Media Library by uploading a new file in its place. No more deleting, renaming and re-uploading files! Now fully compatible with WP Offload Media! New beta feature! You can now remove the background of your images for better integration with eCommerce solutions! Supported by the friendly team that created ShortPixel :)** @@ -34,6 +34,10 @@ This plugin is very powerful and a must-have for all major websites built with W Similar to replacing media, you can also remove the background of the images from the Media Library! The background removal feature sends the images to ShortPixel's API, removes the background and sends them back in a preview window. If everything looks good, just replace the image with the one that has the background removed! If the source image is a PNG file, you will get a transparent background, while the other images default to a solid white background. You also have the option to choose a different color with an embedded color picker. The background removal feature is still in beta and will be free of charge for a reasonable usage. +If you don't want to use the background removal feature, add this line to your theme's functions.php file, or use a plugin like Code Snippets: + +```add_filter( 'emr/feature/background', '__return_false' );``` + #### Show file modification time There is a shortcode that takes the file modification date and displays it in a post or on a page. The code is: @@ -61,6 +65,19 @@ If you want more control over the format in which the time is shown, you can use == Changelog == += 4.1.0 = + +Release date: March 28, 2023 +* New: Integration with WP Offload Media; items that are offloaded can now be replaced correctly; +* New: Switch to a Replacer module that improves the code and makes it more robust; +* New: Adding an intermediate confirmation page to solve possible redirection issues; +* Tweak: The new image can now be dragged and dropped directly into the preview area; +* Tweak: Improved the display of both the original image and the new image on the replacement screen; +* Tweak: Improved the notification system and added a mechanism for remote notifications; +* Tweak: Updated the texts and banners to look better on the replacement screen; +* Fix: Added documentation in the readme to disable the background removal feature (kudos to @jstask82); +* Fix: Increased the security of the plugin by adding checks for various AJAX calls; + = 4.0.3 = Release date: February 21, 2023 diff --git a/scss/_screens.scss b/scss/_screens.scss new file mode 100644 index 0000000..aeb1d86 --- /dev/null +++ b/scss/_screens.scss @@ -0,0 +1,39 @@ + +.enable-media-replace.emr-screen +{ + + width: 90%; + background: #fff; + border: 1px solid #fff; + position: relative; + margin: 30px 0; + + h3 { + font-size: 24px; + position: absolute; + top: -15px; + left: 30px; + padding: 16px 8px; + border: 1px solid #000; + background: #fff; + margin: 0; + } + .content + { + padding: 32px; + + } + h1 { + font-weight: 700; + } + + p { font-size: 14px; } + + + .hide { display: none; } + + &.error-screen { + h1 { color: #ff0000; } + } + +} diff --git a/scss/admin.scss b/scss/admin.scss index 4c61c45..e64d6a6 100644 --- a/scss/admin.scss +++ b/scss/admin.scss @@ -1,5 +1,36 @@ @import 'datepicker'; +@import 'screens'; +#emr-drop-area-active +{ +// border: 4px dashed #b4b9be; +// max-width: 600px; + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 9999; + display: block; + background: rgba(0,0,0,0.5); + border: 6px dashed #AAA; + + h3 { + font-size: 36px; + color: #ddd; + width: 200px; + left: 50%; + top: 50%; + position:absolute; + } + + .drop-wrapper + { + margin: 0 auto; + } +} + +// The main form. .emr_upload_form { form @@ -15,6 +46,9 @@ { width: 100%; } + .emr_drop_area { + display: none; + } .wrapper { @@ -32,52 +66,29 @@ } } + .explainer { + font-size: 14px; + color: #555; + max-width: 800px; + margin: 20px 0; + .underline { + text-decoration-style: dotted; + text-decoration-line: underline; + } + + + } + .image_chooser.wrapper { min-height: 350px; - .emr_drop_area - { - border: 4px dashed #b4b9be; - max-width: 600px; - padding: 28px 14px; - text-align: center; - position: relative; - h1 { display: none; } - .drop-wrapper - { - margin: 0 auto; - } - &.drop_breakout - { - position: fixed; - left: 0; - right:0; - bottom: 0; - top: 0; - max-width: none; - border-color: #83b4d8; - border-width: 10px; - z-index: 999999; - background-color: rgba(#444, 0.7); - h1 { - color: #fff; - position: absolute; - font-size: 50px; - line-height: 50px; - margin-top: -25px; - top: 50%; - width: 100%; - text-align: center; - display: block; - } - .drop-wrapper { display: none; } - } - } .image_previews { margin: 15px 0; + position: relative; + .image_placeholder { position: relative; @@ -86,7 +97,26 @@ margin-bottom: 10px; border: 1px solid #ddd; vertical-align: top; - // max-height: 500px; + box-sizing: border-box; + background: #fff; + max-width: calc(50% - 25px); + + img { object-fit: contain; } + + .upload-title + { + text-align: center; + position: absolute; + padding-top: 8px; + line-height: 20px; + font-weight: 700; + width: 100%; + left: 0; + top: 0; + } + #upload-file { + display: none; + } .textlayer { font-size: 25px; @@ -100,6 +130,7 @@ border: 1px dashed #eee; background-color: #333; padding: 8px; + z-index: 5; //max-width: 100%; } .dashicons @@ -120,6 +151,28 @@ bottom: -25px; width: 100%; } + .drag-and-drop-title + { + text-align: center; + font-weight: 400; + font-size: 14px; + width: 100%; + height: 100%; + position: absolute; + cursor: pointer; + top: 0; + left: 0; + z-index: 1; + span { + position: absolute; + // left: 46%; + top: 50%; + transform: translate(-50%,-50%); + padding: 5px 10px; + width: 100%; + + } + } &.is_image { .dashicons::before, .dashicons { display: none } diff --git a/views/do-replace-background.php b/views/do-replace-background.php index fb1871e..b40e059 100644 --- a/views/do-replace-background.php +++ b/views/do-replace-background.php @@ -5,18 +5,11 @@ use EnableMediaReplace\Notices\NoticeController as Notices; use \EnableMediaReplace\Replacer as Replacer; - if (! defined('ABSPATH')) { exit; // Exit if accessed directly. } -if (! check_admin_referer('do_background_replace')) -{ - wp_die(esc_html__('Nonce in form failed. Go back, refresh and try again.', 'enable-media-replace')); -} - $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : null; - if (is_null($key) || strlen($key) == 0) { wp_die(esc_html__('Error while sending form (no key). Please try again.', 'enable-media-replace')); @@ -41,9 +34,6 @@ $datetime = current_time('mysql'); $replacer->setTimeMode( \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED, $datetime); - - - $api = new Api(); $result = $api->handleDownload($key); diff --git a/views/error.php b/views/error.php new file mode 100644 index 0000000..26986c4 --- /dev/null +++ b/views/error.php @@ -0,0 +1,36 @@ + + +
+

+ +
+

+

errorMessage; ?>

+ + ' . $view->errorDescription . '

'; + } ?> + +

', ''); ?>

+ + +

', ''); ?>

+
+
+ + +uiHelper(); -$attachment_id = intval($_GET['attachment_id']); -$attachment = get_post($attachment_id); +$attachment_id = $view->attachment->ID; +//$attachment_id = intval($_GET['attachment_id']); +//$attachment = get_post($attachment_id); -$replacer = new Replacer($attachment_id); - -$file = $replacer->getSourceFile(); +//$replacer = new Replacer($attachment_id); +//$file = $replacer->getSourceFile(); $uiHelper->setPreviewSizes(); $uiHelper->setSourceSizes($attachment_id); -$base_image = $uiHelper->getPreviewImage($attachment_id, $file ); -$replace_image = $uiHelper->getPreviewImage(-1, $file, array('remove_bg_ui' => true) ); +$base_image = $uiHelper->getPreviewImage($attachment_id, $view->sourceFile ); +$replace_image = $uiHelper->getPreviewImage(-1, $view->sourceFile, array('remove_bg_ui' => true) ); $formurl = $uiHelper->getFormUrl($attachment_id, 'do_background_replace'); -$formurl = wp_nonce_url( $formurl, "do_background_replace" ); +//$formurl = wp_nonce_url( $formurl, "do_background_replace" ); $linebreak = '%0D%0A'; $linebreak_double = $linebreak . $linebreak; $email_subject = __('Bad remove of background report', 'enable-media-replace'); $email_body = sprintf(__('Hello! %s This is a report of a background removal that did not go well %s Url: {url} %s Settings : {settings} %s Thank you! %s', 'enable-media-replace'), $linebreak_double, $linebreak_double, $linebreak, $linebreak_double, $linebreak_double); - $replace_url = add_query_arg(array( 'page' => 'enable-media-replace/enable-media-replace.php', 'action' => 'media_replace', 'attachment_id' => $attachment_id, ), admin_url("upload.php")); -$defaults = array( - 'bg_type' => 'transparent', - 'bg_color' => '#ffffff', - 'bg_transparency' => 100, -); -$settings = get_option('enable_media_replace', $defaults); - -$settings = array_merge($defaults, $settings); // might miss some ?> +
+ + + + @@ -71,7 +67,9 @@ -

Replace this image with another one instead!

+

+ +

@@ -80,7 +78,7 @@

@@ -89,7 +87,7 @@

@@ -97,52 +95,27 @@

-
diff --git a/views/popup.php b/views/screen.php similarity index 78% rename from views/popup.php rename to views/screen.php index 44a1537..2b9beff 100644 --- a/views/popup.php +++ b/views/screen.php @@ -1,6 +1,10 @@ prefix . "posts"; -$attachment_id = intval($_GET['attachment_id']); -$attachment = get_post($attachment_id); - -if (! emr()->checkImagePermission($attachment)) -{ - wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') ); -} - -$replacer = new Replacer($attachment_id); -$file = $replacer->getSourceFile(); -$filepath = $file->getFullPath(); -$filename = $file->getFileName(); -$filetype = $file->getExtension(); -$source_mime = get_post_mime_type($attachment_id); +$attachment_id = $view->attachment->ID; +$settings = $view->settings; +$sourceFile = $view->sourceFile; $uiHelper = emr()->uiHelper(); -$uiHelper->setPreviewSizes(); -$uiHelper->setSourceSizes($attachment_id); - - - -$defaults = array( - 'replace_type' => 'replace', - 'timestamp_replace' => \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED, - 'custom_date' => date("Y-m-d H:i:s"), - 'new_location' => false, - 'new_location_dir' => false, -); -$settings = get_option('enable_media_replace', $defaults); - -$settings = array_merge($defaults, $settings); // might miss some ?>
+ +

+

getFormUrl($attachment_id); - $formurl = wp_nonce_url( $url, "media_replace_upload" ); + $formurl = $uiHelper->getFormUrl($attachment_id); + //$formurl = wp_nonce_url( $url, "media_replace_upload" ); + if (FORCE_SSL_ADMIN) { $formurl = str_replace("http:", "https:", $formurl); } ?> +
-
+ -

+

+ getFullPath() . '">' . $sourceFile->getFileName() . '', '','', '
' ); + ?> +

+

%s','enable-media-replace'), size_format(wp_max_upload_size() ) ) ?>

', ''); ?>

@@ -95,30 +78,25 @@

-
-
+
-

-

-
+ -
- sourceMime == 'application/pdf') { - echo $uiHelper->getPreviewImage($attachment_id, $file); - echo $uiHelper->getPreviewImage(-1, $file); + echo $uiHelper->getPreviewImage($attachment_id, $sourceFile); + echo $uiHelper->getPreviewImage(-1, $sourceFile, array('is_upload' => true)); } else { - if (strlen($filepath) == 0) // check if image in error state. + if (strlen($sourceFile->getFullPath()) == 0) // check if image in error state. { echo $uiHelper->getPreviewError(-1); - echo $uiHelper->getPreviewImage(-1, $file); + echo $uiHelper->getPreviewImage(-1, $sourceFile, array('is_upload' => true)); } else { - echo $uiHelper->getPreviewFile($attachment_id, $file); - echo $uiHelper->getPreviewFile(-1, $file); + echo $uiHelper->getPreviewFile($attachment_id, $sourceFile); + echo $uiHelper->getPreviewFile(-1, $sourceFile, array('is_upload' => true)); } } @@ -134,7 +112,7 @@ ?>

 

- isBackgroundRemovable($attachment)): ?> + isBackgroundRemovable($view->attachment)): ?>
@@ -149,7 +127,7 @@
-
+

- + getExtension(), $sourceFile->getFileName() ); ?>

@@ -181,7 +159,7 @@ -

+

getFileName() ); ?>

@@ -190,15 +168,15 @@
-
+
post_date) ); + $attachment_current_date = date_i18n('d/M/Y H:i', strtotime($view->attachment->post_date) ); $attachment_now_date = date_i18n('d/M/Y H:i' ); $time = current_time('mysql'); $date = $nowDate = new \dateTime($time); // default to now. - $attachmentDate = new \dateTime($attachment->post_date); + $attachmentDate = new \dateTime($view->attachment->post_date); if ($settings['timestamp_replace'] == \EnableMediaReplace\Replacer::TIME_CUSTOM) @@ -218,8 +196,8 @@ - @   - + @   + @@ -235,11 +213,25 @@ class='emr_datepicker' /> if ($settings['new_location'] !== false) $subdir = $settings['new_location_dir']; ?> + +
+ +
+
+ is_movable): ?> - +
+ custom_basedir ?> +
+
diff --git a/views/success.php b/views/success.php new file mode 100644 index 0000000..ad90e76 --- /dev/null +++ b/views/success.php @@ -0,0 +1,37 @@ + + +
+

+
+

+ +

+ +

+ +

', ''); ?>

+ +

', + '', ''); + ?> + +

+ +
+ +
+ +prefix . "posts"; -$postmeta_table_name = $wpdb->prefix . "postmeta"; // Starts processing. $uihelper = emr()->uiHelper(); @@ -143,13 +140,6 @@ // Gather all functions that both options do. do_action('wp_handle_replace', array('post_id' => $post_id)); - -/* if ($mode = \EnableMediaReplace\Replacer::MODE_SEARCHREPLACE && $do_new_location && ! is_null($new_location_dir)) - { - exit($new_filename); - $newdirfile = $replacer->newTargetLocation($new_location_dir); - } -*/ try { $result = $replacer->replaceWith($_FILES["userfile"]["tmp_name"], $new_filename); } catch (\RunTimeException $e) { @@ -162,7 +152,6 @@ wp_safe_redirect($redirect_error); exit(); } -// $returnurl = admin_url("/post.php?post={$_POST["ID"]}&action=edit&message=1"); // Execute hook actions - thanks rubious for the suggestion! } else { diff --git a/views/upsell.php b/views/upsell.php index f5eb6c8..4d35f1a 100644 --- a/views/upsell.php +++ b/views/upsell.php @@ -31,13 +31,6 @@
- -
-

ShortPixel Theme

- -
- - @@ -76,7 +69,7 @@
- ShortPixel + ShortPixel

@@ -103,7 +96,14 @@

- + + + +
+

ShortPixel Theme

+ +
+