Enable Media Replace - Version 4.0.0

Version Description

Release date: September 5th, 2022 * New: added the functionality to remove the background for any image; * Fix: images added to the new block-style widgets were not replaced; * Fix: the original file was not removed after replacement if a multilingual plugin was installed; * Fix: additional checks were added to the new upload path for replacements, to avoid possible vulnerabilities, kudos to @soulseekah; * Fix: an object cache flush was added after an image was replaced to prevent the content from still being cached in the post editor; * Fix: if there was no _wp_attached_file in the postmeta table a fatal error was thrown; * Fix: the time zone was not displayed correctly on the Replace Media screen; * Fix: added some additional checks for file path to avoid open_basedir restrictions; * Fix: added titles for the Replace Media and Remove Background screens; * Fix: various small CSS/JS fixes, wording updates and code cleanups;

Download this release

Release Info

Developer petredobrescu
Plugin Icon 128x128 Enable Media Replace
Version 4.0.0
Comparing to
See all releases

Code changes from version 3.6.3 to 4.0.0

build/shortpixel/autoload.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- require_once (dirname(__FILE__) . "/PackageLoader.php");
3
  $loader = new EnableMediaReplace\Build\PackageLoader();
4
  $loader->load(__DIR__);
5
 
1
  <?php
2
+ require_once (__DIR__ . "/PackageLoader.php");
3
  $loader = new EnableMediaReplace\Build\PackageLoader();
4
  $loader->load(__DIR__);
5
 
build/shortpixel/composer.json CHANGED
@@ -1 +1 @@
1
- {"name":"EnableMediaReplace\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"EnableMediaReplace\\ShortPixelLogger":"log\/src","EnableMediaReplace\\Notices":"notices\/src"}}}
1
+ {"name":"EnableMediaReplace\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"EnableMediaReplace\\ShortPixelLogger":"log\/src","EnableMediaReplace\\Notices":"notices\/src","EnableMediaReplace\\FileSystem":"filesystem\/src"}}}
build/shortpixel/filesystem/composer.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "shortpixel/filesystem",
3
+ "description": "ShortPixel FileSystem",
4
+ "version": 1.0,
5
+ "type": "library",
6
+ "license": "MIT",
7
+ "authors": [
8
+ {
9
+ "name": "Bas",
10
+ "email": "bas@weblogmechanic.com"
11
+ }
12
+ ],
13
+ "minimum-stability": "dev",
14
+ "require": {},
15
+ "autoload": {
16
+ "psr-4": { "ShortPixel\\FileSystem\\" : "src" }
17
+ }
18
+ }
build/shortpixel/filesystem/src/Controller/FileSystemController.php ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace\FileSystem\Controller;
3
+ use EnableMediaReplace\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ use EnableMediaReplace\FileSystem\Model\File\DirectoryModel as DirectoryModel;
6
+ use EnableMediaReplace\FileSystem\Model\File\FileModel as FileModel;
7
+
8
+
9
+ /** Controller for FileSystem operations
10
+ *
11
+ * This controller is used for -compound- ( complex ) FS operations, using the provided models File en Directory.
12
+ * USE via \wpSPIO()->filesystem();
13
+ */
14
+ Class FileSystemController
15
+ {
16
+
17
+ public function __construct()
18
+ {
19
+
20
+ }
21
+
22
+ /** Get FileModel for a certain path. This can exist or not
23
+ *
24
+ * @param String Path Full Path to the file
25
+ * @return FileModel FileModel Object. If file does not exist, not all values are set.
26
+ */
27
+ public function getFile($path)
28
+ {
29
+ return new FileModel($path);
30
+ }
31
+
32
+
33
+
34
+ /** Get DirectoryModel for a certain path. This can exist or not
35
+ *
36
+ * @param String $path Full Path to the Directory.
37
+ * @return DirectoryModel Object with status set on current directory.
38
+ */
39
+ public function getDirectory($path)
40
+ {
41
+ return new DirectoryModel($path);
42
+ }
43
+
44
+
45
+
46
+
47
+ /** This function returns the WordPress Basedir for uploads ( without date and such )
48
+ * Normally this would point to /wp-content/uploads.
49
+ * @returns DirectoryModel
50
+ */
51
+ public function getWPUploadBase()
52
+ {
53
+ $upload_dir = wp_upload_dir(null, false);
54
+
55
+ return $this->getDirectory($upload_dir['basedir']);
56
+ }
57
+
58
+ /** This function returns the Absolute Path of the WordPress installation where the **CONTENT** directory is located.
59
+ * Normally this would be the same as ABSPATH, but there are installations out there with -cough- alternative approaches
60
+ * @returns DirectoryModel Either the ABSPATH or where the WP_CONTENT_DIR is located
61
+ */
62
+ public function getWPAbsPath()
63
+ {
64
+ $wpContentAbs = str_replace( 'wp-content', '', WP_CONTENT_DIR);
65
+ if (ABSPATH == $wpContentAbs)
66
+ $abspath = ABSPATH;
67
+ else
68
+ $abspath = $wpContentAbs;
69
+
70
+ if (defined('UPLOADS')) // if this is set, lead.
71
+ $abspath = trailingslashit(ABSPATH) . UPLOADS;
72
+
73
+ $abspath = apply_filters('shortpixel/filesystem/abspath', $abspath );
74
+
75
+ return $this->getDirectory($abspath);
76
+ }
77
+
78
+
79
+ /** Utility function that tries to convert a file-path to a webURL.
80
+ *
81
+ * If possible, rely on other better methods to find URL ( e.g. via WP functions ).
82
+ */
83
+ public function pathToUrl(FileModel $file)
84
+ {
85
+ $filepath = $file->getFullPath();
86
+ $directory = $file->getFileDir();
87
+
88
+ $is_multi_site = $this->env->is_multisite;
89
+ $is_main_site = $this->env->is_mainsite;
90
+
91
+ // stolen from wp_get_attachment_url
92
+ if ( ( $uploads = wp_get_upload_dir() ) && (false === $uploads['error'] || strlen(trim($uploads['error'])) == 0 ) ) {
93
+ // Check that the upload base exists in the file location.
94
+ if ( 0 === strpos( $filepath, $uploads['basedir'] ) ) { // Simple as it should, filepath and basedir share.
95
+ // Replace file location with url location.
96
+ $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $filepath );
97
+ }
98
+ // Multisite backups are stored under uploads/ShortpixelBackups/etc , but basedir would include uploads/sites/2 etc, not matching above
99
+ // If this is case, test if removing the last two directories will result in a 'clean' uploads reference.
100
+ // This is used by getting preview path ( backup pathToUrl) in bulk and for comparer..
101
+ elseif ($is_multi_site && ! $is_main_site && 0 === strpos($filepath, dirname(dirname($uploads['basedir']))) )
102
+ {
103
+
104
+ $url = str_replace( dirname(dirname($uploads['basedir'])), dirname(dirname($uploads['baseurl'])), $filepath );
105
+ $homeUrl = home_url();
106
+
107
+ // The result didn't end in a full URL because URL might have less subdirs ( dirname dirname) .
108
+ // This happens when site has blogs.dir (sigh) on a subdomain . Try to substitue the ABSPATH root with the home_url
109
+ if (strpos($url, $homeUrl) === false)
110
+ {
111
+ $url = str_replace( trailingslashit(ABSPATH), trailingslashit($homeUrl), $filepath);
112
+ }
113
+
114
+ } elseif ( false !== strpos( $filepath, 'wp-content/uploads' ) ) {
115
+ // Get the directory name relative to the basedir (back compat for pre-2.7 uploads)
116
+ $url = trailingslashit( $uploads['baseurl'] . '/' . _wp_get_attachment_relative_path( $filepath ) ) . wp_basename( $filepath );
117
+ } else {
118
+ // It's a newly-uploaded file, therefore $file is relative to the basedir.
119
+ $url = $uploads['baseurl'] . "/$filepath";
120
+ }
121
+ }
122
+
123
+ $wp_home_path = (string) $this->getWPAbsPath();
124
+ // If the whole WP homepath is still in URL, assume the replace when wrong ( not replaced w/ URL)
125
+ // This happens when file is outside of wp_uploads_dir
126
+ if (strpos($url, $wp_home_path) !== false)
127
+ {
128
+ // This is SITE URL, for the same reason it should be home_url in FILEMODEL. The difference is when the site is running on a subdirectory
129
+ // (1) ** This is a fix for a real-life issue, do not change if this causes issues, another fix is needed then.
130
+ // (2) ** Also a real life fix when a path is /wwwroot/assets/sites/2/ etc, in get site url, the home URL is the site URL, without appending the sites stuff. Fails on original image.
131
+ if ($is_multi_site && ! $is_main_site)
132
+ {
133
+ $wp_home_path = wp_normalize_path(trailingslashit($uploads['basedir']));
134
+ $home_url = trailingslashit($uploads['baseurl']);
135
+ }
136
+ else
137
+ $home_url = trailingslashit(get_site_url()); // (1)
138
+ $url = str_replace($wp_home_path, $home_url, $filepath);
139
+ }
140
+
141
+ // can happen if there are WP path errors.
142
+ if (is_null($url))
143
+ return false;
144
+
145
+ $parsed = parse_url($url); // returns array, null, or false.
146
+
147
+ // Some hosts set the content dir to a relative path instead of a full URL. Api can't handle that, so add domain and such if this is the case.
148
+ if ( !isset($parsed['scheme']) ) {//no absolute URLs used -> we implement a hack
149
+
150
+ if (isset($parsed['host'])) // This is for URL's for // without http or https. hackhack.
151
+ {
152
+ $scheme = is_ssl() ? 'https:' : 'http:';
153
+ return $scheme. $url;
154
+ }
155
+ else
156
+ {
157
+ // From Metafacade. Multiple solutions /hacks.
158
+ $home_url = trailingslashit((function_exists("is_multisite") && is_multisite()) ? trim(network_site_url("/")) : trim(home_url()));
159
+ return $home_url . ltrim($url,'/');//get the file URL
160
+ }
161
+ }
162
+
163
+ if (! is_null($parsed) && $parsed !== false)
164
+ return $url;
165
+
166
+ return false;
167
+ }
168
+
169
+ /** Utility function to check if a path is an URL
170
+ * Checks if this path looks like an URL.
171
+ * @param $path String Path to check
172
+ * @return Boolean If path seems domain.
173
+ */
174
+ public function pathIsUrl($path)
175
+ {
176
+ $is_http = (substr($path, 0, 4) == 'http') ? true : false;
177
+ $is_https = (substr($path, 0, 5) == 'https') ? true : false;
178
+ $is_neutralscheme = (substr($path, 0, 2) == '//') ? true : false; // when URL is relative like //wp-content/etc
179
+ $has_urldots = (strpos($path, '://') !== false) ? true : false; // Like S3 offloads
180
+
181
+ if ($is_http || $is_https || $is_neutralscheme || $has_urldots)
182
+ return true;
183
+ else
184
+ return false;
185
+ }
186
+
187
+ /** Sort files / directories in a certain way.
188
+ * Future dev to include options via arg.
189
+ */
190
+ public function sortFiles($array, $args = array() )
191
+ {
192
+ if (count($array) == 0)
193
+ return $array;
194
+
195
+ // what are we sorting.
196
+ $class = get_class($array[0]);
197
+ $is_files = ($class == 'EnableMediaReplace\FileModel') ? true : false; // if not files, then dirs.
198
+
199
+ usort($array, function ($a, $b) use ($is_files)
200
+ {
201
+ if ($is_files)
202
+ return strcmp($a->getFileName(), $b->getFileName());
203
+ else {
204
+ return strcmp($a->getName(), $b->getName());
205
+ }
206
+ }
207
+ );
208
+
209
+ return $array;
210
+
211
+ }
212
+
213
+ public function downloadFile($url, $destinationPath)
214
+ {
215
+
216
+ //$downloadTimeout = defined(SHORTPIXEL_MAX_EXECUTION_TIME) ? max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15) :;
217
+ $max_exec = intval(ini_get('max_execution_time'));
218
+ if ($max_exec === 0) // max execution time of zero means infinite. Quantify.
219
+ $max_exec = 60;
220
+ elseif($max_exec < 0) // some hosts like to set negative figures on this. Ignore that.
221
+ $max_exec = 30;
222
+
223
+ $downloadTimeout = $max_exec;
224
+
225
+
226
+ $destinationFile = $this->getFile($destinationPath);
227
+
228
+ $args_for_get = array(
229
+ 'stream' => true,
230
+ 'filename' => $destinationPath,
231
+ );
232
+
233
+ $response = wp_remote_get( $url, $args_for_get );
234
+
235
+ if(is_wp_error( $response )) {
236
+ Log::addError('Download file failed', array($url, $response->get_error_messages(), $response->get_error_codes() ));
237
+
238
+ // Try to get it then via this way.
239
+ $response = download_url($url, $downloadTimeout);
240
+ if (!is_wp_error($response)) // response when alright is a tmp filepath. But given path can't be trusted since that can be reason for fail.
241
+ {
242
+ $tmpFile = $this->getFile($response);
243
+ $result = $tmpFile->move($destinationFile);
244
+
245
+ } // download_url ..
246
+ else {
247
+ Log::addError('Secondary download failed', array($url, $response->get_error_messages(), $response->get_error_codes() ));
248
+ }
249
+ }
250
+ else { // success, at least the download.
251
+ $destinationFile = $this->getFile($response['filename']);
252
+ }
253
+
254
+ Log::addDebug('Remote Download attempt result', array($url, $destinationPath));
255
+ if ($destinationFile->exists())
256
+ return true;
257
+ else
258
+ return false;
259
+ }
260
+
261
+
262
+
263
+ /** Get all files from a directory tree, starting at given dir.
264
+ * @param DirectoryModel $dir to recursive into
265
+ * @param Array $filters Collection of optional filters as accepted by FileFilter in directoryModel
266
+ * @return Array Array of FileModel Objects
267
+ **/
268
+ public function getFilesRecursive(DirectoryModel $dir, $filters = array() )
269
+ {
270
+ $fileArray = array();
271
+
272
+ if (! $dir->exists())
273
+ return $fileArray;
274
+
275
+ $files = $dir->getFiles($filters);
276
+ $fileArray = array_merge($fileArray, $files);
277
+
278
+ $subdirs = $dir->getSubDirectories();
279
+
280
+ foreach($subdirs as $subdir)
281
+ {
282
+ $fileArray = array_merge($fileArray, $this->getFilesRecursive($subdir, $filters));
283
+ }
284
+
285
+ return $fileArray;
286
+ }
287
+
288
+ // Url very sparingly.
289
+ public function url_exists($url)
290
+ {
291
+ if (! function_exists('curl_init'))
292
+ {
293
+ return null;
294
+ }
295
+
296
+ $ch = curl_init($url);
297
+ curl_setopt($ch, CURLOPT_NOBODY, true);
298
+ curl_exec($ch);
299
+ $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
300
+ curl_close($ch);
301
+
302
+ if ($responseCode == 200)
303
+ {
304
+ return true;
305
+ }
306
+ else {
307
+ return false;
308
+ }
309
+
310
+ }
311
+ }
build/shortpixel/filesystem/src/Model/File/DirectoryModel.php ADDED
@@ -0,0 +1,541 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace\FileSystem\Model\File;
3
+ use EnableMediaReplace\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ /* Model for Directories
6
+ *
7
+ * For all low-level operations on directories
8
+ * Private model of FileSystemController. Please get your directories via there.
9
+ *
10
+ */
11
+
12
+ class DirectoryModel
13
+ {
14
+ // Directory info
15
+ protected $path;
16
+ protected $name;
17
+
18
+ // Directory status
19
+ protected $exists = null;
20
+ protected $is_writable = null;
21
+ protected $is_readable = null;
22
+ protected $is_virtual = null;
23
+
24
+ protected $fields = array();
25
+
26
+ protected $new_directory_permission = 0755;
27
+
28
+ /** Creates a directory model object. DirectoryModel directories don't need to exist on FileSystem
29
+ *
30
+ * When a filepath is given, it will remove the file part.
31
+ * @param $path String The Path
32
+ */
33
+ public function __construct($path)
34
+ {
35
+ $path = wp_normalize_path($path);
36
+ $fs = $this->getFS();
37
+
38
+ if ($fs->pathIsUrl($path))
39
+ {
40
+ $pathinfo = pathinfo($path);
41
+ if (isset($pathinfo['extension'])) // check if this is a file, remove the file information.
42
+ {
43
+ $path = $pathinfo['dirname'];
44
+ }
45
+
46
+ $this->is_virtual = true;
47
+ $this->is_readable = true; // assume
48
+ $this->exists = true;
49
+ }
50
+
51
+ if (! $this->is_virtual() && ! is_dir($path) ) // path is wrong, *or* simply doesn't exist.
52
+ {
53
+ /* Test for file input.
54
+ * If pathinfo is fed a fullpath, it rips of last entry without setting extension, don't further trust.
55
+ * If it's a file extension is set, then trust.
56
+ */
57
+ $pathinfo = pathinfo($path);
58
+
59
+ if (isset($pathinfo['extension']))
60
+ {
61
+ $path = $pathinfo['dirname'];
62
+ }
63
+ elseif (is_file($path))
64
+ $path = dirname($path);
65
+ }
66
+
67
+ if (! $this->is_virtual() && ! is_dir($path))
68
+ {
69
+ /* Check if realpath improves things. We support non-existing paths, which realpath fails on, so only apply on result.
70
+ Moved realpath to check after main pathinfo is set. Reason is that symlinked directories which don't include the WordPress upload dir will start to fail in file_model on processpath ( doesn't see it as a wp path, starts to try relative path). Not sure if realpath should be used anyhow in this model /BS
71
+ */
72
+ $testpath = realpath($path);
73
+ if ($testpath)
74
+ $path = $testpath;
75
+ }
76
+
77
+ $this->path = trailingslashit($path);
78
+
79
+ // Basename doesn't work properly on non-latin ( cyrillic, greek etc ) directory names, returning the parent path instead.
80
+ $dir = new \SplFileInfo($path);
81
+ //basename($this->path);
82
+ $this->name = $dir->getFileName();
83
+
84
+ // Off the keep resources / not sure if needed.
85
+ // if (file_exists($this->path))
86
+ // {
87
+ // $this->exists();
88
+ // $this->is_writable();
89
+ // $this->is_readable();
90
+ // }
91
+ }
92
+
93
+ private function getFS()
94
+ {
95
+ return new \EnableMediaReplace\FileSystem\Controller\FileSystemController();
96
+ }
97
+
98
+
99
+ public function __toString()
100
+ {
101
+ return (string) $this->path;
102
+ }
103
+
104
+ /** Returns path *with* trailing slash
105
+ *
106
+ * @return String Path with trailing slash
107
+ */
108
+ public function getPath()
109
+ {
110
+ return $this->path;
111
+ }
112
+
113
+ public function getModified()
114
+ {
115
+ return filemtime($this->path);
116
+ }
117
+
118
+ /**
119
+ * Get basename of the directory. Without path
120
+ */
121
+ public function getName()
122
+ {
123
+ return $this->name;
124
+ }
125
+
126
+ public function exists()
127
+ {
128
+ if (is_null($this->exists))
129
+ {
130
+ $this->exists = file_exists($this->path) && is_dir($this->path);
131
+ }
132
+ return $this->exists;
133
+ }
134
+
135
+ public function is_writable()
136
+ {
137
+ if (is_null($this->is_writable))
138
+ {
139
+ $this->is_writable = is_writable($this->path);
140
+ }
141
+ return $this->is_writable;
142
+ }
143
+
144
+
145
+ public function is_readable()
146
+ {
147
+ if (is_null($this->is_readable))
148
+ {
149
+ $this->is_readable = is_readable($this->path);
150
+ }
151
+
152
+ return $this->is_readable;
153
+ }
154
+
155
+ public function is_virtual()
156
+ {
157
+ return $this->is_virtual;
158
+ }
159
+ /** Try to obtain the path, minus the installation directory.
160
+ * @return Mixed False if this didn't work, Path as string without basedir if it did. With trailing slash, without starting slash.
161
+ */
162
+ public function getRelativePath()
163
+ {
164
+ // not used anywhere in directory.
165
+ // $upload_dir = wp_upload_dir(null, false);
166
+
167
+ $install_dir = get_home_path();
168
+ if($install_dir == '/') {
169
+ $install_dir = $this->getFS()->getWPAbsPath();
170
+ }
171
+
172
+ $install_dir = trailingslashit($install_dir);
173
+
174
+ $path = $this->getPath();
175
+ // try to build relativePath without first slash.
176
+ $relativePath = str_replace($install_dir, '', $path);
177
+
178
+ if (is_dir( $install_dir . $relativePath) === false)
179
+ {
180
+ $test_path = $this->reverseConstructPath($path, $install_dir);
181
+ if ($test_path !== false)
182
+ {
183
+ $relativePath = $test_path;
184
+ }
185
+ else {
186
+ if($test_path = $this->constructUsualDirectories($path))
187
+ {
188
+ $relativePath = $test_path;
189
+ }
190
+
191
+ }
192
+ }
193
+
194
+ // If relativePath has less amount of characters, changes are this worked.
195
+ if (strlen($path) > strlen($relativePath))
196
+ {
197
+ return ltrim(trailingslashit($relativePath), '/');
198
+ }
199
+ return false;
200
+ }
201
+
202
+
203
+ private function reverseConstructPath($path, $install_path)
204
+ {
205
+ // Array value to reset index
206
+ $pathar = array_values(array_filter(explode('/', $path)));
207
+ $parts = array();
208
+
209
+ if (is_array($pathar))
210
+ {
211
+ // Reverse loop the structure until solid ground is found.
212
+ for ($i = (count($pathar)); $i > 0; $i--)
213
+ {
214
+ $parts[] = $pathar[$i - 1];
215
+ $testpath = implode('/', array_reverse($parts));
216
+ // if the whole thing exists
217
+ if (is_dir($install_path . $testpath) === true)
218
+ {
219
+ return $testpath;
220
+ }
221
+ }
222
+ }
223
+ return false;
224
+
225
+ }
226
+
227
+
228
+ /* Last Resort function to just reduce path to various known WorPress paths. */
229
+ private function constructUsualDirectories($path)
230
+ {
231
+ $pathar = array_values(array_filter(explode('/', $path))); // array value to reset index
232
+ $testpath = false;
233
+ if ( ($key = array_search('wp-content', $pathar)) !== false)
234
+ {
235
+ $testpath = implode('/', array_slice($pathar, $key));
236
+ }
237
+ elseif ( ($key = array_search('uploads', $pathar)) !== false)
238
+ {
239
+ $testpath = implode('/', array_slice($pathar, $key));
240
+ }
241
+
242
+ return $testpath;
243
+ }
244
+
245
+ /** Checks the directory into working order
246
+ * Tries to create directory if it doesn't exist
247
+ * Tries to fix file permission if writable is needed
248
+ * @param $check_writable Boolean Directory should be writable
249
+ */
250
+ public function check($check_writable = false)
251
+ {
252
+ $permission = $this->getPermissionRecursive();
253
+
254
+ if ($permission === false) // if something wrong, return to default.
255
+ {
256
+ $permission = $this->new_directory_permission;
257
+ }
258
+
259
+ if (! $this->exists())
260
+ {
261
+
262
+ Log::addInfo('Directory does not exists. Try to create recursive ' . $this->path . ' with ' . $permission);
263
+
264
+
265
+ $result = @mkdir($this->path, $permission , true);
266
+ chmod ($this->path, $permission );
267
+
268
+ if (! $result)
269
+ {
270
+ $error = error_get_last();
271
+ Log::addWarn('MkDir failed: ' . $error['message'], array($error));
272
+ }
273
+ // reset.
274
+ $this->exists = null;
275
+ $this->is_readable = null;
276
+ $this->is_writable = null;
277
+
278
+ }
279
+ if ($this->exists() && $check_writable && ! $this->is_writable())
280
+ {
281
+ chmod($this->path, $this->permission);
282
+ if (! $this->is_writable()) // perhaps parent permission is no good.
283
+ {
284
+ chmod($this->path, $this->new_directory_permission);
285
+ }
286
+ }
287
+
288
+ if (! $this->exists())
289
+ {
290
+ Log::addInfo('Directory does not exist :' . $this->path);
291
+ return false;
292
+ }
293
+ if ($check_writable && !$this->is_writable())
294
+ {
295
+ Log::addInfo('Directory not writable :' . $this->path);
296
+ return false;
297
+ }
298
+ return true;
299
+ }
300
+
301
+ public function getPermissionRecursive()
302
+ {
303
+ $parent = $this->getParent();
304
+ if (! $parent->exists())
305
+ {
306
+ return $parent->getPermissionRecursive();
307
+ }
308
+ else
309
+ {
310
+ return $parent->getPermissions();
311
+ }
312
+
313
+ }
314
+
315
+ /* Get files from directory
316
+ * @returns Array|boolean Returns false if something wrong w/ directory, otherwise a files array of FileModel Object.
317
+ */
318
+ public function getFiles($args = array())
319
+ {
320
+
321
+ $defaults = array(
322
+ 'date_newer' => null,
323
+ 'exclude_files' => null,
324
+ 'include_files' => null,
325
+ );
326
+ $args = wp_parse_args($args, $defaults);
327
+
328
+ // if all filters are set to null, so point in checking those.
329
+ $has_filters = (count(array_filter($args)) > 0) ? true : false;
330
+
331
+ if ( ! $this->exists() || ! $this->is_readable() )
332
+ return false;
333
+
334
+ $fileArray = array();
335
+
336
+ if ($handle = opendir($this->path)) {
337
+ while (false !== ($entry = readdir($handle))) {
338
+ if ( ($entry != "." && $entry != "..") && ! is_dir($this->path . $entry) ) {
339
+
340
+
341
+ $fileObj = new FileModel($this->path . $entry);
342
+ if ($has_filters)
343
+ {
344
+ if ($this->fileFilter($fileObj,$args) === false)
345
+ {
346
+ $fileObj = null;
347
+ }
348
+ }
349
+
350
+ if (! is_null($fileObj))
351
+ $fileArray[] = $fileObj;
352
+ }
353
+ }
354
+ closedir($handle);
355
+ }
356
+
357
+ /*
358
+ if ($has_filters)
359
+ {
360
+ $fileArray = array_filter($fileArray, function ($file) use ($args) {
361
+ return $this->fileFilter($file, $args);
362
+ } );
363
+ } */
364
+ return $fileArray;
365
+ }
366
+
367
+ // @return boolean true if it should be kept in array, false if not.
368
+ private function fileFilter(FileModel $file, $args)
369
+ {
370
+ $filter = true;
371
+
372
+ if (! is_null($args['include_files']))
373
+ {
374
+ foreach($args['include_files'] as $inc)
375
+ {
376
+ // If any in included is true, filter is good for us.
377
+ $filter = false;
378
+ if (strpos( strtolower($file->getRawFullPath()), strtolower($inc) ) !== false)
379
+ {
380
+ $filter = true;
381
+ break;
382
+ }
383
+ }
384
+ }
385
+ if (! is_null($args['date_newer']))
386
+ {
387
+ $modified = $file->getModified();
388
+ if ($modified < $args['date_newer'] )
389
+ $filter = false;
390
+ }
391
+ if (! is_null($args['exclude_files']))
392
+ {
393
+ foreach($args['exclude_files'] as $ex)
394
+ {
395
+ if (strpos( strtolower($file->getRawFullPath()), strtolower($ex) ) !== false)
396
+ $filter = false;
397
+ }
398
+ }
399
+
400
+ return $filter;
401
+ }
402
+
403
+ /** Get subdirectories from directory
404
+ * * @returns Array|boolean Returns false if something wrong w/ directory, otherwise a files array of DirectoryModel Object.
405
+ */
406
+ public function getSubDirectories()
407
+ {
408
+
409
+ if (! $this->exists() || ! $this->is_readable())
410
+ {
411
+ return false;
412
+ }
413
+
414
+ $dirIt = new \DirectoryIterator($this->path);
415
+ $dirArray = array();
416
+ foreach ($dirIt as $fileInfo)
417
+ { // IsDot must go first here, or there is possiblity to run into openbasedir restrictions.
418
+ if (! $fileInfo->isDot() && $fileInfo->isDir() && $fileInfo->isReadable())
419
+ {
420
+ if ('EnableMediaReplace\Model\File\DirectoryOtherMediaModel' == get_called_class())
421
+ {
422
+ $dir = new DirectoryOtherMediaModel($fileInfo->getRealPath());
423
+ }
424
+ else
425
+ {
426
+ $dir = new DirectoryModel($fileInfo->getRealPath());
427
+ }
428
+
429
+ if ($dir->exists())
430
+ $dirArray[] = $dir;
431
+ }
432
+
433
+ }
434
+ return $dirArray;
435
+ }
436
+
437
+ /** Check if this dir is a subfolder
438
+ * @param DirectoryModel The directoryObject that is tested as the parent */
439
+ public function isSubFolderOf(DirectoryModel $dir)
440
+ {
441
+ // the same path, is not a subdir of.
442
+ if ($this->getPath() === $dir->getPath())
443
+ return false;
444
+
445
+ // the main path must be followed from the beginning to be a subfolder.
446
+ if (strpos($this->getPath(), $dir->getPath() ) === 0)
447
+ {
448
+ return true;
449
+ }
450
+ return false;
451
+ }
452
+
453
+ //** Note, use sparingly, recursive function
454
+ public function getFolderSize()
455
+ {
456
+ $size = 0;
457
+ $files = $this->getFiles();
458
+
459
+ // GetFiles can return Boolean false on missing directory.
460
+ if (! is_array($files))
461
+ {
462
+ return $size;
463
+ }
464
+
465
+ foreach($files as $fileObj)
466
+ {
467
+ $size += $fileObj->getFileSize();
468
+ }
469
+ unset($files); //attempt at performance.
470
+
471
+ $subdirs = $this->getSubDirectories();
472
+
473
+ foreach($subdirs as $subdir)
474
+ {
475
+ $size += $subdir->getFolderSize();
476
+ }
477
+
478
+ return $size;
479
+ }
480
+
481
+ /** Get this paths parent */
482
+ public function getParent()
483
+ {
484
+ $path = $this->getPath();
485
+ $parentPath = dirname($path);
486
+
487
+ $parentDir = new DirectoryModel($parentPath);
488
+
489
+ return $parentDir;
490
+ }
491
+
492
+ public function getPermissions()
493
+ {
494
+ if (! $this->exists())
495
+ {
496
+ Log::addWarning('Directory not existing (fileperms): '. $this->getPath() );
497
+ return false;
498
+ }
499
+ $perms = fileperms($this->getPath());
500
+
501
+ if ($perms !== false)
502
+ {
503
+ return $perms;
504
+ }
505
+ else
506
+ return false;
507
+
508
+ }
509
+
510
+ public function delete()
511
+ {
512
+ return rmdir($this->getPath());
513
+ }
514
+
515
+ /** This will try to remove the whole structure. Use with care.
516
+ * This is mostly used to clear the backups.
517
+ */
518
+ public function recursiveDelete()
519
+ {
520
+ if (! $this->exists() || ! $this->is_writable())
521
+ return false;
522
+
523
+ // This is a security measure to prevent unintended wipes.
524
+ $wpdir = $this->getFS()->getWPUploadBase();
525
+ if (! $this->isSubFolderOf($wpdir))
526
+ return false;
527
+
528
+ $files = $this->getFiles();
529
+ $subdirs = $this->getSubDirectories();
530
+
531
+ foreach($files as $file)
532
+ $file->delete();
533
+
534
+ foreach($subdirs as $subdir)
535
+ $subdir->recursiveDelete();
536
+
537
+ $this->delete();
538
+
539
+ }
540
+
541
+ }
build/shortpixel/filesystem/src/Model/File/FileModel.php ADDED
@@ -0,0 +1,656 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace\FileSystem\Model\File;
3
+ use EnableMediaReplace\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ /* FileModel class.
6
+ *
7
+ *
8
+ * - Represents a -single- file.
9
+ * - Can handle any type
10
+ * - Usually controllers would use a collection of files
11
+ * - Meant for all low-level file operations and checks.
12
+ * - Every file can have a backup counterpart.
13
+ *
14
+ */
15
+ class FileModel
16
+ {
17
+
18
+ // File info
19
+ protected $fullpath = null;
20
+ protected $filename = null; // filename + extension
21
+ protected $filebase = null; // filename without extension
22
+ protected $directory = null;
23
+ protected $extension = null;
24
+ protected $mime = null;
25
+ protected $permissions = null;
26
+
27
+ // File Status
28
+ protected $exists = null;
29
+ protected $is_writable = null;
30
+ protected $is_readable = null;
31
+ protected $is_file = null;
32
+ protected $is_virtual = false;
33
+
34
+ protected $status;
35
+
36
+ protected $backupDirectory;
37
+
38
+ const FILE_OK = 1;
39
+ const FILE_UNKNOWN_ERROR = 2;
40
+
41
+
42
+ /** Creates a file model object. FileModel files don't need to exist on FileSystem */
43
+ public function __construct($path)
44
+ {
45
+ $this->fullpath = trim($path);
46
+ $fs = $this->getFS();
47
+ if ($fs->pathIsUrl($path)) // Asap check for URL's to prevent remote wrappers from running.
48
+ {
49
+
50
+ $this->UrlToPath($path);
51
+ }
52
+ }
53
+
54
+ private function getFS()
55
+ {
56
+ return new \EnableMediaReplace\FileSystem\Controller\FileSystemController();
57
+ }
58
+
59
+ /* Get a string representation of file, the fullpath
60
+ * Note - this might be risky, without processedpath, in cases.
61
+ * @return String Full path processed or unprocessed.
62
+ */
63
+ public function __toString()
64
+ {
65
+ return (string) $this->fullpath;
66
+ }
67
+
68
+ protected function setFileInfo()
69
+ {
70
+ $processed_path = $this->processPath($this->fullpath);
71
+ if ($processed_path !== false)
72
+ $this->fullpath = $processed_path; // set processed path if that went alright
73
+
74
+
75
+ $info = $this->mb_pathinfo($this->fullpath);
76
+ // Todo, maybe replace this with splFileINfo.
77
+ if ($this->is_file()) // only set fileinfo when it's an actual file.
78
+ {
79
+ $this->filename = isset($info['basename']) ? $info['basename'] : null; // filename + extension
80
+ $this->filebase = isset($info['filename']) ? $info['filename'] : null; // only filename
81
+ $this->extension = isset($info['extension']) ? strtolower($info['extension']) : null; // only (last) extension
82
+ }
83
+
84
+ }
85
+
86
+ /** Call when file status changed, so writable / readable / exists are not reliable anymore */
87
+ public function resetStatus()
88
+ {
89
+ $this->is_writable = null;
90
+ $this->is_readable = null;
91
+ $this->is_file = null;
92
+ $this->exists = null;
93
+ $this->is_virtual = null;
94
+ }
95
+
96
+ public function exists()
97
+ {
98
+ if (is_null($this->exists))
99
+ {
100
+ $this->exists = (@file_exists($this->fullpath) && is_file($this->fullpath));
101
+ }
102
+
103
+ $this->exists = apply_filters('shortpixel_image_exists', $this->exists, $this->fullpath, $this); //legacy
104
+ $this->exists = apply_filters('shortpixel/file/exists', $this->exists, $this->fullpath, $this);
105
+ return $this->exists;
106
+ }
107
+
108
+ public function is_writable()
109
+ {
110
+ if ($this->is_virtual())
111
+ {
112
+ $this->is_writable = false; // can't write to remote files
113
+ }
114
+ elseif (is_null($this->is_writable))
115
+ {
116
+ if ($this->exists())
117
+ {
118
+ $this->is_writable = @is_writable($this->fullpath);
119
+ }
120
+ else // quite expensive check to see if file is writable.
121
+ {
122
+ $res = $this->create();
123
+ $this->delete();
124
+ $this->is_writable = $res;
125
+ }
126
+
127
+ }
128
+
129
+ return $this->is_writable;
130
+ }
131
+
132
+ public function is_readable()
133
+ {
134
+ if (is_null($this->is_readable))
135
+ $this->is_readable = @is_readable($this->fullpath);
136
+
137
+ return $this->is_readable;
138
+ }
139
+
140
+ // A file is virtual when the file is remote with URL and no local alternative is present.
141
+ public function is_virtual()
142
+ {
143
+ if ( is_null($this->is_virtual))
144
+ $this->is_virtual = false; // return bool
145
+ return $this->is_virtual;
146
+ }
147
+
148
+ /* Function checks if path is actually a file. This can be used to check possible confusion if a directory path is given to filemodel */
149
+ public function is_file()
150
+ {
151
+ if ($this->is_virtual()) // don't look further when virtual
152
+ {
153
+ $this->is_file = true;
154
+ return $this->is_file;
155
+ }
156
+ elseif (is_null($this->is_file))
157
+ {
158
+ if ($this->exists())
159
+ {
160
+ if (basename($this->fullpath) == '..' || basename($this->fullpath) == '.')
161
+ $this->is_file = false;
162
+ else
163
+ $this->is_file = is_file($this->fullpath);
164
+ }
165
+ else // file can not exist, but still have a valid filepath format. In that case, if file should return true.
166
+ {
167
+
168
+ /* if file does not exist on disk, anything can become a file ( with/ without extension, etc). Meaning everything non-existing is a potential file ( or directory ) until created. */
169
+
170
+ if (basename($this->fullpath) == '..' || basename($this->fullpath) == '.') // don't see this as file.
171
+ {
172
+ $this->is_file = false;
173
+ }
174
+ else if (! file_exists($this->fullpath) && ! is_dir($this->fullpath))
175
+ {
176
+ $this->is_file = true;
177
+ }
178
+ else //if (! is_file($this->fullpath)) // can be a non-existing directory. /
179
+ {
180
+ $this->is_file = false;
181
+ }
182
+
183
+ }
184
+ }
185
+ return $this->is_file;
186
+ }
187
+
188
+ public function getModified()
189
+ {
190
+ return filemtime($this->fullpath);
191
+ }
192
+
193
+ public function hasBackup()
194
+ {
195
+ $directory = $this->getBackupDirectory();
196
+ if (! $directory)
197
+ return false;
198
+
199
+ $backupFile = $directory . $this->getFileName();
200
+
201
+ if (file_exists($backupFile) && ! is_dir($backupFile) )
202
+ return true;
203
+ else {
204
+ return false;
205
+ }
206
+ }
207
+
208
+
209
+ /** Returns the Directory Model this file resides in
210
+ *
211
+ * @return DirectoryModel Directorymodel Object
212
+ */
213
+ public function getFileDir()
214
+ {
215
+ $fullpath = $this->getFullPath(); // triggers a file lookup if needed.
216
+ // create this only when needed.
217
+ if (is_null($this->directory) && strlen($fullpath) > 0)
218
+ {
219
+ // Feed to full path to DirectoryModel since it checks if input is file, or dir. Using dirname here would cause errors when fullpath is already just a dirpath ( faulty input )
220
+ $this->directory = new DirectoryModel($fullpath);
221
+ }
222
+
223
+ return $this->directory;
224
+ }
225
+
226
+ public function getFileSize()
227
+ {
228
+ if ($this->exists())
229
+ return filesize($this->fullpath);
230
+ else
231
+ return 0;
232
+ }
233
+
234
+ // Creates an empty file
235
+ public function create()
236
+ {
237
+ if (! $this->exists() )
238
+ {
239
+ $fileDir = $this->getFileDir();
240
+
241
+ if (! is_null($fileDir) && $fileDir->exists())
242
+ {
243
+ $res = @touch($this->fullpath);
244
+ $this->exists = $res;
245
+ return $res;
246
+ }
247
+ }
248
+ else
249
+ Log::addWarn('Could not create/write file: ' . $this->fullpath);
250
+
251
+ return false;
252
+ }
253
+
254
+ public function append($message)
255
+ {
256
+ if (! $this->exists() )
257
+ $this->create();
258
+
259
+ if (! $this->is_writable() )
260
+ {
261
+ Log::addWarn('File append failed on ' . $this->getFullPath() . ' - not writable');
262
+ return false;
263
+ }
264
+ $handle = fopen($this->getFullPath(), 'a');
265
+ fwrite($handle, $message);
266
+ fclose($handle);
267
+
268
+ return true;
269
+ }
270
+
271
+
272
+ /** Copy a file to somewhere
273
+ *
274
+ * @param $destination String Full Path to new file.
275
+ */
276
+ public function copy(FileModel $destination)
277
+ {
278
+ $sourcePath = $this->getFullPath();
279
+ $destinationPath = $destination->getFullPath();
280
+ Log::addDebug("Copy from $sourcePath to $destinationPath ");
281
+
282
+ if (! strlen($sourcePath) > 0 || ! strlen($destinationPath) > 0)
283
+ {
284
+ Log::addWarn('Attempted Copy on Empty Path', array($sourcePath, $destinationPath));
285
+ return false;
286
+ }
287
+
288
+ if (! $this->exists())
289
+ {
290
+ Log::addWarn('Tried to copy non-existing file - ' . $sourcePath);
291
+ return false;
292
+ }
293
+
294
+ $is_new = ($destination->exists()) ? false : true;
295
+ $status = @copy($sourcePath, $destinationPath);
296
+
297
+ if (! $status)
298
+ {
299
+ Log::addWarn('Could not copy file ' . $sourcePath . ' to' . $destinationPath);
300
+ }
301
+ else
302
+ {
303
+ $destination->resetStatus();
304
+ $destination->setFileInfo(); // refresh info.
305
+ }
306
+ //
307
+ do_action('shortpixel/filesystem/addfile', array($destinationPath, $destination, $this, $is_new));
308
+ return $status;
309
+ }
310
+
311
+ /** Move a file to somewhere
312
+ * This uses copy and delete functions and will fail if any of those fail.
313
+ * @param $destination String Full Path to new file.
314
+ */
315
+ public function move(FileModel $destination)
316
+ {
317
+ $result = false;
318
+ if ($this->copy($destination))
319
+ {
320
+ $result = $this->delete();
321
+ if ($result == false)
322
+ {
323
+ Log::addError('Move can\'t remove file ' . $this->getFullPath());
324
+ }
325
+
326
+ $this->resetStatus();
327
+ $destination->resetStatus();
328
+ }
329
+ return $result;
330
+ }
331
+
332
+ /** Deletes current file
333
+ * This uses the WP function since it has a filter that might be useful
334
+ */
335
+ public function delete()
336
+ {
337
+ if ($this->exists())
338
+ {
339
+ \wp_delete_file($this->fullpath); // delete file hook via wp_delete_file
340
+ }
341
+ else
342
+ {
343
+ Log::addWarn('Trying to remove non-existing file: ' . $this->getFullPath());
344
+ }
345
+
346
+ if (! file_exists($this->fullpath))
347
+ {
348
+ $this->resetStatus();
349
+ return true;
350
+ }
351
+ else {
352
+ $writable = ($this->is_writable()) ? 'true' : 'false';
353
+ Log::addWarn('File seems not removed - ' . $this->getFullPath() . ' (writable:' . $writable . ')');
354
+ return false;
355
+ }
356
+
357
+ }
358
+
359
+ public function getContents()
360
+ {
361
+ return file_get_contents($this->getFullPath());
362
+ }
363
+
364
+ public function getFullPath()
365
+ {
366
+ // filename here since fullpath is set unchecked in constructor, but might be a different take
367
+ if (is_null($this->filename))
368
+ {
369
+ $this->setFileInfo();
370
+ }
371
+
372
+ return $this->fullpath;
373
+ }
374
+
375
+ // Testing this. Principle is that when the plugin is absolutely sure this is a file, not something remote, not something non-existing, get the fullpath without any check.
376
+ // This function should *only* be used when processing mega amounts of files while not doing optimization or any processing.
377
+ // So far, testing use for file Filter */
378
+ public function getRawFullPath()
379
+ {
380
+ return $this->fullpath;
381
+ }
382
+
383
+ public function getFileName()
384
+ {
385
+ if (is_null($this->filename))
386
+ $this->setFileInfo();
387
+
388
+ return $this->filename;
389
+ }
390
+
391
+ public function getFileBase()
392
+ {
393
+ if (is_null($this->filebase))
394
+ $this->setFileInfo();
395
+
396
+ return $this->filebase;
397
+ }
398
+
399
+
400
+ public function getExtension()
401
+ {
402
+ if (is_null($this->extension))
403
+ $this->setFileInfo();
404
+
405
+ return $this->extension;
406
+ }
407
+
408
+ public function getMime()
409
+ {
410
+ if (is_null($this->mime))
411
+ $this->setFileInfo();
412
+
413
+ if ($this->exists() && ! $this->is_virtual() )
414
+ {
415
+ $this->mime = wp_get_image_mime($this->fullpath);
416
+ }
417
+ else
418
+ $this->mime = false;
419
+
420
+ return $this->mime;
421
+ }
422
+
423
+
424
+ /* Internal function to check if path is a real path
425
+ * - Test for URL's based on http / https
426
+ * - Test if given path is absolute, from the filesystem root.
427
+ * @param $path String The file path
428
+ * @param String The Fixed filepath.
429
+ */
430
+ protected function processPath($path)
431
+ {
432
+ $original_path = $path;
433
+ $fs = $this->getFS();
434
+
435
+ if ($fs->pathIsUrl($path))
436
+ {
437
+ $path = $this->UrlToPath($path);
438
+ }
439
+
440
+ if ($path === false) // don't process further
441
+ return false;
442
+
443
+ $path = wp_normalize_path($path);
444
+ $abspath = $fs->getWPAbsPath();
445
+
446
+ if ( is_file($path) && ! is_dir($path) ) // if path and file exist, all should be okish.
447
+ {
448
+ return $path;
449
+ }
450
+ // If attempted file does not exist, but the file is in a dir that exists, that is good enough.
451
+ elseif ( ! is_dir($path) && is_dir(dirname($path)) )
452
+ {
453
+ return $path;
454
+ }
455
+ // If path is not in the abspath, it might be relative.
456
+ elseif (strpos($path, $abspath->getPath()) === false)
457
+ {
458
+ // if path does not contain basepath.
459
+ //$uploadDir = $fs->getWPUploadBase();
460
+ //$abspath = $fs->getWPAbsPath();
461
+
462
+ $path = $this->relativeToFullPath($path);
463
+ }
464
+ $path = apply_filters('shortpixel/filesystem/processFilePath', $path, $original_path);
465
+ /* This needs some check here on malformed path's, but can't be test for existing since that's not a requirement.
466
+ if (file_exists($path) === false) // failed to process path to something workable.
467
+ {
468
+ // Log::addInfo('Failed to process path', array($path));
469
+ $path = false;
470
+ } */
471
+
472
+ return $path;
473
+ }
474
+
475
+
476
+
477
+ /** Resolve an URL to a local path
478
+ * This partially comes from WordPress functions attempting the same
479
+ * @param String $url The URL to resolve
480
+ * @return String/Boolean - False is this seems an external domain, otherwise resolved path.
481
+ */
482
+ private function UrlToPath($url)
483
+ {
484
+ //$uploadDir = wp_upload_dir();
485
+
486
+ $site_url = str_replace('http:', '', home_url('', 'http'));
487
+ $url = str_replace(array('http:', 'https:'), '', $url);
488
+ $fs = $this->getFS();
489
+
490
+ if (strpos($url, $site_url) !== false)
491
+ {
492
+ // try to replace URL for Path
493
+ $abspath = $this->getFS()->getWPAbsPath();
494
+ $path = str_replace($site_url, rtrim($abspath->getPath(),'/'), $url);
495
+
496
+
497
+ if (! $fs->pathIsUrl($path)) // test again.
498
+ {
499
+ return $path;
500
+ }
501
+ }
502
+
503
+ $this->is_virtual = true;
504
+
505
+ // This filter checks if some supplier will be able to handle the file when needed.
506
+ $path = apply_filters('shortpixel/image/urltopath', false, $url);
507
+
508
+ if ($path !== false)
509
+ {
510
+ $this->exists = true;
511
+ $this->is_readable = true;
512
+ $this->is_file = true;
513
+ }
514
+ else
515
+ {
516
+ $this->exists = false;
517
+ $this->is_readable = false;
518
+ $this->is_file = false;
519
+ }
520
+
521
+
522
+ return false; // seems URL from other server, use virtual mode.
523
+ }
524
+
525
+ /** Tries to find the full path for a perceived relative path.
526
+ *
527
+ * Relative path is detected on basis of WordPress ABSPATH. If this doesn't appear in the file path, it might be a relative path.
528
+ * Function checks for expections on this rule ( tmp path ) and returns modified - or not - path.
529
+ * @param $path The path for the file_exists
530
+ * @returns String The updated path, if that was possible.
531
+ */
532
+ private function relativeToFullPath($path)
533
+ {
534
+ $originalPath = $path; // for safe-keeping
535
+
536
+ // A file with no path, can never be created to a fullpath.
537
+ if (strlen($path) == 0)
538
+ return $path;
539
+
540
+ // if the file plainly exists, it's usable /**
541
+ if (file_exists($path))
542
+ {
543
+ return $path;
544
+ }
545
+
546
+ // Test if our 'relative' path is not a path to /tmp directory.
547
+
548
+ // This ini value might not exist.
549
+ $tempdirini = ini_get('upload_tmp_dir');
550
+ if ( (strlen($tempdirini) > 0) && strpos($path, $tempdirini) !== false)
551
+ return $path;
552
+
553
+ $tempdir = sys_get_temp_dir();
554
+ if ( (strlen($tempdir) > 0) && strpos($path, $tempdir) !== false)
555
+ return $path;
556
+
557
+ // Path contains upload basedir. This happens when upload dir is outside of usual WP.
558
+ $fs = $this->getFS();
559
+ $uploadDir = $fs->getWPUploadBase();
560
+ $abspath = $fs->getWPAbsPath();
561
+
562
+ if (strpos($path, $uploadDir->getPath()) !== false) // If upload Dir is feature in path, consider it ok.
563
+ {
564
+ return $path;
565
+ }
566
+ elseif (file_exists($abspath->getPath() . $path)) // If upload dir is abspath plus return path. Exceptions.
567
+ {
568
+ return $abspath->getPath() . $path;
569
+ }
570
+ elseif(file_exists($uploadDir->getPath() . $path)) // This happens when upload_dir is not properly prepended in get_attachment_file due to WP errors
571
+ {
572
+ return $uploadDir->getPath() . $path;
573
+ }
574
+
575
+ // this is probably a bit of a sharp corner to take.
576
+ // if path starts with / remove it due to trailingslashing ABSPATH
577
+ $path = ltrim($path, '/');
578
+ $fullpath = $abspath->getPath() . $path;
579
+
580
+ // We can't test for file_exists here, since file_model allows non-existing files.
581
+ // Test if directory exists, perhaps. Otherwise we are in for a failure anyhow.
582
+ //if (is_dir(dirname($fullpath)))
583
+ return $fullpath;
584
+ //else
585
+ // return $originalPath;
586
+ }
587
+
588
+ public function getPermissions()
589
+ {
590
+ if (is_null($this->permissions))
591
+ $this->permissions = fileperms($this->fullpath) & 0777;
592
+
593
+ return $this->permissions;
594
+ }
595
+
596
+ // @tozo Lazy IMplementation / copy, should be brought in line w/ other attributes.
597
+ public function setPermissions($permissions)
598
+ {
599
+ @chmod($this->fullpath, $permissions);
600
+ }
601
+
602
+
603
+ /** Fix for multibyte pathnames and pathinfo which doesn't take into regard the locale.
604
+ * This snippet taken from PHPMailer.
605
+ */
606
+ private function mb_pathinfo($path, $options = null)
607
+ {
608
+ $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
609
+ $pathinfo = [];
610
+ if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) {
611
+ if (array_key_exists(1, $pathinfo)) {
612
+ $ret['dirname'] = $pathinfo[1];
613
+ }
614
+ if (array_key_exists(2, $pathinfo)) {
615
+ $ret['basename'] = $pathinfo[2];
616
+ }
617
+ if (array_key_exists(5, $pathinfo)) {
618
+ $ret['extension'] = $pathinfo[5];
619
+ }
620
+ if (array_key_exists(3, $pathinfo)) {
621
+ $ret['filename'] = $pathinfo[3];
622
+ }
623
+ }
624
+ switch ($options) {
625
+ case PATHINFO_DIRNAME:
626
+ case 'dirname':
627
+ return $ret['dirname'];
628
+ case PATHINFO_BASENAME:
629
+ case 'basename':
630
+ return $ret['basename'];
631
+ case PATHINFO_EXTENSION:
632
+ case 'extension':
633
+ return $ret['extension'];
634
+ case PATHINFO_FILENAME:
635
+ case 'filename':
636
+ return $ret['filename'];
637
+ default:
638
+ return $ret;
639
+ }
640
+ }
641
+
642
+ public function __debuginfo()
643
+ {
644
+ return [
645
+ 'fullpath' => $this->fullpath,
646
+ 'filename' => $this->filename,
647
+ 'filebase' => $this->filebase,
648
+ 'exists' => $this->exists,
649
+ 'is_writable' => $this->is_writable,
650
+ 'is_readable' => $this->is_readable,
651
+ 'is_virtual' => $this->is_virtual,
652
+ ];
653
+ }
654
+
655
+
656
+ } // FileModel Class
build/shortpixel/log/src/ShortPixelLogger.php CHANGED
@@ -24,6 +24,8 @@ namespace EnableMediaReplace\ShortPixelLogger;
24
  protected $format_data = "\t %%data%% ";
25
 
26
  protected $hooks = array();
 
 
27
  /* protected $hooks = array(
28
  'shortpixel_image_exists' => array('numargs' => 3),
29
  'shortpixel_webp_image_base' => array('numargs' => 2),
@@ -49,16 +51,19 @@ namespace EnableMediaReplace\ShortPixelLogger;
49
  $ns = __NAMESPACE__;
50
  $this->namespace = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
51
 
 
52
  if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) // manual takes precedence over constants
53
  {
54
  $this->is_manual_request = true;
55
  $this->is_active = true;
56
 
 
57
  if ($_REQUEST['SHORTPIXEL_DEBUG'] === 'true')
58
  {
59
  $this->logLevel = DebugItem::LEVEL_INFO;
60
  }
61
  else {
 
62
  $this->logLevel = intval($_REQUEST['SHORTPIXEL_DEBUG']);
63
  }
64
 
@@ -75,8 +80,6 @@ namespace EnableMediaReplace\ShortPixelLogger;
75
 
76
  if (defined('SHORTPIXEL_DEBUG_TARGET') && SHORTPIXEL_DEBUG_TARGET || $this->is_manual_request)
77
  {
78
- //$this->logPath = SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log";
79
- //$this->logMode = defined('SHORTPIXEL_LOG_OVERWRITE') ? 0 : FILE_APPEND;
80
  if (defined('SHORTPIXEL_LOG_OVERWRITE')) // if overwrite, do this on init once.
81
  file_put_contents($this->logPath,'-- Log Reset -- ' .PHP_EOL);
82
 
@@ -127,6 +130,7 @@ namespace EnableMediaReplace\ShortPixelLogger;
127
  public function setLogPath($logPath)
128
  {
129
  $this->logPath = $logPath;
 
130
  }
131
  protected function addLog($message, $level, $data = array())
132
  {
@@ -174,7 +178,7 @@ namespace EnableMediaReplace\ShortPixelLogger;
174
  {
175
  $items = $debugItem->getForFormat();
176
  $items['time_passed'] = round ( ($items['time'] - $this->start_time), 5);
177
- $items['time'] = date('Y-m-d H:i:s', $items['time'] );
178
 
179
  if ( ($items['caller']) && is_array($items['caller']) && count($items['caller']) > 0)
180
  {
@@ -184,16 +188,50 @@ namespace EnableMediaReplace\ShortPixelLogger;
184
 
185
  $line = $this->formatLine($items);
186
 
 
 
187
  // try to write to file. Don't write if directory doesn't exists (leads to notices)
188
- if ($this->logPath && is_dir(dirname($this->logPath)) )
189
  {
190
- file_put_contents($this->logPath,$line, FILE_APPEND);
 
191
  }
192
  else {
193
- error_log($line);
194
  }
195
  }
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  protected function formatLine($args = array() )
198
  {
199
  $line= $this->format;
@@ -344,8 +382,8 @@ namespace EnableMediaReplace\ShortPixelLogger;
344
  include($template_path);
345
  }
346
  else {
347
- self::addError("View $template could not be found in " . $template_path,
348
- array('class' => get_class($this), 'req' => $_REQUEST));
349
  }
350
  }
351
 
24
  protected $format_data = "\t %%data%% ";
25
 
26
  protected $hooks = array();
27
+
28
+ private $logFile; // pointer resource to the logFile.
29
  /* protected $hooks = array(
30
  'shortpixel_image_exists' => array('numargs' => 3),
31
  'shortpixel_webp_image_base' => array('numargs' => 2),
51
  $ns = __NAMESPACE__;
52
  $this->namespace = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
53
 
54
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is not a form
55
  if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) // manual takes precedence over constants
56
  {
57
  $this->is_manual_request = true;
58
  $this->is_active = true;
59
 
60
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is not a form
61
  if ($_REQUEST['SHORTPIXEL_DEBUG'] === 'true')
62
  {
63
  $this->logLevel = DebugItem::LEVEL_INFO;
64
  }
65
  else {
66
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is not a form
67
  $this->logLevel = intval($_REQUEST['SHORTPIXEL_DEBUG']);
68
  }
69
 
80
 
81
  if (defined('SHORTPIXEL_DEBUG_TARGET') && SHORTPIXEL_DEBUG_TARGET || $this->is_manual_request)
82
  {
 
 
83
  if (defined('SHORTPIXEL_LOG_OVERWRITE')) // if overwrite, do this on init once.
84
  file_put_contents($this->logPath,'-- Log Reset -- ' .PHP_EOL);
85
 
130
  public function setLogPath($logPath)
131
  {
132
  $this->logPath = $logPath;
133
+ $this->getWriteFile(true); // reset the writeFile here.
134
  }
135
  protected function addLog($message, $level, $data = array())
136
  {
178
  {
179
  $items = $debugItem->getForFormat();
180
  $items['time_passed'] = round ( ($items['time'] - $this->start_time), 5);
181
+ $items['time'] = date('Y-m-d H:i:s', (int) $items['time'] );
182
 
183
  if ( ($items['caller']) && is_array($items['caller']) && count($items['caller']) > 0)
184
  {
188
 
189
  $line = $this->formatLine($items);
190
 
191
+ $file = $this->getWriteFile();
192
+
193
  // try to write to file. Don't write if directory doesn't exists (leads to notices)
194
+ if ($file )
195
  {
196
+ fwrite($file, $line);
197
+ // file_put_contents($this->logPath,$line, FILE_APPEND);
198
  }
199
  else {
200
+ // error_log($line);
201
  }
202
  }
203
 
204
+ protected function getWriteFile($reset = false)
205
+ {
206
+ if (! is_null($this->logFile) && $reset === false)
207
+ {
208
+ return $this->logFile;
209
+ }
210
+ elseif(is_object($this->logFile))
211
+ {
212
+ fclose($this->logFile);
213
+ }
214
+
215
+ $logDir = dirname($this->logPath);
216
+ if (! is_dir($logDir) || ! is_writable($logDir))
217
+ {
218
+ error_log('ShortpixelLogger: Log Directory is not writable');
219
+ $this->logFile = false;
220
+ return false;
221
+ }
222
+
223
+ $file = fopen($this->logPath, 'a');
224
+ if ($file === false)
225
+ {
226
+ error_log('ShortpixelLogger: File could not be opened / created: ' . $this->logPath);
227
+ $this->logFile = false;
228
+ return $file;
229
+ }
230
+
231
+ $this->logFile = $file;
232
+ return $file;
233
+ }
234
+
235
  protected function formatLine($args = array() )
236
  {
237
  $line= $this->format;
382
  include($template_path);
383
  }
384
  else {
385
+ self::addError("View $template for ShortPixelLogger could not be found in " . $template_path,
386
+ array('class' => get_class($this)));
387
  }
388
  }
389
 
build/shortpixel/log/src/view-debug-box.php CHANGED
@@ -49,8 +49,8 @@ wp_enqueue_script( 'jquery-ui-draggable' );
49
 
50
 
51
  <div class='sp_debug_box'>
52
- <div class='header'><?php echo $view->namespace ?> Debug Box </div>
53
- <a target="_blank" href='<?php echo $view->logLink ?>'>Logfile</a>
54
  <div class='content_box'>
55
 
56
  </div>
49
 
50
 
51
  <div class='sp_debug_box'>
52
+ <div class='header'><?php echo esc_html($view->namespace) ?> Debug Box </div>
53
+ <a target="_blank" href='<?php echo esc_url($view->logLink) ?>'>Logfile</a>
54
  <div class='content_box'>
55
 
56
  </div>
build/shortpixel/notices/src/NoticeController.php CHANGED
@@ -108,7 +108,7 @@ class NoticeController //extends ShortPixelController
108
  }
109
  self::$notices[] = $notice;
110
  $this->countNotices();
111
- Log::addDebug('Adding notice - ', $notice);
112
  $this->update();
113
  return $notice;
114
  }
@@ -212,14 +212,23 @@ class NoticeController //extends ShortPixelController
212
  {
213
  $response = array('result' => false, 'reason' => '');
214
 
215
- if ( wp_verify_nonce( $_POST['nonce'], 'dismiss') )
216
  {
217
- if ($_POST['plugin_action'] == 'dismiss')
218
  {
219
- $id = sanitize_text_field($_POST['id']);
220
- $notice = $this->getNoticeByID($id);
221
-
222
- if($notice)
 
 
 
 
 
 
 
 
 
223
  {
224
  $notice->dismiss();
225
  $this->update();
@@ -228,7 +237,8 @@ class NoticeController //extends ShortPixelController
228
  else
229
  {
230
  Log::addError('Notice not found when dismissing -> ' . $id, self::$notices);
231
- $response['result'] = ' Notice ' . $id . ' not found. ';
 
232
  }
233
 
234
  }
@@ -349,7 +359,7 @@ class NoticeController //extends ShortPixelController
349
  {
350
  if (file_exists(__DIR__ . '/css/notices.css'))
351
  {
352
- echo '<style>' . file_get_contents(__DIR__ . '/css/notices.css') . '</style>';
353
  }
354
  else {
355
  Log::addDebug('Notices : css/notices.css could not be loaded');
108
  }
109
  self::$notices[] = $notice;
110
  $this->countNotices();
111
+
112
  $this->update();
113
  return $notice;
114
  }
212
  {
213
  $response = array('result' => false, 'reason' => '');
214
 
215
+ if (isset($_POST['nonce']) && wp_verify_nonce( sanitize_key($_POST['nonce']), 'dismiss') )
216
  {
217
+ if (isset($_POST['plugin_action']) && 'dismiss' == $_POST['plugin_action'] )
218
  {
219
+ $id = (isset($_POST['id'])) ? sanitize_text_field( wp_unslash($_POST['id'])) : null;
220
+
221
+ if (! is_null($id))
222
+ {
223
+
224
+ $notice = $this->getNoticeByID($id);
225
+ }
226
+ else
227
+ {
228
+ $notice = false;
229
+ }
230
+
231
+ if(false !== $notice)
232
  {
233
  $notice->dismiss();
234
  $this->update();
237
  else
238
  {
239
  Log::addError('Notice not found when dismissing -> ' . $id, self::$notices);
240
+ $response['result'] = false;
241
+ $response['reason'] = ' Notice ' . $id . ' not found. ';
242
  }
243
 
244
  }
359
  {
360
  if (file_exists(__DIR__ . '/css/notices.css'))
361
  {
362
+ echo '<style>' . esc_html(file_get_contents(__DIR__ . '/css/notices.css')) . '</style>';
363
  }
364
  else {
365
  Log::addDebug('Notices : css/notices.css could not be loaded');
build/shortpixel/notices/src/NoticeModel.php CHANGED
@@ -21,6 +21,8 @@ class NoticeModel //extends ShortPixelModel
21
 
22
  public static $icons = array();
23
 
 
 
24
  const NOTICE_NORMAL = 1;
25
  const NOTICE_ERROR = 2;
26
  const NOTICE_SUCCESS = 3;
@@ -142,7 +144,7 @@ class NoticeModel //extends ShortPixelModel
142
  public function getForDisplay()
143
  {
144
  $this->viewed = true;
145
- $class = 'shortpixel notice ';
146
 
147
  $icon = '';
148
 
@@ -217,10 +219,31 @@ class NoticeModel //extends ShortPixelModel
217
  $output .= "<div class='detail-content-wrapper'><p class='detail-content'>" . $this->parseDetails() . "</p></div>";
218
  $output .= '<label for="check-' . $id . '" class="hide-details"><span>' . __('Hide Details', 'shortpixel-image-optimiser') . '</span></label>';
219
 
220
- $output .= '</div>'; // detail rapper
 
 
 
221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  }
223
- $output .= "</span></div>";
 
224
 
225
  if ($this->is_persistent && $this->is_removable)
226
  {
@@ -245,20 +268,37 @@ class NoticeModel //extends ShortPixelModel
245
 
246
  private function getDismissJS()
247
  {
248
- $url = wp_json_encode(admin_url('admin-ajax.php'));
249
- // $action = 'dismiss';
250
- $nonce = wp_create_nonce('dismiss');
251
 
252
- $data = wp_json_encode(array('action' => $this->notice_action, 'plugin_action' => 'dismiss', 'nonce' => $nonce, 'id' => $this->id, 'time' => $this->suppress_period));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
- // $data_string = "{action:'$this->notice_action'}";
255
 
256
- $js = "jQuery(document).on('click','#$this->id button.notice-dismiss',
257
- function() {
258
- var data = $data;
259
- var url = $url;
260
- jQuery.post(url, data); }
261
- );";
262
  return "\n jQuery(document).ready(function(){ \n" . $js . "\n});";
263
  }
264
 
21
 
22
  public static $icons = array();
23
 
24
+ private static $jsDismissLoaded;
25
+
26
  const NOTICE_NORMAL = 1;
27
  const NOTICE_ERROR = 2;
28
  const NOTICE_SUCCESS = 3;
144
  public function getForDisplay()
145
  {
146
  $this->viewed = true;
147
+ $class = 'shortpixel shortpixel-notice ';
148
 
149
  $icon = '';
150
 
219
  $output .= "<div class='detail-content-wrapper'><p class='detail-content'>" . $this->parseDetails() . "</p></div>";
220
  $output .= '<label for="check-' . $id . '" class="hide-details"><span>' . __('Hide Details', 'shortpixel-image-optimiser') . '</span></label>';
221
 
222
+ $output .= '</div>'; // detail wrapper
223
+
224
+ }
225
+ $output .= "</span>";
226
 
227
+ if ($this->is_removable)
228
+ {
229
+ $output .= '<button type="button" id="button-' . $id . '" class="notice-dismiss" data-dismiss="' . $this->suppress_period . '" ><span class="screen-reader-text">' . __('Dismiss this notice', 'shortpixel-image-optimiser') . '</span></button>';
230
+
231
+ if (! $this->is_persistent)
232
+ {
233
+ $output .= "<script type='text/javascript'>\n
234
+ document.getElementById('button-$id').onclick = function()
235
+ {
236
+ var el = document.getElementById('$id');
237
+ $(el).fadeTo(100,0,function() {
238
+ $(el).slideUp(100, 0, function () {
239
+ $(el).remove();
240
+ })
241
+ });
242
+ } </script>";
243
+ }
244
  }
245
+
246
+ $output .= "</div>";
247
 
248
  if ($this->is_persistent && $this->is_removable)
249
  {
268
 
269
  private function getDismissJS()
270
  {
 
 
 
271
 
272
+ $js = '';
273
+ if (is_null(self::$jsDismissLoaded))
274
+ {
275
+ $nonce = wp_create_nonce('dismiss');
276
+ $url = wp_json_encode(admin_url('admin-ajax.php'));
277
+ $js = "function shortpixel_notice_dismiss(event) {
278
+ event.preventDefault();
279
+ var ev = event.detail;
280
+ var target = event.target;
281
+ var parent = target.parentElement;
282
+
283
+ var data = {
284
+ 'plugin_action': 'dismiss',
285
+ 'action' : '$this->notice_action',
286
+ 'nonce' : '$nonce',
287
+ }
288
+ data.time = target.getAttribute('data-dismiss');
289
+ data.id = parent.getAttribute('id');
290
+ jQuery.post($url,data);
291
+
292
+ jQuery(parent).fadeTo(100,0,function() {
293
+ jQuery(parent).slideUp(100, 0, function () {
294
+ jQuery(parent).remove();
295
+ })
296
+ });
297
+ }";
298
+ }
299
 
300
+ $js .= ' jQuery("#' . $this->id . '").find(".notice-dismiss").on("click", shortpixel_notice_dismiss); ';
301
 
 
 
 
 
 
 
302
  return "\n jQuery(document).ready(function(){ \n" . $js . "\n});";
303
  }
304
 
build/shortpixel/notices/src/css/notices.css CHANGED
@@ -1,21 +1,48 @@
1
- .shortpixel.notice {
2
- padding: 8px; }
3
- .shortpixel.notice img {
4
- display: inline-block;
5
- margin: 0 25px 0 0;
6
- max-height: 50px; }
7
- .shortpixel.notice .notice-dismiss {
8
- margin-top: 10px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  /* In-view notice ( not on top, between the options ) - styled after WP notice */
11
  .view-notice {
12
  box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
13
  border: 4px solid #fff;
14
- padding: 1px 12px; }
15
- .view-notice p {
16
- margin: 1em 0 !important; }
17
- .view-notice.warning {
18
- border-left-color: #ffb900; }
 
 
 
19
 
20
  .view-notice-row {
21
- display: none; }
 
 
 
1
+ .shortpixel.shortpixel-notice {
2
+ padding: 8px;
3
+ background: #fff;
4
+ border: 1px solid #c3c4c7;
5
+ border-left-width: 4px;
6
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
7
+ margin: 5px 15px 2px;
8
+ position: relative;
9
+ }
10
+ .shortpixel.shortpixel-notice img {
11
+ display: inline-block;
12
+ margin: 0 25px 0 0;
13
+ max-height: 50px;
14
+ }
15
+ .shortpixel.shortpixel-notice .notice-dismiss {
16
+ margin-top: 6px;
17
+ }
18
+ .shortpixel.shortpixel-notice.notice-success {
19
+ border-left-color: #00a32a;
20
+ }
21
+ .shortpixel.shortpixel-notice.notice-warning {
22
+ border-left-color: #dba617;
23
+ }
24
+ .shortpixel.shortpixel-notice.notice-error {
25
+ border-left-color: #ff0000;
26
+ }
27
+ .shortpixel.shortpixel-notice.notice-info {
28
+ border-left-color: #72aee6;
29
+ }
30
 
31
  /* In-view notice ( not on top, between the options ) - styled after WP notice */
32
  .view-notice {
33
  box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
34
  border: 4px solid #fff;
35
+ padding: 1px 12px;
36
+ }
37
+ .view-notice p {
38
+ margin: 1em 0 !important;
39
+ }
40
+ .view-notice.warning {
41
+ border-left-color: #ffb900;
42
+ }
43
 
44
  .view-notice-row {
45
+ display: none;
46
+ }
47
+
48
+ /*# sourceMappingURL=notices.css.map */
build/shortpixel/notices/src/css/notices.css.map ADDED
@@ -0,0 +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"}
build/shortpixel/notices/src/css/notices.scss CHANGED
@@ -1,9 +1,16 @@
1
 
2
- .shortpixel.notice
3
  {
4
  //padding: 18px;
5
  //min-height: 50px;
6
  padding: 8px;
 
 
 
 
 
 
 
7
  img
8
  {
9
  display:inline-block;
@@ -12,8 +19,25 @@
12
  }
13
  .notice-dismiss
14
  {
15
- margin-top: 10px;
16
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  }
18
 
19
  /* In-view notice ( not on top, between the options ) - styled after WP notice */
1
 
2
+ .shortpixel.shortpixel-notice
3
  {
4
  //padding: 18px;
5
  //min-height: 50px;
6
  padding: 8px;
7
+ background: #fff;
8
+ border: 1px solid #c3c4c7;
9
+ border-left-width: 4px;
10
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
11
+ margin: 5px 15px 2px;
12
+ position: relative;
13
+
14
  img
15
  {
16
  display:inline-block;
19
  }
20
  .notice-dismiss
21
  {
22
+ margin-top: 6px;
23
  }
24
+
25
+ &.notice-success
26
+ {
27
+ border-left-color: #00a32a;
28
+ }
29
+ &.notice-warning
30
+ {
31
+ border-left-color: #dba617;
32
+ }
33
+ &.notice-error
34
+ {
35
+ border-left-color: #ff0000;
36
+ }
37
+ &.notice-info
38
+ {
39
+ border-left-color: #72aee6;
40
+ }
41
  }
42
 
43
  /* In-view notice ( not on top, between the options ) - styled after WP notice */
classes/ajax.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace;
3
+
4
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
+ use EnableMediaReplace\Api as Api;
6
+
7
+ class Ajax {
8
+ public function __construct() {
9
+ $endpoints = array(
10
+ 'remove_background',
11
+
12
+ );
13
+ foreach ( $endpoints as $action ) {
14
+ add_action( "wp_ajax_emr_{$action}", array( $this, $action ) );
15
+ }
16
+ }
17
+
18
+ public function remove_background() {
19
+ if ( $this->check_nonce() ) {
20
+ $api = new Api;
21
+ $response = $api->request( $_POST );
22
+ wp_send_json($response);
23
+ }
24
+ else {
25
+ die('Wrong nonce');
26
+ }
27
+ }
28
+
29
+ private function check_nonce() {
30
+ $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( $_POST['nonce'] ) : '';
31
+ $action = isset( $_POST['action'] ) ? sanitize_text_field( $_POST['action'] ) : '';
32
+ return wp_verify_nonce( $nonce, $action );
33
+ }
34
+ }
35
+
36
+
37
+ new Ajax();
classes/api.php ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This page contains api class.
4
+ */
5
+ namespace EnableMediaReplace;
6
+
7
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
8
+
9
+ use Exception;
10
+ use stdClass;
11
+ /**
12
+ * This class contains api methods
13
+ */
14
+ class Api {
15
+
16
+ /**
17
+ * Request Counter
18
+ *
19
+ * @var int $counter
20
+ */
21
+ private $counter = 0;
22
+
23
+ /**
24
+ * ShortPixel api url
25
+ *
26
+ * @var string $url
27
+ */
28
+ private $url = 'http://api.shortpixel.com/v2/free-reducer.php';
29
+
30
+ /**
31
+ * ShortPixel api request headers
32
+ *
33
+ * @var array $headers
34
+ */
35
+ private $headers = array(
36
+ 'Content-Type: application/json',
37
+ 'Accept: application/json',
38
+ );
39
+
40
+ private $refresh = true; // only first request should be fresh
41
+
42
+
43
+
44
+ public function __construct()
45
+ {
46
+
47
+ }
48
+ /**
49
+ * Create ShortPixel api request
50
+ *
51
+ * @param array $data
52
+ * @return stdClass $result
53
+ */
54
+ public function request( array $posted_data ) {
55
+ $bg_remove = '1';
56
+ $compression_level = 0; // intval($posted_data['compression_level']); // off for now.
57
+
58
+ $attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : null;
59
+
60
+ if (is_null($attachment_id))
61
+ {
62
+ $result = $this->getResponseObject();
63
+ $result->success = false;
64
+ $result->message = __('No attachment ID given', 'enable-media-replace');
65
+ return $result;
66
+ }
67
+
68
+ $replacer = new Replacer($attachment_id);
69
+ $url = $replacer->getSourceUrl();
70
+
71
+ $settings = get_option('enable_media_replace', array()); // save settings and show last loaded.
72
+ $settings['bg_type'] = isset($_POST['background']['type']) ? sanitize_text_field($_POST['background']['type']) : false;
73
+ $settings['bg_color'] = isset($_POST['background']['color']) ? sanitize_text_field($_POST['background']['color']) : '#ffffff'; // default to white.
74
+ $settings['bg_transparency'] = isset($_POST['background']['transparency']) ? sanitize_text_field($_POST['background']['transparency']) : false;
75
+
76
+ update_option('enable_media_replace', $settings, false);
77
+
78
+
79
+ if ( 'solid' === $posted_data['background']['type'] ) {
80
+ $bg_remove = $posted_data['background']['color'];
81
+
82
+ $transparency = isset($posted_data['background']['transparency']) ? intval($posted_data['background']['transparency']) : -1;
83
+ // if transparancy without acceptable boundaries, add it to color ( as rgba I presume )
84
+ if ($transparency >= 0 && $transparency < 100)
85
+ {
86
+ if ($transparency == 100)
87
+ $transparency = 'FF';
88
+
89
+ // Strpad for lower than 10 should add 09, 08 etc.
90
+ $bg_remove .= str_pad($transparency, 2, '0', STR_PAD_LEFT);
91
+ }
92
+ }
93
+
94
+
95
+
96
+ $data = array(
97
+ 'plugin_version' => EMR_VERSION,
98
+ 'bg_remove' => $bg_remove,
99
+ 'urllist' => array( urlencode( esc_url($url) ) ),
100
+ 'lossy' => $compression_level,
101
+ 'refresh' => $this->refresh,
102
+ );
103
+
104
+ $request = array(
105
+ 'method' => 'POST',
106
+ 'timeout' => 60,
107
+ 'headers' => $this->headers,
108
+ 'body' => json_encode( $data ),
109
+
110
+ );
111
+
112
+ $settingsData = '';
113
+ //unset($settingsData['url']);
114
+
115
+ foreach($data as $key => $val)
116
+ {
117
+ if ($key == 'urllist' || $key == 'refresh')
118
+ {
119
+ continue;
120
+ }
121
+ $settingsData .= " $key:$val ";
122
+ }
123
+
124
+
125
+ //we need to wait a bit until we try to check if the image is ready
126
+ if ($this->counter > 0)
127
+ sleep( $this->counter + 3 );
128
+
129
+ $this->counter++;
130
+
131
+ $result = $this->getResponseObject();
132
+
133
+ if ( $this->counter < 10 ) {
134
+ try {
135
+
136
+ Log::addDebug('Sending request', $request);
137
+ $response = wp_remote_post( $this->url, $request );
138
+
139
+ $this->refresh = false;
140
+
141
+ if ( is_wp_error( $response ) ) {
142
+ $result->message = $response->get_error_message();
143
+ } else {
144
+
145
+ $json = json_decode( $response['body'] );
146
+
147
+ Log::addDebug('Response Json', $json);
148
+ if ( is_array( $json ) && '2' === $json[0]->Status->Code ) {
149
+ $result->success = true;
150
+
151
+ if ( '1' === $compression_level || '2' === $compression_level ) {
152
+ $result->image = $json[0]->LossyURL;
153
+ } else {
154
+ $result->image = $json[0]->LosslessURL;
155
+ }
156
+
157
+ $key = $this->handleSuccess($result);
158
+ $result->key = $key;
159
+ $result->url = $url;
160
+ $result->image = add_query_arg('ts', time(), $result->image);
161
+
162
+ $result->settings = $settingsData;
163
+
164
+ // $this->handleSuccess($result);
165
+ } elseif ( is_array( $json ) && '1' === $json[0]->Status->Code ) {
166
+ return $this->request( $posted_data );
167
+ } else {
168
+ if (is_array($json))
169
+ {
170
+ $result->message = $json[0]->Status->Message;
171
+ }
172
+ elseif (is_object($json) && property_exists($json, 'Status'))
173
+ {
174
+ $result->message = $json->Status->Message;
175
+ }
176
+ }
177
+ }
178
+ } catch ( Exception $e ) {
179
+ $result->message = $e->getMessage();
180
+ }
181
+ } else {
182
+ $result->message = __( 'The background could not be removed in a reasonable amount of time. The file might be too big, or the API could be busy. Please try again later!', 'enable-media-replace' );
183
+ }
184
+
185
+ return $result;
186
+ }
187
+
188
+ public function handleSuccess($result)
189
+ {
190
+ // $fs = emr()->filesystem();
191
+ // $result = $fs->downloadFile($result->image, wp_tempnam($result->image));
192
+ $nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : wp_create_nonce();
193
+ $key = wp_hash($nonce . $result->image, 'logged_in');
194
+
195
+ set_transient('emr_' . $key, $result->image, 30 * MINUTE_IN_SECONDS);
196
+ return $key;
197
+ }
198
+
199
+ public function handleDownload($key)
200
+ {
201
+ $url = get_transient('emr_' . $key);
202
+ $result = $this->getResponseObject();
203
+
204
+ if ($url === false)
205
+ {
206
+ $result->message = __('This file seems not available anymore. Please try again', 'enable-media-replace');
207
+ return $result;
208
+ }
209
+
210
+ $fs = emr()->filesystem();
211
+ $target = wp_tempnam($url);
212
+
213
+ $bool = $fs->downloadFile($url, $target);
214
+
215
+ if ($bool === false)
216
+ {
217
+ $result->message = __('Download failed', 'enable-media-replace');
218
+ }
219
+ else {
220
+ $result->success = true;
221
+ $result->image = $target;
222
+ }
223
+ return $result;
224
+ }
225
+
226
+ protected function getResponseObject()
227
+ {
228
+ $result = new stdClass;
229
+ $result->success = false;
230
+ $result->image = null;
231
+ $result->message = null;
232
+
233
+ return $result;
234
+ }
235
+
236
+ }
classes/cache.php CHANGED
@@ -65,9 +65,9 @@ class emrCache
65
  $this->checkCaches();
66
 
67
  // general WP
68
- if ($post_id > 0)
69
- clean_post_cache($post_id);
70
- else
71
  wp_cache_flush();
72
 
73
  /* Verified working without.
65
  $this->checkCaches();
66
 
67
  // general WP
68
+ //if ($post_id > 0)
69
+ // clean_post_cache($post_id);
70
+ //else
71
  wp_cache_flush();
72
 
73
  /* Verified working without.
classes/emr-plugin.php CHANGED
@@ -1,378 +1,481 @@
1
  <?php
2
  namespace EnableMediaReplace;
 
3
  use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4
  use EnableMediaReplace\Notices\NoticeController as Notices;
 
 
5
 
6
  // Does what a plugin does.
7
  class EnableMediaReplacePlugin
8
  {
9
 
10
- protected $plugin_path;
11
- private static $instance;
12
-
13
- private $user_cap = false;
14
- private $general_cap = false;
15
-
16
- public function __construct()
17
- {
18
- add_action('plugins_loaded', array($this, 'runtime'));
19
- }
20
 
21
- public function runtime()
22
- {
23
- $this->nopriv_plugin_actions();
24
 
25
- if (EMR_CAPABILITY !== false)
26
- {
27
- if (is_array(EMR_CAPABILITY))
28
- {
29
- $this->general_cap = EMR_CAPABILITY[0];
30
- $this->user_cap = EMR_CAPABILITY[1];
31
 
32
- if (! current_user_can($this->general_cap) && ! current_user_can($this->user_cap))
33
- return;
34
- }
35
- else
36
- {
37
- $this->general_cap = EMR_CAPABILITY;
38
- if (! current_user_can($this->general_cap))
 
 
 
 
 
 
 
 
 
 
 
 
39
  return;
40
  }
41
- }
42
- elseif (! current_user_can('upload_files'))
43
- return;
44
 
45
- $this->plugin_actions(); // init
46
- }
47
-
48
- public static function get()
49
- {
50
- if (is_null(self::$instance))
51
- self::$instance = new EnableMediaReplacePlugin();
52
-
53
- $log = Log::getInstance();
54
- if (Log::debugIsActive())
55
- {
56
- $uploaddir = wp_upload_dir(null, false, false);
57
- if (isset($uploaddir['basedir']))
58
- $log->setLogPath($uploaddir['basedir'] . "/emr_log");
59
  }
60
- return self::$instance;
61
- }
62
-
63
- // Actions for EMR that always need to hook
64
- protected function nopriv_plugin_actions()
65
- {
66
- // shortcode
67
- add_shortcode('file_modified', array($this, 'get_modified_date'));
68
- }
69
-
70
- public function plugin_actions()
71
- {
72
- $this->plugin_path = plugin_dir_path(EMR_ROOT_FILE);
73
- $this->plugin_url = plugin_dir_url(EMR_ROOT_FILE);
74
-
75
- // init plugin
76
- add_action('admin_menu', array($this,'menu'));
77
- add_action('admin_init', array($this,'init'));
78
- add_action('admin_enqueue_scripts', array($this,'admin_scripts'));
79
-
80
 
81
- // content filters
82
- add_filter('media_row_actions', array($this,'add_media_action'), 10, 2);
83
- add_action('attachment_submitbox_misc_actions', array($this,'admin_date_replaced_media_on_edit_media_screen'), 91 );
84
- //add_filter('upload_mimes', array($this,'add_mime_types'), 1, 1);
85
 
86
- // notices
87
- add_action('admin_notices', array($this,'display_notices'));
88
- add_action('network_admin_notices', array($this,'display_network_notices'));
89
- add_action('wp_ajax_emr_dismiss_notices', array($this,'dismiss_notices'));
90
 
91
- // editors
92
- add_action( 'add_meta_boxes', array($this, 'add_meta_boxes'),10,2 );
93
- add_filter('attachment_fields_to_edit', array($this, 'attachment_editor'), 10, 2);
94
-
95
- /** Just after an image is replaced, try to browser decache the images */
96
- if (isset($_GET['emr_replaced']) && intval($_GET['emr_replaced'] == 1))
97
  {
98
- add_filter('wp_get_attachment_image_src',array($this, 'attempt_uncache_image'), 10, 4);
 
 
99
 
100
- // adds a metabox to list thumbnails. This is a cache reset hidden as feature.
101
- //add_action( 'add_meta_boxes', function () { );
102
- add_filter('postbox_classes_attachment_emr-showthumbs-box', function($classes) { $classes[] = 'closed'; return $classes; });
 
 
 
 
 
103
  }
104
 
 
 
 
 
 
 
105
 
106
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  /**
109
  * Register this file in WordPress so we can call it with a ?page= GET var.
110
  * To suppress it in the menu we give it an empty menu title.
111
  */
112
- public function menu()
113
- {
114
- /* add_submenu_page(null, esc_html__("Replace media", "enable-media-replace"), esc_html__("Replace media", "enable-media-replace"), 'upload_files', 'enable-media-replace/enable-media-replace', array($this, 'route')); */
115
- add_submenu_page(null, esc_html__("Replace media", "enable-media-replace"), esc_html__("Replace media", "enable-media-replace"), 'upload_files', 'enable-media-replace/enable-media-replace', array($this, 'route'));
116
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  /**
119
  * Initialize this plugin. Called by 'admin_init' hook.
120
  *
121
  */
122
- public function init()
123
- {
124
- load_plugin_textdomain( 'enable-media-replace', false, basename(dirname(EMR_ROOT_FILE) ) . '/languages' );
125
 
126
- // Load Submodules
127
 
128
- $notices = Notices::getInstance();
129
 
130
- // Enqueue notices
131
- add_action('admin_notices', array($notices, 'admin_notices')); // previous page / init time
132
- add_action('admin_footer', array($notices, 'admin_notices')); // fresh notices between init - end
133
 
134
- new Externals();
135
- }
 
136
 
137
- /** Load EMR views based on request */
138
- public function route()
139
- {
140
- global $plugin_page;
141
- switch($plugin_page)
142
- {
143
- case 'enable-media-replace/enable-media-replace.php':
144
- $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : '';
145
- wp_enqueue_style('emr_style');
146
- wp_enqueue_script('jquery-ui-datepicker');
147
- wp_enqueue_style('jquery-ui-datepicker');
148
- wp_enqueue_script('emr_admin');
149
-
150
- if (! check_admin_referer( $action, '_wpnonce') )
151
- {
152
- die('Invalid Nonce');
153
- }
154
 
155
- // @todo Later this should be move to it's own controller, and built view from there.
156
- if ( $action == 'media_replace' ) {
157
- if ( array_key_exists("attachment_id", $_GET) && intval($_GET["attachment_id"]) > 0) {
158
- wp_enqueue_script('emr_upsell');
159
- require_once($this->plugin_path . "views/popup.php"); // warning variables like $action be overwritten here.
160
- }
161
- }
162
- elseif ( $action == 'media_replace_upload' ) {
163
- require_once($this->plugin_path . 'views/upload.php');
164
- }
165
- else {
166
- exit('Something went wrong loading page, please try again');
167
- }
168
 
169
- break;
170
- }
 
171
 
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
- public function getPluginURL($path = '')
175
- {
176
- return plugins_url($path, EMR_ROOT_FILE);
177
- }
178
 
179
  /** register styles and scripts
180
  *
181
  * Nothing should ever by -enqueued- here, just registered.
182
  */
183
- public function admin_scripts()
184
- {
185
- if (is_rtl())
186
  {
187
- wp_register_style('emr_style', plugins_url('css/admin.rtl.css', EMR_ROOT_FILE) );
188
- }
189
- else {
190
- wp_register_style('emr_style', plugins_url('css/admin.css', EMR_ROOT_FILE) );
191
- }
192
 
193
- wp_register_style('emr_edit-attachment', plugins_url('css/edit_attachment.css', EMR_ROOT_FILE));
194
 
195
- $mimes = array_values(get_allowed_mime_types());
196
 
197
- wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true );
198
- $emr_options = array(
199
- 'dateFormat' => $this->convertdate(get_option( 'date_format' )),
 
 
200
  'maxfilesize' => wp_max_upload_size(),
201
  'allowed_mime' => $mimes,
202
- );
203
 
204
- wp_register_script('emr_upsell', plugins_url('js/upsell.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true );
205
 
206
- wp_localize_script('emr_upsell', 'emr_upsell', array(
207
- 'ajax' => admin_url('admin-ajax.php'),
208
- 'installing' => __('Installing ...', 'enable-media-replace'),
209
 
210
- ));
211
 
212
- if (Log::debugIsActive())
213
- $emr_options['is_debug'] = true;
214
 
215
- wp_localize_script('emr_admin', 'emr_options', $emr_options);
216
- }
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
  /** Utility function for the Jquery UI Datepicker */
219
- public function convertdate( $sFormat ) {
220
- switch( $sFormat ) {
221
- //Predefined WP date formats
222
- case 'F j, Y':
223
- return( 'MM dd, yy' );
 
224
  break;
225
- case 'Y/m/d':
226
- return( 'yy/mm/dd' );
227
  break;
228
- case 'm/d/Y':
229
- return( 'mm/dd/yy' );
230
  break;
231
- case 'd/m/Y':
232
- default:
233
- return( 'dd/mm/yy' );
234
- break;
235
- }
236
- }
237
 
238
- public function checkImagePermission($author_id, $post_id)
239
- {
240
- if ($this->general_cap === false && $this->user_cap === false)
241
- {
242
- if ( current_user_can('edit_post', $post_id) === true)
243
- return true;
244
- }
245
- elseif (current_user_can($this->general_cap))
246
- return true;
247
- elseif (current_user_can($this->user_cap) && $author_id == get_current_user_id())
248
- return true;
249
-
250
- return false;
251
- }
252
 
253
  /** Get the URL to the media replace page
254
  * @param $attach_id The attachment ID to replace
255
  * @return Admin URL to the page.
256
  */
257
- protected function getMediaReplaceURL($attach_id)
258
- {
259
- $url = admin_url( "upload.php");
260
- $url = add_query_arg(array(
261
  'page' => 'enable-media-replace/enable-media-replace.php',
262
  'action' => 'media_replace',
263
  'attachment_id' => $attach_id,
264
- ), $url);
265
 
266
- return $url;
 
267
 
268
- }
 
 
 
 
 
 
 
269
 
270
- public function add_meta_boxes($post_type, $post)
271
- {
272
- // Because some plugins don't like to play by the rules.
273
- if (is_null($post_type) || is_null($post))
274
- return false;
275
 
276
- if (! $this->checkImagePermission($post->post_author, $post->ID))
277
- { return; }
 
 
 
 
278
 
279
- add_meta_box('emr-replace-box', __('Replace Media', 'enable-media-replace'), array($this, 'replace_meta_box'), 'attachment', 'side', 'low');
 
 
280
 
281
- if (isset($_GET['emr_replaced']) && intval($_GET['emr_replaced'] == 1))
282
- {
283
- add_meta_box('emr-showthumbs-box', __('Replaced Thumbnails Preview', 'enable-media-replace'), array($this, 'show_thumbs_box'), 'attachment', 'side', 'low');
284
- }
285
 
286
- }
 
 
 
287
 
288
- public function replace_meta_box($post)
289
- {
290
 
291
- $url = $this->getMediaReplaceURL($post->ID);
 
292
 
293
- $action = "media_replace";
294
- $editurl = wp_nonce_url( $url, $action );
295
 
296
- /* Unneeded - admin_url already checks for force_ssl_admin ( in set_scheme function )
297
- if (FORCE_SSL_ADMIN) {
298
- $editurl = str_replace("http:", "https:", $editurl);
299
- } */
300
- $link = "href=\"$editurl\"";
301
 
 
 
 
302
 
303
- echo "<p><a class='button-secondary' $link>" . esc_html__("Upload a new file", "enable-media-replace") . "</a></p><p>" . esc_html__("To replace the current file, click the link and upload a replacement.", "enable-media-replace") . "</p>";
304
- }
305
 
306
- public function show_thumbs_box($post)
307
- {
308
- if (! $this->checkImagePermission($post->post_author, $post->ID))
309
- { return; }
310
 
311
- wp_enqueue_style('emr_edit-attachment');
312
-
313
- $meta = wp_get_attachment_metadata($post->ID);
314
-
315
- if (! isset($meta['sizes']) )
316
- { echo __('Thumbnails were not generated', 'enable-media-replace');
317
- return false;
318
  }
319
 
320
- if (function_exists('wp_get_original_image_url')) // indicating WP 5.3+
321
  {
322
- $source_url = wp_get_original_image_url($post->ID);
323
- // oldway will give -scaled in case of scaling.
324
- $source_url_oldway = wp_get_attachment_url($post->ID);
325
 
326
- if ($source_url !== $source_url_oldway)
327
- {
328
- echo "<div class='original previewwrapper'><img src='" . $source_url_oldway . "'><span class='label'>" . __('Original') . "</span></div>";
329
- }
330
 
331
- }
332
 
 
 
 
 
333
 
334
- foreach($meta['sizes'] as $size => $data)
335
- {
336
- $display_size = ucfirst(str_replace("_", " ", $size));
337
- $img = wp_get_attachment_image_src($post->ID, $size);
338
- echo "<div class='$size previewwrapper'><img src='" . $img[0] . "'><span class='label'>$display_size</span></div>";
 
 
 
 
 
 
 
 
 
 
 
339
  }
340
- }
341
 
342
- public function attachment_editor($form_fields, $post)
343
- {
344
- $screen = null;
345
 
346
- if (! $this->checkImagePermission($post->post_author, $post->ID))
347
- { return $form_fields; }
 
348
 
349
- if (function_exists('get_current_screen'))
350
- {
351
- $screen = get_current_screen();
352
 
353
- if(! is_null($screen) && $screen->id == 'attachment') // hide on edit attachment screen.
354
- return $form_fields;
355
- }
 
356
 
357
- $url = $this->getMediaReplaceURL($post->ID);
358
- $action = "media_replace";
359
- $editurl = wp_nonce_url( $url, $action );
360
 
361
- $link = "href=\"$editurl\"";
362
- $form_fields["enable-media-replace"] = array(
363
  "label" => esc_html__("Replace media", "enable-media-replace"),
364
  "input" => "html",
365
- "html" => "<a class='button-secondary' $link>" . esc_html__("Upload a new file", "enable-media-replace") . "</a>", "helps" => esc_html__("To replace the current file, click the link and upload a replacement.", "enable-media-replace")
366
  );
367
 
368
- return $form_fields;
369
- }
 
 
 
 
 
 
 
 
 
 
370
 
371
  /**
372
  * @param array $mime_types
373
  * @return array
374
  */
375
- /* Off, no clue why this is here.
376
  public function add_mime_types($mime_types)
377
  {
378
  $mime_types['dat'] = 'text/plain'; // Adding .dat extension
@@ -383,102 +486,117 @@ class EnableMediaReplacePlugin
383
  * Function called by filter 'media_row_actions'
384
  * Enables linking to EMR straight from the media library
385
  */
386
- public function add_media_action( $actions, $post) {
 
387
 
 
 
 
388
 
389
- if (! $this->checkImagePermission($post->post_author, $post->ID))
390
- { return $actions; }
 
 
 
 
391
 
392
- $url = $this->getMediaReplaceURL($post->ID);
393
- $action = "media_replace";
394
- $editurl = wp_nonce_url( $url, $action );
 
 
 
395
 
396
- /* See above, not needed.
397
- if (FORCE_SSL_ADMIN) {
398
- $editurl = str_replace("http:", "https:", $editurl);
399
- } */
400
- $link = "href=\"$editurl\"";
401
 
402
- $newaction['adddata'] = '<a ' . $link . ' aria-label="' . esc_attr__("Replace media", "enable-media-replace") . '" rel="permalink">' . esc_html__("Replace media", "enable-media-replace") . '</a>';
403
- return array_merge($actions,$newaction);
404
- }
405
 
 
 
 
406
 
407
- public function display_notices() {
408
- $current_screen = get_current_screen();
409
 
410
- $crtScreen = function_exists("get_current_screen") ? get_current_screen() : (object)array("base" => false);
411
- /*
412
- if(current_user_can( 'activate_plugins' ) && !get_option( 'emr_news') && !is_plugin_active('shortpixel-image-optimiser/wp-shortpixel.php')
413
- && ($crtScreen->base == "upload" || $crtScreen->base == "plugins")
 
 
 
 
414
  //for network installed plugins, don't display the message on subsites.
415
  && !(function_exists('is_multisite') && is_multisite() && is_plugin_active_for_network('enable-media-replace/enable-media-replace.php') && !is_main_site()))
416
- {
417
- require_once($this->plugin_path . '/views/notice.php');
418
- } */
419
- }
420
 
421
- public function display_network_notices() {
422
- if(current_user_can( 'activate_plugins' ) && !get_option( 'emr_news') && !is_plugin_active_for_network('shortpixel-image-optimiser/wp-shortpixel.php')) {
423
- require_once( str_replace("enable-media-replace.php", "notice.php", __FILE__) );
424
- }
425
- }
 
426
 
427
  /* Ajax function to dismiss notice */
428
- public function dismiss_notices() {
429
- update_option( 'emr_news', true);
430
- exit(json_encode(array("Status" => 0)));
431
- }
 
432
 
433
  /** Outputs the replaced date of the media on the edit_attachment screen
434
  *
435
  * @param $post Obj Post Object
436
  */
437
- function admin_date_replaced_media_on_edit_media_screen($post) {
438
-
439
- // Fallback for before version 4.9, doens't pass post.
440
- if (! is_object($post))
441
- global $post;
442
 
443
- if (! is_object($post)) // try to global, if it doesn't work - return.
444
- return false;
 
 
445
 
446
- $post_id = $post->ID;
447
- if ( $post->post_modified !== $post->post_date ) {
 
448
 
449
- $modified = date_i18n( __( 'M j, Y @ H:i' ) , strtotime( $post->post_modified ) );
450
- ?>
451
- <div class="misc-pub-section curtime">
452
- <span id="timestamp"><?php echo esc_html__( 'Revised', 'enable-media-replace' ); ?>: <b><?php echo $modified; ?></b></span>
453
- </div>
 
 
454
 
455
- <?php
456
- }
457
- $author_id = get_post_meta($post_id, '_emr_replace_author', true);
458
 
459
- if ($author_id)
460
- {
461
- $display_name = get_the_author_meta('display_name', $author_id);
462
- ?>
463
  <div class="misc-pub-section replace_author">
464
- <span><?php echo esc_html__( 'Replaced By', 'enable-media-replace' ); ?>: <b><?php echo $display_name; ?></b></span>
465
  </div>
466
- <?php
 
467
  }
468
 
469
- }
470
-
471
  /** When an image is just replaced, it can stuck in the browser cache making a look like it was not replaced. Try
472
  * undo that effect by adding a timestamp to the query string */
473
- public function attempt_uncache_image($image, $attachment_id, $size, $icon)
474
- {
475
- if ($image === false)
476
- return $image;
 
477
 
478
- // array with image src on 0
479
- $image[0] = add_query_arg('time', time(), $image[0]);
480
- return $image;
481
- }
482
 
483
  /**
484
  * Shorttag function to show the media file modification date/time.
@@ -486,35 +604,35 @@ class EnableMediaReplacePlugin
486
  * @return string content / replacement shorttag
487
  * @todo Note this returns the wrong date, ie. server date not corrected for timezone. Function could be removed altogether, not sure about purpose.
488
  */
489
- public function get_modified_date($atts) {
490
- $id=0;
491
- $format= '';
492
-
493
- extract(shortcode_atts(array(
494
- 'id' => '',
495
- 'format' => get_option('date_format') . " " . get_option('time_format'),
496
- ), $atts));
497
-
498
- if ($id == '') return false;
499
-
500
- // Get path to file
501
- $current_file = get_attached_file($id);
502
 
503
- if ( ! file_exists( $current_file ) ) {
504
- return false;
505
- }
 
506
 
507
- // Get file modification time
508
- $filetime = filemtime($current_file);
 
509
 
510
- if ( false !== $filetime ) {
511
- // do date conversion
512
- return date( $format, $filetime );
513
- }
514
 
515
- return false;
516
- }
 
517
 
 
 
518
 
 
 
 
 
519
 
 
 
520
  } // class
1
  <?php
2
  namespace EnableMediaReplace;
3
+
4
  use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
  use EnableMediaReplace\Notices\NoticeController as Notices;
6
+ use EnableMediaReplace\FileSystem\Controller\FileSystemController as FileSystem;
7
+ use EnableMediaReplace\Ajax;
8
 
9
  // Does what a plugin does.
10
  class EnableMediaReplacePlugin
11
  {
12
 
13
+ protected $plugin_path;
14
+ private static $instance;
 
 
 
 
 
 
 
 
15
 
16
+ private $user_cap = false;
17
+ private $general_cap = false;
 
18
 
19
+ public function __construct()
20
+ {
21
+ add_action('plugins_loaded', array($this, 'runtime'));
22
+ }
 
 
23
 
24
+ public function runtime()
25
+ {
26
+ $this->nopriv_plugin_actions();
27
+
28
+ if (EMR_CAPABILITY !== false) {
29
+ if (is_array(EMR_CAPABILITY)) {
30
+ $this->general_cap = EMR_CAPABILITY[0];
31
+ $this->user_cap = EMR_CAPABILITY[1];
32
+
33
+ if (! current_user_can($this->general_cap) && ! current_user_can($this->user_cap)) {
34
+ return;
35
+ }
36
+ } else {
37
+ $this->general_cap = EMR_CAPABILITY;
38
+ if (! current_user_can($this->general_cap)) {
39
+ return;
40
+ }
41
+ }
42
+ } elseif (! current_user_can('upload_files')) {
43
  return;
44
  }
 
 
 
45
 
46
+ $this->plugin_actions(); // init
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ public function filesystem()
50
+ {
51
+ return new FileSystem();
52
+ }
53
 
54
+ public function uiHelper()
55
+ {
56
+ return Uihelper::getInstance();
57
+ }
58
 
59
+ public static function get()
 
 
 
 
 
60
  {
61
+ if (is_null(self::$instance)) {
62
+ self::$instance = new EnableMediaReplacePlugin();
63
+ }
64
 
65
+ $log = Log::getInstance();
66
+ if (Log::debugIsActive()) {
67
+ $uploaddir = wp_upload_dir(null, false, false);
68
+ if (isset($uploaddir['basedir'])) {
69
+ $log->setLogPath($uploaddir['basedir'] . "/emr_log");
70
+ }
71
+ }
72
+ return self::$instance;
73
  }
74
 
75
+ // Actions for EMR that always need to hook
76
+ protected function nopriv_plugin_actions()
77
+ {
78
+ // shortcode
79
+ add_shortcode('file_modified', array($this, 'get_modified_date'));
80
+ }
81
 
82
+ public function plugin_actions()
83
+ {
84
+ $this->plugin_path = plugin_dir_path(EMR_ROOT_FILE);
85
+ $this->plugin_url = plugin_dir_url(EMR_ROOT_FILE);
86
+
87
+ // loads the dismiss hook.
88
+ $notices = Notices::getInstance();
89
+
90
+ // init plugin
91
+ add_action('admin_menu', array($this,'menu'));
92
+ add_action('submenu_file', array($this, 'hide_sub_menu'));
93
+ add_action('admin_init', array($this,'init'));
94
+ add_action( 'current_screen', array($this, 'setScreen') ); // annoying workaround for notices in edit-attachment screen
95
+ add_action('admin_enqueue_scripts', array($this,'admin_scripts'));
96
+
97
+
98
+ // content filters
99
+ add_filter('media_row_actions', array($this,'add_media_action'), 10, 2);
100
+ add_action('attachment_submitbox_misc_actions', array($this,'admin_date_replaced_media_on_edit_media_screen'), 91);
101
+ //add_filter('upload_mimes', array($this,'add_mime_types'), 1, 1);
102
+
103
+ // notices
104
+ add_action('admin_notices', array($this,'display_notices'));
105
+ add_action('network_admin_notices', array($this,'display_network_notices'));
106
+ add_action('wp_ajax_emr_dismiss_notices', array($this,'dismiss_notices'));
107
+
108
+ // editors
109
+ add_action('add_meta_boxes', array($this, 'add_meta_boxes'), 10, 2);
110
+ add_filter('attachment_fields_to_edit', array($this, 'attachment_editor'), 10, 2);
111
+
112
+ /** Just after an image is replaced, try to browser decache the images */
113
+ if (isset($_GET['emr_replaced']) && intval($_GET['emr_replaced'] == 1)) {
114
+ add_filter('wp_get_attachment_image_src', array($this, 'attempt_uncache_image'), 10, 4);
115
+
116
+ // adds a metabox to list thumbnails. This is a cache reset hidden as feature.
117
+ //add_action( 'add_meta_boxes', function () { );
118
+ add_filter('postbox_classes_attachment_emr-showthumbs-box', function ($classes) {
119
+ $classes[] = 'closed';
120
+ return $classes;
121
+ });
122
+ }
123
+ }
124
 
125
  /**
126
  * Register this file in WordPress so we can call it with a ?page= GET var.
127
  * To suppress it in the menu we give it an empty menu title.
128
  */
129
+ public function menu()
130
+ {
131
+ $title = esc_html__("Replace media", "enable-media-replace");
132
+ $title = (isset($_REQUEST['action']) && ($_REQUEST['action'] === 'emr_prepare_remove')) ? esc_html__("Remove background", "enable-media-replace") : $title;
133
+ add_submenu_page('upload.php',$title, $title, 'upload_files', 'enable-media-replace/enable-media-replace.php', array($this, 'route'));
134
+ /* add_submenu_page(null, esc_html__("Remove background", "enable-media-replace"), esc_html__("Remove the media Background", "enable-media-replace"), 'upload_files', 'emr-remove-background', array($this, 'route')); */
135
+
136
+
137
+ }
138
+
139
+ public function hide_sub_menu($submenu_file)
140
+ {
141
+ global $plugin_page;
142
+ // Select another submenu item to highlight (optional).
143
+ if ( $plugin_page && $plugin_page == 'enable-media-replace/enable-media-replace.php' ) {
144
+ $submenu_file = 'upload.php';
145
+ }
146
+
147
+ // Hide the submenu.
148
+
149
+ remove_submenu_page( 'upload.php', 'enable-media-replace/enable-media-replace.php' );
150
+
151
+ return $submenu_file;
152
+ }
153
 
154
  /**
155
  * Initialize this plugin. Called by 'admin_init' hook.
156
  *
157
  */
158
+ public function init()
159
+ {
160
+ load_plugin_textdomain('enable-media-replace', false, basename(dirname(EMR_ROOT_FILE)) . '/languages');
161
 
162
+ // Load Submodules
163
 
 
164
 
165
+ new Externals();
166
+ new Ajax();
167
+ }
168
 
169
+ public function setScreen()
170
+ {
171
+ $screen = get_current_screen();
172
 
173
+ $notice_pages = array('attachment', 'media_page_enable-media-replace/enable-media-replace', 'upload' );
174
+ if ( in_array($screen->id, $notice_pages) )
175
+ {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
+ $notices = Notices::getInstance();
178
+ add_action('admin_notices', array($notices, 'admin_notices')); // previous page / init time
 
 
 
 
 
 
 
 
 
 
 
179
 
180
+ }
181
+ // var_dump($screen);
182
+ }
183
 
184
+ /** Load EMR views based on request */
185
+ public function route()
186
+ {
187
+ global $plugin_page;
188
+ switch ($plugin_page) {
189
+ case 'enable-media-replace/enable-media-replace.php':
190
+ $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : '';
191
+ wp_enqueue_style('emr_style');
192
+ wp_enqueue_script('jquery-ui-datepicker');
193
+ wp_enqueue_style('jquery-ui-datepicker');
194
+ wp_enqueue_script('emr_admin');
195
+
196
+ $this->uiHelper()->featureNotice();
197
+
198
+
199
+
200
+
201
+ if (! check_admin_referer($action, '_wpnonce')) {
202
+ die('Invalid Nonce');
203
+ }
204
+
205
+ // @todo Later this should be move to it's own controller, and built view from there.
206
+ if ($action == 'media_replace') {
207
+ if (array_key_exists("attachment_id", $_GET) && intval($_GET["attachment_id"]) > 0) {
208
+ wp_enqueue_script('emr_upsell');
209
+ require_once($this->plugin_path . "views/popup.php"); // warning variables like $action be overwritten here.
210
+ }
211
+ }
212
+ elseif ($action == 'media_replace_upload') {
213
+ require_once($this->plugin_path . 'views/upload.php');
214
+ }
215
+ elseif ('emr_prepare_remove' === $action) {
216
+ $attachment_id = intval($_GET['attachment_id']);
217
+ $attachment = get_post($attachment_id);
218
+ //We're adding a timestamp to the image URL for cache busting
219
+
220
+ wp_enqueue_script('emr_remove_bg');
221
+
222
+ wp_enqueue_style('emr_style');
223
+ wp_enqueue_style('emr-remove-background');
224
+ wp_enqueue_script('emr_upsell');
225
+ require_once($this->plugin_path . "views/prepare-remove-background.php");
226
+
227
+ } elseif ('do_background_replace' === $action && check_admin_referer($action, '_wpnonce')) {
228
+ require_once($this->plugin_path . 'views/do-replace-background.php');
229
+ }
230
+ else {
231
+ exit('Something went wrong loading page, please try again');
232
+ }
233
+ break;
234
+ }
235
+ }
236
 
237
+ public function getPluginURL($path = '')
238
+ {
239
+ return plugins_url($path, EMR_ROOT_FILE);
240
+ }
241
 
242
  /** register styles and scripts
243
  *
244
  * Nothing should ever by -enqueued- here, just registered.
245
  */
246
+ public function admin_scripts()
 
 
247
  {
248
+ if (is_rtl()) {
249
+ wp_register_style('emr_style', plugins_url('css/admin.rtl.css', EMR_ROOT_FILE));
250
+ } else {
251
+ wp_register_style('emr_style', plugins_url('css/admin.css', EMR_ROOT_FILE));
252
+ }
253
 
254
+ wp_register_style('emr_edit-attachment', plugins_url('css/edit_attachment.css', EMR_ROOT_FILE));
255
 
256
+ wp_register_style('emr-remove-background', plugins_url('css/remove_background.css', EMR_ROOT_FILE));
257
 
258
+ $mimes = array_values(get_allowed_mime_types());
259
+
260
+ wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true);
261
+ $emr_options = array(
262
+ 'dateFormat' => $this->convertdate(get_option('date_format')),
263
  'maxfilesize' => wp_max_upload_size(),
264
  'allowed_mime' => $mimes,
265
+ );
266
 
267
+ wp_register_script('emr_upsell', plugins_url('js/upsell.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true);
268
 
269
+ wp_localize_script('emr_upsell', 'emr_upsell', array(
270
+ 'ajax' => admin_url('admin-ajax.php'),
271
+ 'installing' => __('Installing ...', 'enable-media-replace'),
272
 
273
+ ));
274
 
275
+ $ts = time();
276
+ $ajax_url = admin_url('admin-ajax.php');
277
 
278
+
279
+ wp_register_script('emr_remove_bg', plugins_url('js/remove_bg.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true);
280
+ wp_localize_script('emr_remove_bg', 'emrObject', array(
281
+ 'ajax_url' => $ajax_url,
282
+ 'nonce' => wp_create_nonce('emr_remove_background')
283
+ ));
284
+
285
+
286
+ if (Log::debugIsActive()) {
287
+ $emr_options['is_debug'] = true;
288
+ }
289
+
290
+ wp_localize_script('emr_admin', 'emr_options', $emr_options);
291
+ }
292
 
293
  /** Utility function for the Jquery UI Datepicker */
294
+ public function convertdate($sFormat)
295
+ {
296
+ switch ($sFormat) {
297
+ //Predefined WP date formats
298
+ case 'F j, Y':
299
+ return( 'MM dd, yy' );
300
  break;
301
+ case 'Y/m/d':
302
+ return( 'yy/mm/dd' );
303
  break;
304
+ case 'm/d/Y':
305
+ return( 'mm/dd/yy' );
306
  break;
307
+ case 'd/m/Y':
308
+ default:
309
+ return( 'dd/mm/yy' );
310
+ break;
311
+ }
312
+ }
313
 
314
+ public function checkImagePermission($author_id, $post_id)
315
+ {
316
+ if ($this->general_cap === false && $this->user_cap === false) {
317
+ if (current_user_can('edit_post', $post_id) === true) {
318
+ return true;
319
+ }
320
+ } elseif (current_user_can($this->general_cap)) {
321
+ return true;
322
+ } elseif (current_user_can($this->user_cap) && $author_id == get_current_user_id()) {
323
+ return true;
324
+ }
325
+
326
+ return false;
327
+ }
328
 
329
  /** Get the URL to the media replace page
330
  * @param $attach_id The attachment ID to replace
331
  * @return Admin URL to the page.
332
  */
333
+ protected function getMediaReplaceURL($attach_id)
334
+ {
335
+ $url = admin_url("upload.php");
336
+ $url = add_query_arg(array(
337
  'page' => 'enable-media-replace/enable-media-replace.php',
338
  'action' => 'media_replace',
339
  'attachment_id' => $attach_id,
340
+ ), $url);
341
 
342
+ return $url;
343
+ }
344
 
345
+ protected function getRemoveBgURL($attach_id)
346
+ {
347
+ $url = admin_url("upload.php");
348
+ $url = add_query_arg(array(
349
+ 'page' => 'enable-media-replace/enable-media-replace.php',
350
+ 'action' => 'emr_prepare_remove',
351
+ 'attachment_id' => $attach_id,
352
+ ), $url);
353
 
354
+ return $url;
355
+ }
 
 
 
356
 
357
+ public function add_meta_boxes($post_type, $post)
358
+ {
359
+ // Because some plugins don't like to play by the rules.
360
+ if (is_null($post_type) || is_null($post)) {
361
+ return false;
362
+ }
363
 
364
+ if (! $this->checkImagePermission($post->post_author, $post->ID)) {
365
+ return;
366
+ }
367
 
368
+ add_meta_box('emr-replace-box', __('Replace Media', 'enable-media-replace'), array($this, 'replace_meta_box'), 'attachment', 'side', 'low');
 
 
 
369
 
370
+ if (isset($_GET['emr_replaced']) && intval($_GET['emr_replaced'] == 1)) {
371
+ add_meta_box('emr-showthumbs-box', __('Replaced Thumbnails Preview', 'enable-media-replace'), array($this, 'show_thumbs_box'), 'attachment', 'side', 'low');
372
+ }
373
+ }
374
 
375
+ public function replace_meta_box($post)
376
+ {
377
 
378
+ //Replace media button
379
+ $replace_url = $this->getMediaReplaceURL($post->ID);
380
 
381
+ $replace_action = "media_replace";
382
+ $replace_editurl = wp_nonce_url($replace_url, $replace_action);
383
 
384
+ $replace_link = "href=\"$replace_editurl\"";
 
 
 
 
385
 
386
+ echo "<p><a class='button-secondary' $replace_link>" . esc_html__("Upload a new file", "enable-media-replace") . "</a></p><p>" . esc_html__("To replace the current file, click the link and upload a replacement file.", "enable-media-replace") . "</p>";
387
+ //Remove background button
388
+ $removeBg_url = $this->getRemoveBgURL($post->ID);
389
 
390
+ $removeBg_action = "emr_prepare_remove";
391
+ $removeBg_editurl = wp_nonce_url($removeBg_url, $removeBg_action);
392
 
393
+ $removeBg_link = "href=\"$removeBg_editurl\"";
 
 
 
394
 
395
+ if ($this->uiHelper()->isBackgroundRemovable($post))
396
+ {
397
+ echo "<p><a class='button-secondary' $removeBg_link>" . esc_html__("Remove background", "enable-media-replace") . "</a></p><p>" . esc_html__("To remove the background, click the link and select the options.", "enable-media-replace") . "</p>";
398
+ }
 
 
 
399
  }
400
 
401
+ public function show_thumbs_box($post)
402
  {
403
+ if (! $this->checkImagePermission($post->post_author, $post->ID)) {
404
+ return;
405
+ }
406
 
407
+ wp_enqueue_style('emr_edit-attachment');
 
 
 
408
 
409
+ $meta = wp_get_attachment_metadata($post->ID);
410
 
411
+ if (! isset($meta['sizes'])) {
412
+ echo __('Thumbnails were not generated', 'enable-media-replace');
413
+ return false;
414
+ }
415
 
416
+ if (function_exists('wp_get_original_image_url')) { // indicating WP 5.3+
417
+ $source_url = wp_get_original_image_url($post->ID);
418
+ // oldway will give -scaled in case of scaling.
419
+ $source_url_oldway = wp_get_attachment_url($post->ID);
420
+
421
+ if ($source_url !== $source_url_oldway) {
422
+ echo "<div class='original previewwrapper'><img src='" . $source_url_oldway . "'><span class='label'>" . __('Original') . "</span></div>";
423
+ }
424
+ }
425
+
426
+
427
+ foreach ($meta['sizes'] as $size => $data) {
428
+ $display_size = ucfirst(str_replace("_", " ", $size));
429
+ $img = wp_get_attachment_image_src($post->ID, $size);
430
+ echo "<div class='$size previewwrapper'><img src='" . $img[0] . "'><span class='label'>$display_size</span></div>";
431
+ }
432
  }
 
433
 
434
+ public function attachment_editor($form_fields, $post)
435
+ {
436
+ $screen = null;
437
 
438
+ if (! $this->checkImagePermission($post->post_author, $post->ID)) {
439
+ return $form_fields;
440
+ }
441
 
442
+ if (function_exists('get_current_screen')) {
443
+ $screen = get_current_screen();
 
444
 
445
+ if (! is_null($screen) && $screen->id == 'attachment') { // hide on edit attachment screen.
446
+ return $form_fields;
447
+ }
448
+ }
449
 
450
+ $url = $this->getMediaReplaceURL($post->ID);
451
+ $action = "media_replace";
452
+ $editurl = wp_nonce_url($url, $action);
453
 
454
+ $link = "href=\"$editurl\"";
455
+ $form_fields["enable-media-replace"] = array(
456
  "label" => esc_html__("Replace media", "enable-media-replace"),
457
  "input" => "html",
458
+ "html" => "<a class='button-secondary' $link>" . esc_html__("Upload a new file", "enable-media-replace") . "</a>", "helps" => esc_html__("To replace the current file, click the link and upload a replacement file.", "enable-media-replace")
459
  );
460
 
461
+ if ($this->uiHelper()->isBackgroundRemovable($post))
462
+ {
463
+ $link = $this->getRemoveBgURL($post->ID);
464
+ $link = "href='" . wp_nonce_url($link, 'emr_prepare_remove') . "'";
465
+ $form_fields["emr-remove-background"] = array(
466
+ "label" => esc_html__("Remove background", "enable-media-replace"),
467
+ "input" => "html",
468
+ "html" => "<a class='button-secondary' $link>" . esc_html__("Remove background", "enable-media-replace") . "</a>", "helps" => esc_html__("To remove the background, click the link.", "enable-media-replace")
469
+ );
470
+ }
471
+ return $form_fields;
472
+ }
473
 
474
  /**
475
  * @param array $mime_types
476
  * @return array
477
  */
478
+ /* Off, no clue why this is here.
479
  public function add_mime_types($mime_types)
480
  {
481
  $mime_types['dat'] = 'text/plain'; // Adding .dat extension
486
  * Function called by filter 'media_row_actions'
487
  * Enables linking to EMR straight from the media library
488
  */
489
+ public function add_media_action($actions, $post)
490
+ {
491
 
492
+ if (! $this->checkImagePermission($post->post_author, $post->ID)) {
493
+ return $actions;
494
+ }
495
 
496
+ $url = $this->getMediaReplaceURL($post->ID);
497
+ $media_replace_action = "media_replace";
498
+ $media_replace_editurl = wp_nonce_url($url, $media_replace_action);
499
+ $url = $this->getRemoveBgURL($post->ID);
500
+ $background_remove_action = "emr_prepare_remove";
501
+ $background_remove_editurl = wp_nonce_url($url, $background_remove_action);
502
 
503
+ /* See above, not needed.
504
+ if (FORCE_SSL_ADMIN) {
505
+ $editurl = str_replace("http:", "https:", $editurl);
506
+ } */
507
+ $media_replace_link = "href=\"$media_replace_editurl\"";
508
+ $background_remove_link = "href=\"$background_remove_editurl\"";
509
 
510
+ $newaction['media_replace'] = '<a ' . $media_replace_link . ' aria-label="' . esc_attr__("Replace media", "enable-media-replace") . '" rel="permalink">' . esc_html__("Replace media", "enable-media-replace") . '</a>';
 
 
 
 
511
 
512
+ if ($this->uiHelper()->isBackgroundRemovable($post))
513
+ {
514
+ $newaction['remove_background'] = '<a ' . $background_remove_link . ' aria-label="' . esc_attr__("Remove background", "enable-media-replace") . '" rel="permalink">' . esc_html__("Remove background", "enable-media-replace") . '</a>';
515
 
516
+ }
517
+ return array_merge($actions, $newaction);
518
+ }
519
 
 
 
520
 
521
+ public function display_notices()
522
+ {
523
+ $current_screen = get_current_screen();
524
+
525
+ $crtScreen = function_exists("get_current_screen") ? get_current_screen() : (object)array("base" => false);
526
+ /*
527
+ if(current_user_can( 'activate_plugins' ) && !get_option( 'emr_news') && !is_plugin_active('shortpixel-image-optimiser/wp-shortpixel.php')
528
+ && ($crtScreen->base == "upload" || $crtScreen->base == "plugins")
529
  //for network installed plugins, don't display the message on subsites.
530
  && !(function_exists('is_multisite') && is_multisite() && is_plugin_active_for_network('enable-media-replace/enable-media-replace.php') && !is_main_site()))
531
+ {
532
+ require_once($this->plugin_path . '/views/notice.php');
533
+ } */
534
+ }
535
 
536
+ public function display_network_notices()
537
+ {
538
+ if (current_user_can('activate_plugins') && !get_option('emr_news') && !is_plugin_active_for_network('shortpixel-image-optimiser/wp-shortpixel.php')) {
539
+ require_once(str_replace("enable-media-replace.php", "notice.php", __FILE__));
540
+ }
541
+ }
542
 
543
  /* Ajax function to dismiss notice */
544
+ public function dismiss_notices()
545
+ {
546
+ update_option('emr_news', true);
547
+ exit(json_encode(array("Status" => 0)));
548
+ }
549
 
550
  /** Outputs the replaced date of the media on the edit_attachment screen
551
  *
552
  * @param $post Obj Post Object
553
  */
554
+ function admin_date_replaced_media_on_edit_media_screen($post)
555
+ {
 
 
 
556
 
557
+ // Fallback for before version 4.9, doens't pass post.
558
+ if (! is_object($post)) {
559
+ global $post;
560
+ }
561
 
562
+ if (! is_object($post)) { // try to global, if it doesn't work - return.
563
+ return false;
564
+ }
565
 
566
+ $post_id = $post->ID;
567
+ if ($post->post_modified !== $post->post_date) {
568
+ $modified = date_i18n(__('M j, Y @ H:i'), strtotime($post->post_modified));
569
+ ?>
570
+ <div class="misc-pub-section curtime">
571
+ <span id="timestamp"><?php echo esc_html__('Revised', 'enable-media-replace'); ?>: <b><?php echo $modified; ?></b></span>
572
+ </div>
573
 
574
+ <?php
575
+ }
576
+ $author_id = get_post_meta($post_id, '_emr_replace_author', true);
577
 
578
+ if ($author_id) {
579
+ $display_name = get_the_author_meta('display_name', $author_id);
580
+ ?>
 
581
  <div class="misc-pub-section replace_author">
582
+ <span><?php echo esc_html__('Replaced By', 'enable-media-replace'); ?>: <b><?php echo $display_name; ?></b></span>
583
  </div>
584
+ <?php
585
+ }
586
  }
587
 
 
 
588
  /** When an image is just replaced, it can stuck in the browser cache making a look like it was not replaced. Try
589
  * undo that effect by adding a timestamp to the query string */
590
+ public function attempt_uncache_image($image, $attachment_id, $size, $icon)
591
+ {
592
+ if ($image === false) {
593
+ return $image;
594
+ }
595
 
596
+ // array with image src on 0
597
+ $image[0] = add_query_arg('time', time(), $image[0]);
598
+ return $image;
599
+ }
600
 
601
  /**
602
  * Shorttag function to show the media file modification date/time.
604
  * @return string content / replacement shorttag
605
  * @todo Note this returns the wrong date, ie. server date not corrected for timezone. Function could be removed altogether, not sure about purpose.
606
  */
607
+ public function get_modified_date($atts)
608
+ {
609
+ $id=0;
610
+ $format= '';
 
 
 
 
 
 
 
 
 
611
 
612
+ extract(shortcode_atts(array(
613
+ 'id' => '',
614
+ 'format' => get_option('date_format') . " " . get_option('time_format'),
615
+ ), $atts));
616
 
617
+ if ($id == '') {
618
+ return false;
619
+ }
620
 
621
+ // Get path to file
622
+ $current_file = get_attached_file($id);
 
 
623
 
624
+ if (! file_exists($current_file)) {
625
+ return false;
626
+ }
627
 
628
+ // Get file modification time
629
+ $filetime = filemtime($current_file);
630
 
631
+ if (false !== $filetime) {
632
+ // do date conversion
633
+ return date($format, $filetime);
634
+ }
635
 
636
+ return false;
637
+ }
638
  } // class
classes/file.php DELETED
@@ -1,104 +0,0 @@
1
- <?php
2
- namespace EnableMediaReplace;
3
-
4
- use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
- use EnableMediaReplace\Notices\NoticeController as Notices;
6
-
7
- class emrFile
8
- {
9
-
10
- protected $file; // the full file w/ path.
11
- protected $extension;
12
- protected $fileName;
13
- protected $filePath; // with trailing slash! not the image name.
14
- protected $fileURL;
15
- protected $fileMime;
16
- protected $permissions = 0;
17
-
18
- protected $exists = false;
19
-
20
- public function __construct($file)
21
- {
22
- clearstatcache($file);
23
- // file can not exist i.e. crashed files replacement and the lot.
24
- if ( file_exists($file))
25
- {
26
- $this->exists = true;
27
- }
28
-
29
- $this->file = $file;
30
- $fileparts = pathinfo($file);
31
-
32
- $this->fileName = isset($fileparts['basename']) ? $fileparts['basename'] : '';
33
- $this->filePath = isset($fileparts['dirname']) ? trailingslashit($fileparts['dirname']) : '';
34
- $this->extension = isset($fileparts['extension']) ? $fileparts['extension'] : '';
35
- if ($this->exists) // doesn't have to be.
36
- $this->permissions = fileperms($file) & 0777;
37
-
38
- $filedata = wp_check_filetype_and_ext($this->file, $this->fileName);
39
- // This will *not* be checked, is not meant for permission of validation!
40
- // Note: this function will work on non-existing file, but not on existing files containing wrong mime in file.
41
- $this->fileMime = (isset($filedata['type']) && strlen($filedata['type']) > 0) ? $filedata['type'] : false;
42
-
43
- if ($this->fileMime == false && strlen($this->file) > 0 && function_exists('mime_content_type') && $this->exists)
44
- {
45
- // If it's not a registered mimetype
46
- $this->fileMime = mime_content_type($this->file);
47
- }
48
-
49
- }
50
-
51
- public function checkAndCreateFolder()
52
- {
53
- $path = $this->getFilePath();
54
- if (! is_dir($path) && ! file_exists($path))
55
- {
56
- return wp_mkdir_p($path);
57
- }
58
- }
59
-
60
- public function getFullFilePath()
61
- {
62
- return $this->file;
63
- }
64
-
65
- public function getPermissions()
66
- {
67
- return $this->permissions;
68
- }
69
-
70
- public function setPermissions($permissions)
71
- {
72
- @chmod($this->file, $permissions);
73
- }
74
-
75
- public function getFileSize()
76
- {
77
- return filesize($this->file);
78
- }
79
-
80
- public function getFilePath()
81
- {
82
- return $this->filePath;
83
- }
84
-
85
- public function getFileName()
86
- {
87
- return $this->fileName;
88
- }
89
-
90
- public function getFileExtension()
91
- {
92
- return $this->extension;
93
- }
94
-
95
- public function getFileMime()
96
- {
97
- return $this->fileMime;
98
- }
99
-
100
- public function exists()
101
- {
102
- return $this->exists;
103
- }
104
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/installHelper.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace;
3
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use EnableMediaReplace\Notices\NoticeController as Notices;
5
+
6
+ class InstallHelper
7
+ {
8
+
9
+ public static function unInstallPlugin()
10
+ {
11
+ delete_option( 'enable_media_replace' );
12
+ delete_option( 'emr_news' );
13
+ delete_option( 'emr_url_cache');
14
+ }
15
+
16
+ public static function deactivatePlugin()
17
+ {
18
+ Notices::resetNotices();
19
+ }
20
+ }
classes/replacer.php CHANGED
@@ -62,7 +62,7 @@ class Replacer
62
  }
63
 
64
  Log::addDebug('SourceFile ' . $source_file);
65
- $this->sourceFile = new File($source_file);
66
 
67
  $this->source_post = get_post($post_id);
68
  $this->source_is_image = wp_attachment_is('image', $this->source_post);
@@ -82,6 +82,11 @@ class Replacer
82
  //$this->ThumbnailUpdater->setOldMetadata($this->source_metadata);
83
  }
84
 
 
 
 
 
 
85
  public function setMode($mode)
86
  {
87
  $this->replaceMode = $mode;
@@ -107,6 +112,7 @@ class Replacer
107
  $this->targetName = $fileName;
108
 
109
  $targetFile = $this->getTargetFile();
 
110
 
111
  if (is_null($targetFile))
112
  {
@@ -115,34 +121,41 @@ class Replacer
115
  // throw new \RuntimeException($ex);
116
  }
117
 
118
- $targetFileObj = new File($targetFile);
119
- $result = $targetFileObj->checkAndCreateFolder();
 
 
120
  if ($result === false)
121
  Log::addError('Directory creation for targetFile failed');
122
 
 
123
 
124
  $this->removeCurrent(); // tries to remove the current files.
125
-
126
  /* @todo See if wp_handle_sideload / wp_handle_upload can be more securely used for this */
127
- $result_moved = move_uploaded_file($file,$targetFile);
 
 
 
 
 
 
 
 
 
 
128
 
129
- if (false === $result_moved)
130
- {
131
- $ex = sprintf( esc_html__('The uploaded file could not be moved to %1$s. This is most likely an issue with permissions, or upload failed.', "enable-media-replace"), $targetFile );
132
- throw new \RuntimeException($ex);
133
- }
134
 
135
  // init targetFile.
136
- $this->targetFile = new File($targetFile);
137
 
138
- if ($this->sourceFile->getPermissions() > 0)
139
- chmod( $targetFile, $this->sourceFile->getPermissions() ); // restore permissions
140
  else {
141
  Log::addWarn('Setting permissions failed');
142
  }
143
 
144
  // update the file attached. This is required for wp_get_attachment_url to work.
145
- $updated = update_attached_file($this->post_id, $this->targetFile->getFullFilePath() );
146
  if (! $updated)
147
  Log::addError('Update Attached File reports as not updated or same value');
148
 
@@ -150,31 +163,31 @@ class Replacer
150
 
151
  // Run the filter, so other plugins can hook if needed.
152
  $filtered = apply_filters( 'wp_handle_upload', array(
153
- 'file' => $this->targetFile->getFullFilePath(),
154
  'url' => $this->target_url,
155
- 'type' => $this->targetFile->getFileMime(),
156
  ), 'sideload');
157
 
158
  // check if file changed during filter. Set changed to attached file meta properly.
159
- if (isset($filtered['file']) && $filtered['file'] != $this->targetFile->getFullFilePath() )
160
  {
161
  update_attached_file($this->post_id, $filtered['file'] );
162
- $this->targetFile = new File($filtered['file']); // handle as a new file
163
  Log::addInfo('WP_Handle_upload filter returned different file', $filtered);
164
  }
165
 
166
  // Check and update post mimetype, otherwise badly coded plugins cry.
167
  $post_mime = get_post_mime_type($this->post_id);
168
- $target_mime = $this->targetFile->getFileMime();
169
 
170
  // update DB post mime type, if somebody decided to mess it up, and the target one is not empty.
171
  if ($target_mime !== $post_mime && strlen($target_mime) > 0)
172
  {
173
 
174
- \wp_update_post(array('post_mime_type' => $this->targetFile->getFileMime(), 'ID' => $this->post_id));
175
  }
176
 
177
- $metadata = wp_generate_attachment_metadata( $this->post_id, $this->targetFile->getFullFilePath() );
178
  wp_update_attachment_metadata( $this->post_id, $metadata );
179
  $this->target_metadata = $metadata;
180
 
@@ -259,7 +272,7 @@ class Replacer
259
  protected function getNewTitle()
260
  {
261
  // get basename without extension
262
- $title = basename($this->targetFile->getFileName(), '.' . $this->targetFile->getFileExtension());
263
  $meta = $this->target_metadata;
264
 
265
  if (isset($meta['image_meta']))
@@ -304,11 +317,24 @@ class Replacer
304
  return $this->sourceFile;
305
  }
306
 
 
 
 
 
 
 
307
  public function setNewTargetLocation($new_rel_location)
308
  {
309
  $uploadDir = wp_upload_dir();
310
  $newPath = trailingslashit($uploadDir['basedir']) . $new_rel_location;
311
 
 
 
 
 
 
 
 
312
  if (! is_dir($newPath))
313
  {
314
  Notices::addError(__('Specificed new directory does not exist. Path must be a relative path from the upload directory and exist', 'enable-media-replace'));
@@ -324,14 +350,14 @@ class Replacer
324
  $targetPath = null;
325
  if ($this->replaceMode == self::MODE_REPLACE)
326
  {
327
- $targetFile = $this->sourceFile->getFullFilePath(); // overwrite source
328
  }
329
  elseif ($this->replaceMode == self::MODE_SEARCHREPLACE)
330
  {
331
- $path = $this->sourceFile->getFilePath();
332
  if ($this->target_location) // Replace to another path.
333
  {
334
- $otherTarget = new File($this->target_location . $this->targetName);
335
  if ($otherTarget->exists())
336
  {
337
  Notices::addError(__('In specificied directory there is already a file with the same name. Can\'t replace.', 'enable-media-replace'));
@@ -343,7 +369,7 @@ class Replacer
343
  $targetpath = $path . $this->targetName;
344
 
345
  // If the source and target path AND filename are identical, user has wrong mode, just overwrite the sourceFile.
346
- if ($targetpath == $this->sourceFile->getFullFilePath())
347
  {
348
  $unique = $this->sourceFile->getFileName();
349
  $this->replaceMode == self::MODE_REPLACE;
@@ -390,7 +416,7 @@ class Replacer
390
  if (strpos($url, '://') === false)
391
  {
392
  $uploads = wp_get_upload_dir();
393
- $url = str_replace($uploads['basedir'], $uploads['baseurl'], $this->targetFile->getFullFilePath());
394
  }
395
  // This can happen when WordPress is not taking from attached file, but wrong /old GUID. Try to replace it to the new one.
396
  elseif ($this->targetFile->getFileName() != $url_basename)
@@ -412,12 +438,14 @@ class Replacer
412
  $backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
413
 
414
  // 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.
415
- $file = $this->sourceFile->getFullFilePath();
416
  $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $file );
417
 
418
  // If Attached file is not the same path as file, this indicates a -scaled images is in play.
 
 
419
  $attached_file = get_attached_file($this->post_id);
420
- if ($file !== $attached_file && file_exists($attached_file))
421
  {
422
  @unlink($attached_file);
423
  }
@@ -466,8 +494,9 @@ class Replacer
466
  $base_url = str_replace('.' . pathinfo($base_url, PATHINFO_EXTENSION), '', $base_url);
467
 
468
 
 
469
  /** Fail-safe if base_url is a whole directory, don't go search/replace */
470
- if (is_dir($base_url))
471
  {
472
  Log::addError('Search Replace tried to replace to directory - ' . $base_url);
473
  Notices::addError(__('Fail Safe :: Source Location seems to be a directory.', 'enable-media-replace'));
@@ -603,13 +632,9 @@ class Replacer
603
 
604
  if ($replaced_content !== $post_content)
605
  {
606
- //Log::addDebug('POST CONTENT TO SAVE', $replaced_content);
607
-
608
- // $result = wp_update_post($post_ar);
609
  $sql = 'UPDATE ' . $wpdb->posts . ' SET post_content = %s WHERE ID = %d';
610
  $sql = $wpdb->prepare($sql, $replaced_content, $post_id);
611
 
612
- //Log::addTemp("POSt update query " . $sql);
613
  $result = $wpdb->query($sql);
614
 
615
  if ($result === false)
@@ -630,7 +655,7 @@ class Replacer
630
  {
631
  global $wpdb;
632
 
633
- $meta_options = apply_filters('emr/metadata_tables', array('post', 'comment', 'term', 'user'));
634
  $number_of_updates = 0;
635
 
636
  foreach($meta_options as $type)
@@ -644,6 +669,13 @@ class Replacer
644
 
645
  $update_sql = ' UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d';
646
  break;
 
 
 
 
 
 
 
647
  default:
648
  $table = $wpdb->{$type . 'meta'}; // termmeta, commentmeta etc
649
 
@@ -651,6 +683,7 @@ class Replacer
651
  if ($type == 'user')
652
  $meta_id = 'umeta_id';
653
 
 
654
  $sql = 'SELECT ' . $meta_id . ' as id, meta_value FROM ' . $table . '
655
  WHERE meta_value like %s';
656
 
@@ -659,6 +692,10 @@ class Replacer
659
  }
660
 
661
  $sql = $wpdb->prepare($sql, '%' . $url . '%');
 
 
 
 
662
 
663
  // 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.
664
  // $postmeta_sql = str_replace('[JSON_URL]', $json_url, $postmeta_sql);
@@ -681,6 +718,9 @@ class Replacer
681
  Log::addDebug('Update Meta SQl' . $prepared_sql);
682
  $result = $wpdb->query($prepared_sql);
683
 
 
 
 
684
  }
685
  }
686
  } // foreach
@@ -734,10 +774,11 @@ class Replacer
734
  }
735
  }
736
  }
737
- elseif(is_object($content)) // metadata objects, they exist.
738
  {
739
  foreach($content as $key => $value)
740
  {
 
741
  $content->{$key} = $this->replaceContent($value, $search, $replace, true); //str_replace($value, $search, $replace);
742
  }
743
  }
62
  }
63
 
64
  Log::addDebug('SourceFile ' . $source_file);
65
+ $this->sourceFile = $this->fs()->getFile($source_file);
66
 
67
  $this->source_post = get_post($post_id);
68
  $this->source_is_image = wp_attachment_is('image', $this->source_post);
82
  //$this->ThumbnailUpdater->setOldMetadata($this->source_metadata);
83
  }
84
 
85
+ private function fs()
86
+ {
87
+ return emr()->filesystem();
88
+ }
89
+
90
  public function setMode($mode)
91
  {
92
  $this->replaceMode = $mode;
112
  $this->targetName = $fileName;
113
 
114
  $targetFile = $this->getTargetFile();
115
+ $fs = $this->fs();
116
 
117
  if (is_null($targetFile))
118
  {
121
  // throw new \RuntimeException($ex);
122
  }
123
 
124
+ $targetFileObj = $fs->getFile($targetFile);
125
+ $directoryObj = $targetFileObj->getFileDir();
126
+ $result = $directoryObj->check();
127
+
128
  if ($result === false)
129
  Log::addError('Directory creation for targetFile failed');
130
 
131
+ $permissions = $this->sourceFile->getPermissions();
132
 
133
  $this->removeCurrent(); // tries to remove the current files.
 
134
  /* @todo See if wp_handle_sideload / wp_handle_upload can be more securely used for this */
135
+ // @todo Use FS / File copy for this.
136
+
137
+ $fileObj = $fs->getFile($file);
138
+ $result_moved = $fileObj->move($targetFileObj);
139
+
140
+ if (false === $result_moved)
141
+ {
142
+ $ex = sprintf( esc_html__('The uploaded file could not be moved to %1$s. This is most likely an issue with permissions, or upload failed.', "enable-media-replace"), $targetFile );
143
+ throw new \RuntimeException($ex);
144
+ }
145
+
146
 
 
 
 
 
 
147
 
148
  // init targetFile.
149
+ $this->targetFile = $fs->getFile($targetFile);
150
 
151
+ if ($permissions > 0)
152
+ chmod( $targetFile, $permissions ); // restore permissions
153
  else {
154
  Log::addWarn('Setting permissions failed');
155
  }
156
 
157
  // update the file attached. This is required for wp_get_attachment_url to work.
158
+ $updated = update_attached_file($this->post_id, $this->targetFile->getFullPath() );
159
  if (! $updated)
160
  Log::addError('Update Attached File reports as not updated or same value');
161
 
163
 
164
  // Run the filter, so other plugins can hook if needed.
165
  $filtered = apply_filters( 'wp_handle_upload', array(
166
+ 'file' => $this->targetFile->getFullPath(),
167
  'url' => $this->target_url,
168
+ 'type' => $this->targetFile->getMime(),
169
  ), 'sideload');
170
 
171
  // check if file changed during filter. Set changed to attached file meta properly.
172
+ if (isset($filtered['file']) && $filtered['file'] != $this->targetFile->getFullPath() )
173
  {
174
  update_attached_file($this->post_id, $filtered['file'] );
175
+ $this->targetFile = $this->fs()->getFile($filtered['file']); // handle as a new file
176
  Log::addInfo('WP_Handle_upload filter returned different file', $filtered);
177
  }
178
 
179
  // Check and update post mimetype, otherwise badly coded plugins cry.
180
  $post_mime = get_post_mime_type($this->post_id);
181
+ $target_mime = $this->targetFile->getMime();
182
 
183
  // update DB post mime type, if somebody decided to mess it up, and the target one is not empty.
184
  if ($target_mime !== $post_mime && strlen($target_mime) > 0)
185
  {
186
 
187
+ \wp_update_post(array('post_mime_type' => $this->targetFile->getMime(), 'ID' => $this->post_id));
188
  }
189
 
190
+ $metadata = wp_generate_attachment_metadata( $this->post_id, $this->targetFile->getFullPath() );
191
  wp_update_attachment_metadata( $this->post_id, $metadata );
192
  $this->target_metadata = $metadata;
193
 
272
  protected function getNewTitle()
273
  {
274
  // get basename without extension
275
+ $title = basename($this->targetFile->getFileName(), '.' . $this->targetFile->getExtension());
276
  $meta = $this->target_metadata;
277
 
278
  if (isset($meta['image_meta']))
317
  return $this->sourceFile;
318
  }
319
 
320
+
321
+ public function getSourceUrl()
322
+ {
323
+ return $this->source_url;
324
+ }
325
+
326
  public function setNewTargetLocation($new_rel_location)
327
  {
328
  $uploadDir = wp_upload_dir();
329
  $newPath = trailingslashit($uploadDir['basedir']) . $new_rel_location;
330
 
331
+ // Detect traversal by making sure the canonical path starts with uploads' basedir.
332
+ if (($newPath = realpath($newPath)) && strpos($newPath, $uploadDir['basedir']) !== 0)
333
+ {
334
+ Notices::addError(__('Specificed directory is outside the upload directory. This is not allowed for security reasons', 'enable-media-replace'));
335
+ return false;
336
+ }
337
+
338
  if (! is_dir($newPath))
339
  {
340
  Notices::addError(__('Specificed new directory does not exist. Path must be a relative path from the upload directory and exist', 'enable-media-replace'));
350
  $targetPath = null;
351
  if ($this->replaceMode == self::MODE_REPLACE)
352
  {
353
+ $targetFile = $this->sourceFile->getFullPath(); // overwrite source
354
  }
355
  elseif ($this->replaceMode == self::MODE_SEARCHREPLACE)
356
  {
357
+ $path = (string) $this->sourceFile->getFileDir();
358
  if ($this->target_location) // Replace to another path.
359
  {
360
+ $otherTarget = $this->fs()->getFile($this->target_location . $this->targetName);
361
  if ($otherTarget->exists())
362
  {
363
  Notices::addError(__('In specificied directory there is already a file with the same name. Can\'t replace.', 'enable-media-replace'));
369
  $targetpath = $path . $this->targetName;
370
 
371
  // If the source and target path AND filename are identical, user has wrong mode, just overwrite the sourceFile.
372
+ if ($targetpath == $this->sourceFile->getFullPath())
373
  {
374
  $unique = $this->sourceFile->getFileName();
375
  $this->replaceMode == self::MODE_REPLACE;
416
  if (strpos($url, '://') === false)
417
  {
418
  $uploads = wp_get_upload_dir();
419
+ $url = str_replace($uploads['basedir'], $uploads['baseurl'], $this->targetFile->getFullPath());
420
  }
421
  // This can happen when WordPress is not taking from attached file, but wrong /old GUID. Try to replace it to the new one.
422
  elseif ($this->targetFile->getFileName() != $url_basename)
438
  $backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
439
 
440
  // 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.
441
+ $file = $this->sourceFile->getFullPath();
442
  $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $file );
443
 
444
  // If Attached file is not the same path as file, this indicates a -scaled images is in play.
445
+ // Also plugins like Polylang tend to block delete image while there is translation / duplicate item somewhere
446
+ // 10/06/22 : Added a hard delete if file still exists. Be gone, hard way.
447
  $attached_file = get_attached_file($this->post_id);
448
+ if (file_exists($attached_file))
449
  {
450
  @unlink($attached_file);
451
  }
494
  $base_url = str_replace('.' . pathinfo($base_url, PATHINFO_EXTENSION), '', $base_url);
495
 
496
 
497
+ $abspath = $this->fs()->getWPAbsPath();
498
  /** Fail-safe if base_url is a whole directory, don't go search/replace */
499
+ if (strpos($abspath, $base_url) === 0 && is_dir($base_url))
500
  {
501
  Log::addError('Search Replace tried to replace to directory - ' . $base_url);
502
  Notices::addError(__('Fail Safe :: Source Location seems to be a directory.', 'enable-media-replace'));
632
 
633
  if ($replaced_content !== $post_content)
634
  {
 
 
 
635
  $sql = 'UPDATE ' . $wpdb->posts . ' SET post_content = %s WHERE ID = %d';
636
  $sql = $wpdb->prepare($sql, $replaced_content, $post_id);
637
 
 
638
  $result = $wpdb->query($sql);
639
 
640
  if ($result === false)
655
  {
656
  global $wpdb;
657
 
658
+ $meta_options = apply_filters('emr/metadata_tables', array('post', 'comment', 'term', 'user', 'options'));
659
  $number_of_updates = 0;
660
 
661
  foreach($meta_options as $type)
669
 
670
  $update_sql = ' UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d';
671
  break;
672
+ case "options": // basked case (for guten widgets).
673
+ $sql = 'SELECT option_id as id, option_name, option_value as meta_value FROM ' . $wpdb->options . '
674
+ WHERE option_value like %s and option_name = "widget_block" ';
675
+ $type = 'option';
676
+
677
+ $update_sql = ' UPDATE ' . $wpdb->options . ' SET option_value = %s WHERE option_id = %d';
678
+ break;
679
  default:
680
  $table = $wpdb->{$type . 'meta'}; // termmeta, commentmeta etc
681
 
683
  if ($type == 'user')
684
  $meta_id = 'umeta_id';
685
 
686
+
687
  $sql = 'SELECT ' . $meta_id . ' as id, meta_value FROM ' . $table . '
688
  WHERE meta_value like %s';
689
 
692
  }
693
 
694
  $sql = $wpdb->prepare($sql, '%' . $url . '%');
695
+ Log::addTemp('Handle MEta SQL ' . $sql);
696
+
697
+ if ($wpdb->last_error)
698
+ Log::addWarn('Error' . $wpdb->last_error, $wpdb->last_query);
699
 
700
  // 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.
701
  // $postmeta_sql = str_replace('[JSON_URL]', $json_url, $postmeta_sql);
718
  Log::addDebug('Update Meta SQl' . $prepared_sql);
719
  $result = $wpdb->query($prepared_sql);
720
 
721
+ if ($wpdb->last_error)
722
+ Log::addWarn('Error' . $wpdb->last_error, $wpdb->last_query);
723
+
724
  }
725
  }
726
  } // foreach
774
  }
775
  }
776
  }
777
+ elseif(is_object($content) && '__PHP_Incomplete_Class' !== get_class($content)) // metadata objects, they exist. prevent incomplete classes from deactivated plugins or whatever. They crash.
778
  {
779
  foreach($content as $key => $value)
780
  {
781
+
782
  $content->{$key} = $this->replaceContent($value, $search, $replace, true); //str_replace($value, $search, $replace);
783
  }
784
  }
classes/uihelper.php CHANGED
@@ -10,24 +10,41 @@ class UIHelper
10
  protected $preview_width = 0;
11
  protected $preview_height = 0;
12
 
13
- protected $preview_max_height = 500;
14
- protected $preview_max_width = 400;
15
 
16
  protected $full_width = 0;
17
  protected $full_height = 0;
18
 
 
 
 
 
19
  public function __construct()
20
  {
21
 
22
  }
23
 
24
- public function getFormUrl($attach_id)
 
 
 
 
 
 
 
 
 
 
 
25
  {
 
 
26
  $url = admin_url('upload.php');
27
  $url = add_query_arg(array(
28
  'page' => 'enable-media-replace/enable-media-replace.php',
29
  'noheader' => true,
30
- 'action' => 'media_replace_upload',
31
  'attachment_id' => $attach_id,
32
  ));
33
 
@@ -91,6 +108,7 @@ class UIHelper
91
 
92
  public function setPreviewSizes()
93
  {
 
94
  list($this->preview_size, $this->preview_width, $this->preview_height) = $this->findImageSizeByMax($this->preview_max_width);
95
  }
96
 
@@ -109,14 +127,15 @@ class UIHelper
109
  }
110
 
111
  // Returns Preview Image HTML Output.
112
- public function getPreviewImage($attach_id,$file)
113
  {
114
  $data = false;
115
 
116
  if ($attach_id > 0)
117
  {
118
  $data = $this->getImageSizes($attach_id, $this->preview_size); //wp_get_attachment_image_src($attach_id, $this->preview_size);
119
- /*$file = get_attached_file($attach_id);
 
120
 
121
  // If the file is relative, prepend upload dir.
122
  if (! file_exists($file) && $file && 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) )
@@ -126,7 +145,6 @@ class UIHelper
126
  $file = $uploads['basedir'] . "/$file";
127
  }
128
  */
129
- Log::addDebug('Attached File ' . $file->getFullFilePath(), $data);
130
  }
131
 
132
  $mime_type = get_post_mime_type($attach_id);
@@ -137,7 +155,7 @@ class UIHelper
137
  $icon = ($attach_id < 0) ? '' : 'dashicons-no';
138
  $is_document = false;
139
 
140
- $args = array(
141
  'width' => $this->preview_width,
142
  'height' => $this->preview_height,
143
  'is_image' => false,
@@ -146,7 +164,7 @@ class UIHelper
146
  'mime_type' => null,
147
  );
148
 
149
-
150
 
151
  // failed, it might be this server doens't support PDF thumbnails. Fallback to File preview.
152
  if ($mime_type == 'application/pdf')
@@ -157,26 +175,40 @@ class UIHelper
157
  return $this->getPlaceHolder($args);
158
  }
159
 
160
-
161
  $url = $data[0];
162
  $width = $data[1];
163
  $height = $data[2];
164
 
165
- // SVG's without any helpers return around 0 for width / height. Fix preview.
 
 
 
 
 
 
 
 
 
 
 
166
 
167
- // preview width, if source if found, should be set to source.
168
- $this->preview_width = $width;
169
- $this->preview_height = $height;
 
 
 
 
 
170
 
171
 
172
- if ($width > $this->preview_max_width)
173
- $width = $this->preview_max_width;
174
- if ($height > $this->preview_max_height)
175
- $height = $this->preview_max_height;
176
 
177
  $image = "<img src='$url' width='$width' height='$height' class='image' style='max-width:100%; max-height: 100%;' />";
178
 
179
- $args = array(
180
  'width' => $width,
181
  'height' => $height,
182
  'image' => $image,
@@ -184,16 +216,44 @@ class UIHelper
184
  'file_size' => $file->getFileSize(),
185
  );
186
 
 
 
187
  $output = $this->getPlaceHolder($args);
188
  return $output;
189
  }
190
 
191
  protected function getImageSizes($attach_id, $size = 'thumbnail')
192
  {
193
- $data = wp_get_attachment_image_src($attach_id, $size);
194
- $width = isset($data[1]) ? $data[1] : 0;
195
- //$mime_type = get_post_mime_type($attach_id);
196
- $file = get_attached_file($attach_id);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  $mime_type = wp_get_image_mime($file);
198
 
199
  if (strpos($mime_type, 'svg') !== false && $width <= 5)
@@ -202,6 +262,7 @@ class UIHelper
202
  $data = $this->fixSVGSize($data, $file);
203
  }
204
 
 
205
  return $data;
206
  }
207
 
@@ -246,7 +307,7 @@ class UIHelper
246
  $filename = false;
247
  }
248
 
249
- $mime_type = $file->getFileMime();
250
 
251
  $args = array(
252
  'width' => 300,
@@ -263,7 +324,7 @@ class UIHelper
263
 
264
  public function findImageSizeByMax($maxwidth)
265
  {
266
- $image_sizes = $this->get_image_sizes();
267
 
268
  $match_width = 0;
269
  $match_height = 0;
@@ -295,6 +356,8 @@ class UIHelper
295
  'is_document' => false,
296
  'mime_type' => false,
297
  'file_size' => false,
 
 
298
  );
299
 
300
  $args = wp_parse_args($args, $defaults);
@@ -330,11 +393,13 @@ class UIHelper
330
 
331
  $filesize = ($args['file_size']) ? $args['file_size'] : '';
332
 
 
333
 
334
  $output = "<div class='image_placeholder $placeholder_class' $filetype style='width:" . $w . "px; height:". $h ."px'> ";
335
  $output .= $args['image'];
336
  $output .= "<div class='dashicons $icon'>&nbsp;</div>";
337
  $output .= "<span class='textlayer'>" . $args['layer'] . "</span>";
 
338
  $output .= "<div class='image_size'>" . $this->convertFileSize($filesize). "</div>";
339
  $output .= "</div>";
340
 
@@ -343,6 +408,35 @@ class UIHelper
343
  return $output;
344
  }
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  private function convertFileSize($filesize)
347
  {
348
  return size_format($filesize);
@@ -355,7 +449,7 @@ class UIHelper
355
  * @uses get_intermediate_image_sizes()
356
  * @return array $sizes Data for all currently-registered image sizes.
357
  */
358
- private function get_image_sizes() {
359
  global $_wp_additional_image_sizes;
360
 
361
  $sizes = array();
@@ -385,4 +479,14 @@ class UIHelper
385
  return false;
386
  }
387
 
 
 
 
 
 
 
 
 
 
 
388
  } // class
10
  protected $preview_width = 0;
11
  protected $preview_height = 0;
12
 
13
+ protected $preview_max_width = 600;
14
+ protected $preview_max_height = 600;
15
 
16
  protected $full_width = 0;
17
  protected $full_height = 0;
18
 
19
+ private static $instance;
20
+
21
+ const NOTICE_NEW_FEATURE = 'EMR001';
22
+
23
  public function __construct()
24
  {
25
 
26
  }
27
 
28
+ public static function getInstance()
29
+ {
30
+ if (is_null(self::$instance))
31
+ {
32
+ self::$instance = new UiHelper();
33
+ }
34
+
35
+ return self::$instance;
36
+ }
37
+
38
+ // @todo Add nonce URL to this url as well, in popup / prepare-remove-background
39
+ public function getFormUrl($attach_id, $action = null)
40
  {
41
+ $action = (! is_null($action)) ? $action : 'media_replace_upload';
42
+
43
  $url = admin_url('upload.php');
44
  $url = add_query_arg(array(
45
  'page' => 'enable-media-replace/enable-media-replace.php',
46
  'noheader' => true,
47
+ 'action' => $action,
48
  'attachment_id' => $attach_id,
49
  ));
50
 
108
 
109
  public function setPreviewSizes()
110
  {
111
+
112
  list($this->preview_size, $this->preview_width, $this->preview_height) = $this->findImageSizeByMax($this->preview_max_width);
113
  }
114
 
127
  }
128
 
129
  // Returns Preview Image HTML Output.
130
+ public function getPreviewImage($attach_id,$file, $args = array())
131
  {
132
  $data = false;
133
 
134
  if ($attach_id > 0)
135
  {
136
  $data = $this->getImageSizes($attach_id, $this->preview_size); //wp_get_attachment_image_src($attach_id, $this->preview_size);
137
+ /*$file = get_attached_file($attach_id);
138
+
139
 
140
  // If the file is relative, prepend upload dir.
141
  if (! file_exists($file) && $file && 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) )
145
  $file = $uploads['basedir'] . "/$file";
146
  }
147
  */
 
148
  }
149
 
150
  $mime_type = get_post_mime_type($attach_id);
155
  $icon = ($attach_id < 0) ? '' : 'dashicons-no';
156
  $is_document = false;
157
 
158
+ $defaults = array(
159
  'width' => $this->preview_width,
160
  'height' => $this->preview_height,
161
  'is_image' => false,
164
  'mime_type' => null,
165
  );
166
 
167
+ $args = wp_parse_args($args, $defaults);
168
 
169
  // failed, it might be this server doens't support PDF thumbnails. Fallback to File preview.
170
  if ($mime_type == 'application/pdf')
175
  return $this->getPlaceHolder($args);
176
  }
177
 
 
178
  $url = $data[0];
179
  $width = $data[1];
180
  $height = $data[2];
181
 
182
+ // width
183
+ $width_ratio = $height_ratio = 0;
184
+ if ($width > $this->preview_max_width)
185
+ {
186
+ $width_ratio = $width / $this->preview_max_width;
187
+ }
188
+ if ($height > $this->preview_max_height) // height
189
+ {
190
+ $height_ratio = $height / $this->preview_max_height;
191
+ }
192
+
193
+ $ratio = ($width_ratio > $height_ratio) ? $width_ratio : $height_ratio;
194
 
195
+ if ($ratio > 0)
196
+ {
197
+
198
+ $width = floor($width / $ratio);
199
+ $height = floor($height / $ratio);
200
+ }
201
+
202
+ // SVG's without any helpers return around 0 for width / height. Fix preview.
203
 
204
 
205
+ // preview width, if source if found, should be set to source.
206
+ $this->preview_width = $width;
207
+ $this->preview_height = $height;
 
208
 
209
  $image = "<img src='$url' width='$width' height='$height' class='image' style='max-width:100%; max-height: 100%;' />";
210
 
211
+ $defaults = array(
212
  'width' => $width,
213
  'height' => $height,
214
  'image' => $image,
216
  'file_size' => $file->getFileSize(),
217
  );
218
 
219
+ $args = wp_parse_args($args, $defaults);
220
+
221
  $output = $this->getPlaceHolder($args);
222
  return $output;
223
  }
224
 
225
  protected function getImageSizes($attach_id, $size = 'thumbnail')
226
  {
227
+ // 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
228
+ // $data = wp_get_attachment_image_src($attach_id, $size);
229
+ $meta = wp_get_attachment_metadata($attach_id);
230
+
231
+ $data = false;
232
+
233
+ if (isset($meta['sizes']))
234
+ {
235
+ foreach($meta['sizes'] as $sizeName => $metaData)
236
+ {
237
+ if ($sizeName == $size)
238
+ {
239
+ $width = isset($metaData['width']) ? $metaData['width'] : 0;
240
+ $height = isset($metaData['height']) ? $metaData['height'] : 0;
241
+ $imgData = image_downsize($attach_id, $size); // return whole array w/ possible wrong dimensions.
242
+ $data = array($imgData[0], $width, $height);
243
+ }
244
+ }
245
+ }
246
+
247
+ if ($data === false)
248
+ {
249
+ $data = wp_get_attachment_image_src($attach_id, $size);
250
+ $width = isset($data[1]) ? $data[1] : 0;
251
+ }
252
+
253
+ $file = get_attached_file($attach_id, true);
254
+ if (! file_exists($file))
255
+ return $data;
256
+
257
  $mime_type = wp_get_image_mime($file);
258
 
259
  if (strpos($mime_type, 'svg') !== false && $width <= 5)
262
  $data = $this->fixSVGSize($data, $file);
263
  }
264
 
265
+
266
  return $data;
267
  }
268
 
307
  $filename = false;
308
  }
309
 
310
+ $mime_type = $file->getMime();
311
 
312
  $args = array(
313
  'width' => 300,
324
 
325
  public function findImageSizeByMax($maxwidth)
326
  {
327
+ $image_sizes = $this->wp_get_image_sizes();
328
 
329
  $match_width = 0;
330
  $match_height = 0;
356
  'is_document' => false,
357
  'mime_type' => false,
358
  'file_size' => false,
359
+ 'remove_bg_ui' => false, // In process icons et al when removing background, for preview pane.
360
+
361
  );
362
 
363
  $args = wp_parse_args($args, $defaults);
393
 
394
  $filesize = ($args['file_size']) ? $args['file_size'] : '';
395
 
396
+ $background_remove_ui = (isset($args['remove_bg_ui']) && $args['remove_bg_ui'] == true) ? $this->getBgremoveUI() : '';
397
 
398
  $output = "<div class='image_placeholder $placeholder_class' $filetype style='width:" . $w . "px; height:". $h ."px'> ";
399
  $output .= $args['image'];
400
  $output .= "<div class='dashicons $icon'>&nbsp;</div>";
401
  $output .= "<span class='textlayer'>" . $args['layer'] . "</span>";
402
+ $output .= $background_remove_ui;
403
  $output .= "<div class='image_size'>" . $this->convertFileSize($filesize). "</div>";
404
  $output .= "</div>";
405
 
408
  return $output;
409
  }
410
 
411
+ public function isBackgroundRemovable($post)
412
+ {
413
+ if (false === wp_attachment_is_image($post))
414
+ return false;
415
+
416
+ $extensions = array('jpg', 'png','jpeg');
417
+
418
+ $mime = get_post_mime_type($post);
419
+ foreach($extensions as $extension)
420
+ {
421
+ if (strpos($mime, $extension) !== false )
422
+ return true;
423
+ }
424
+
425
+ return false;
426
+
427
+ }
428
+
429
+ private function getBgremoveUI()
430
+ {
431
+ $output = '<div class="overlay" id="overlay">
432
+ <div class="lds-spinner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>';
433
+
434
+ $output .= '<h3>' . esc_html__('Removing background...', 'enable-media-replace') . '</h3>';
435
+ $output .= '</div>';
436
+
437
+ return $output;
438
+ }
439
+
440
  private function convertFileSize($filesize)
441
  {
442
  return size_format($filesize);
449
  * @uses get_intermediate_image_sizes()
450
  * @return array $sizes Data for all currently-registered image sizes.
451
  */
452
+ private function wp_get_image_sizes() {
453
  global $_wp_additional_image_sizes;
454
 
455
  $sizes = array();
479
  return false;
480
  }
481
 
482
+ public function featureNotice()
483
+ {
484
+ // @todo Remove in 2023.
485
+ $message = sprintf(__('%s New Beta Feature! %s %s Enable Media Replace now gives you the ability to remove the background of any image. Try it out in the Media Library: hover over an image and click on Remove Background. Or just click on Remove background from the image editing window! %s ', 'enable-media-replace' ), '<h3>', '</h3>',
486
+ '<p>', '</p>');
487
+
488
+ $notice = Notices::addNormal($message, true);
489
+ Notices::makePersistent($notice, self::NOTICE_NEW_FEATURE, 2 * YEAR_IN_SECONDS);
490
+ }
491
+
492
  } // class
css/admin.css CHANGED
@@ -990,6 +990,9 @@
990
  .emr_upload_form form .upsell-wrapper {
991
  margin-left: 10px;
992
  }
 
 
 
993
  .emr_upload_form .wrapper {
994
  padding: 18px;
995
  border: 1px solid #ccc;
@@ -1052,7 +1055,6 @@
1052
  margin-bottom: 10px;
1053
  border: 1px solid #ddd;
1054
  vertical-align: top;
1055
- max-height: 500px;
1056
  }
1057
  .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .textlayer {
1058
  font-size: 25px;
990
  .emr_upload_form form .upsell-wrapper {
991
  margin-left: 10px;
992
  }
993
+ .emr_upload_form .editor-wrapper {
994
+ width: 100%;
995
+ }
996
  .emr_upload_form .wrapper {
997
  padding: 18px;
998
  border: 1px solid #ccc;
1055
  margin-bottom: 10px;
1056
  border: 1px solid #ddd;
1057
  vertical-align: top;
 
1058
  }
1059
  .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .textlayer {
1060
  font-size: 25px;
css/admin.css.map CHANGED
@@ -1 +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;;AAIJ;EAGE;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;EACA;;AACA;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;;AAEH;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;;AAS/C;EAEE;;AAIF;EAEI;IAEG;;EAEH;IACE;;EACA;IACE;;;AAGR;EAEE;IAAkB;;;AAErB;EAEE;IACE;IACA;IACA;;EAIA;IACE;IACD","file":"admin.css"}
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;;AAEH;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;;AAS/C;EAEE;;AAIF;EAEI;IAEG;;EAEH;IACE;;EACA;IACE;;;AAGR;EAEE;IAAkB;;;AAErB;EAEE;IACE;IACA;IACA;;EAIA;IACE;IACD","file":"admin.css"}
css/remove_background.css ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #remove-background-form form {
2
+ display: flex;
3
+ }
4
+ #remove-background-form .image_chooser.wrapper {
5
+ min-height: 0;
6
+ }
7
+ #remove-background-form .image_placeholder.is_image {
8
+ border: none;
9
+ width: 45%;
10
+ height: auto;
11
+ }
12
+ #remove-background-form .image_placeholder.is_image .textlayer, #remove-background-form .image_placeholder.is_image .image_size {
13
+ display: none;
14
+ }
15
+ #remove-background-form .bad-button {
16
+ text-align: right;
17
+ margin-right: 25px;
18
+ }
19
+ #remove-background-form .bad-button a {
20
+ visibility: hidden;
21
+ }
22
+ #remove-background-form .overlay {
23
+ visibility: hidden;
24
+ display: flex;
25
+ flex-direction: column;
26
+ justify-content: center;
27
+ align-items: center;
28
+ width: 100%;
29
+ height: 100%;
30
+ background-color: rgba(0, 0, 0, 0.2);
31
+ position: absolute;
32
+ top: 0;
33
+ }
34
+ #remove-background-form .lds-spinner {
35
+ color: official;
36
+ display: inline-block;
37
+ position: relative;
38
+ width: 80px;
39
+ height: 80px;
40
+ }
41
+ #remove-background-form .lds-spinner div {
42
+ transform-origin: 40px 40px;
43
+ animation: lds-spinner 1.2s linear infinite;
44
+ }
45
+ #remove-background-form .lds-spinner div:after {
46
+ content: " ";
47
+ display: block;
48
+ position: absolute;
49
+ top: 3px;
50
+ left: 37px;
51
+ width: 6px;
52
+ height: 18px;
53
+ border-radius: 20%;
54
+ background: #fff;
55
+ }
56
+ #remove-background-form .lds-spinner div:nth-child(1) {
57
+ transform: rotate(0deg);
58
+ animation-delay: -1.1s;
59
+ }
60
+ #remove-background-form .lds-spinner div:nth-child(2) {
61
+ transform: rotate(30deg);
62
+ animation-delay: -1s;
63
+ }
64
+ #remove-background-form .lds-spinner div:nth-child(3) {
65
+ transform: rotate(60deg);
66
+ animation-delay: -0.9s;
67
+ }
68
+ #remove-background-form .lds-spinner div:nth-child(4) {
69
+ transform: rotate(90deg);
70
+ animation-delay: -0.8s;
71
+ }
72
+ #remove-background-form .lds-spinner div:nth-child(5) {
73
+ transform: rotate(120deg);
74
+ animation-delay: -0.7s;
75
+ }
76
+ #remove-background-form .lds-spinner div:nth-child(6) {
77
+ transform: rotate(150deg);
78
+ animation-delay: -0.6s;
79
+ }
80
+ #remove-background-form .lds-spinner div:nth-child(7) {
81
+ transform: rotate(180deg);
82
+ animation-delay: -0.5s;
83
+ }
84
+ #remove-background-form .lds-spinner div:nth-child(8) {
85
+ transform: rotate(210deg);
86
+ animation-delay: -0.4s;
87
+ }
88
+ #remove-background-form .lds-spinner div:nth-child(9) {
89
+ transform: rotate(240deg);
90
+ animation-delay: -0.3s;
91
+ }
92
+ #remove-background-form .lds-spinner div:nth-child(10) {
93
+ transform: rotate(270deg);
94
+ animation-delay: -0.2s;
95
+ }
96
+ #remove-background-form .lds-spinner div:nth-child(11) {
97
+ transform: rotate(300deg);
98
+ animation-delay: -0.1s;
99
+ }
100
+ #remove-background-form .lds-spinner div:nth-child(12) {
101
+ transform: rotate(330deg);
102
+ animation-delay: 0s;
103
+ }
104
+ @keyframes lds-spinner {
105
+ 0% {
106
+ opacity: 1;
107
+ }
108
+ 100% {
109
+ opacity: 0;
110
+ }
111
+ }
112
+ #remove-background-form * {
113
+ box-sizing: border-box;
114
+ }
115
+ #remove-background-form .img-comp-container {
116
+ position: relative;
117
+ height: 200px;
118
+ /*should be the same height as the images*/
119
+ }
120
+ #remove-background-form .img-comp-img {
121
+ position: absolute;
122
+ width: auto;
123
+ height: auto;
124
+ overflow: hidden;
125
+ }
126
+ #remove-background-form .img-comp-img img {
127
+ display: block;
128
+ }
129
+ #remove-background-form .img-comp-slider {
130
+ position: absolute;
131
+ z-index: 9;
132
+ cursor: ew-resize;
133
+ /*set the appearance of the slider:*/
134
+ width: 20px;
135
+ height: 20px;
136
+ background-color: #2196F3;
137
+ opacity: 0.7;
138
+ border-radius: 50%;
139
+ }
140
+ #remove-background-form .preview-area {
141
+ display: flex;
142
+ justify-content: center;
143
+ align-items: center;
144
+ width: 100%;
145
+ height: 100%;
146
+ }
147
+ #remove-background-form .preview-area h1 {
148
+ color: red !important;
149
+ }
150
+
151
+ /*# sourceMappingURL=remove_background.css.map */
css/remove_background.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../scss/remove_background.scss"],"names":[],"mappings":"AAEG;EACA;;AAGD;EACE;;AAEF;EAEE;EACA;EACA;;AACA;EACE;;AAIJ;EAEE;EACA;;AACA;EACC;;AAIH;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACE;EACA;;AAEF;EACC;IACC;;EAED;IACC;;;AAMH;EACC;;AAGD;EACC;EACA;AAAe;;AAGhB;EACC;EACA;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;AACA;EACA;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;;AAED;EACC","file":"remove_background.css"}
enable-media-replace.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Enable Media Replace
4
  * Plugin URI: https://wordpress.org/plugins/enable-media-replace/
5
  * Description: Enable replacing media files by uploading a new file in the "Edit Media" section of the WordPress Media Library.
6
- * Version: 3.6.3
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * GitHub Plugin URI: https://github.com/short-pixel-optimizer/enable-media-replace
@@ -20,14 +20,14 @@
20
  *
21
  * @author ShortPixel <https://shortpixel.com>
22
  * @copyright ShortPixel 2018-2020
23
- * @package wordpress
24
  * @subpackage enable-media-replace
25
  *
26
  */
27
 
28
  namespace EnableMediaReplace;
29
 
30
- define('EMR_VERSION', '3.6.3');
31
 
32
  if ( ! defined( 'ABSPATH' ) ) {
33
  exit; // Exit if accessed directly.
@@ -35,15 +35,15 @@ if ( ! defined( 'ABSPATH' ) ) {
35
 
36
  /* Not sure why we define this?
37
  if(!defined("S3_UPLOADS_AUTOENABLE")) {
38
- define('S3_UPLOADS_AUTOENABLE', true);
39
  } */
40
 
41
- if (! defined("EMR_ROOT_FILE")) {
42
- define("EMR_ROOT_FILE", __FILE__);
43
  }
44
 
45
- if(!defined("SHORTPIXEL_AFFILIATE_CODE")) {
46
- define("SHORTPIXEL_AFFILIATE_CODE", 'VKG6LYN28044');
47
  }
48
 
49
  /** Usage:
@@ -55,34 +55,39 @@ if(!defined("SHORTPIXEL_AFFILIATE_CODE")) {
55
  *
56
  *
57
  **/
58
- if (! defined('EMR_CAPABILITY'))
59
- define('EMR_CAPABILITY', false);
 
60
 
61
  /* if (! defined('EMR_CAPABILITY_USERONLY'))
62
  define('EMR_CAPABILITY_USERONLY', false); */
63
 
64
- $plugin_path = plugin_dir_path(EMR_ROOT_FILE);
65
-
66
- require_once($plugin_path . 'build/shortpixel/autoload.php');
67
- require_once($plugin_path . 'classes/compat.php');
68
- require_once($plugin_path . 'classes/functions.php');
69
- require_once($plugin_path . 'classes/replacer.php');
70
- require_once($plugin_path . 'classes/uihelper.php');
71
- require_once($plugin_path . 'classes/file.php');
72
- require_once($plugin_path . 'classes/cache.php');
73
- require_once($plugin_path . 'classes/emr-plugin.php');
74
- require_once($plugin_path . 'classes/externals.php');
75
- require_once($plugin_path . 'classes/external/elementor.php');
76
- require_once($plugin_path . 'classes/external/wpbakery.php');
77
- require_once($plugin_path . 'classes/external/upsell_installer.php');
78
- require_once($plugin_path . 'thumbnail_updater.php');
79
 
80
- $emr_plugin = EnableMediaReplacePlugin::get();
 
 
 
 
 
 
 
 
 
 
81
 
82
- register_uninstall_hook(__FILE__, '\EnableMediaReplace\emr_uninstall');
 
 
 
 
83
 
84
- function emr_uninstall()
85
  {
86
- delete_option('enable_media_replace');
87
- delete_option('emr_news');
88
  }
 
 
 
 
 
3
  * Plugin Name: Enable Media Replace
4
  * Plugin URI: https://wordpress.org/plugins/enable-media-replace/
5
  * Description: Enable replacing media files by uploading a new file in the "Edit Media" section of the WordPress Media Library.
6
+ * Version: 4.0.0
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * GitHub Plugin URI: https://github.com/short-pixel-optimizer/enable-media-replace
20
  *
21
  * @author ShortPixel <https://shortpixel.com>
22
  * @copyright ShortPixel 2018-2020
23
+ * @package WordPress
24
  * @subpackage enable-media-replace
25
  *
26
  */
27
 
28
  namespace EnableMediaReplace;
29
 
30
+ define( 'EMR_VERSION', '4.0.0' );
31
 
32
  if ( ! defined( 'ABSPATH' ) ) {
33
  exit; // Exit if accessed directly.
35
 
36
  /* Not sure why we define this?
37
  if(!defined("S3_UPLOADS_AUTOENABLE")) {
38
+ define('S3_UPLOADS_AUTOENABLE', true);
39
  } */
40
 
41
+ if ( ! defined( 'EMR_ROOT_FILE' ) ) {
42
+ define( 'EMR_ROOT_FILE', __FILE__ );
43
  }
44
 
45
+ if ( ! defined( 'SHORTPIXEL_AFFILIATE_CODE' ) ) {
46
+ define( 'SHORTPIXEL_AFFILIATE_CODE', 'VKG6LYN28044' );
47
  }
48
 
49
  /** Usage:
55
  *
56
  *
57
  **/
58
+ if ( ! defined( 'EMR_CAPABILITY' ) ) {
59
+ define( 'EMR_CAPABILITY', false );
60
+ }
61
 
62
  /* if (! defined('EMR_CAPABILITY_USERONLY'))
63
  define('EMR_CAPABILITY_USERONLY', false); */
64
 
65
+ $plugin_path = plugin_dir_path( EMR_ROOT_FILE );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ require_once( $plugin_path . 'build/shortpixel/autoload.php' );
68
+ require_once( $plugin_path . 'classes/compat.php' );
69
+ require_once( $plugin_path . 'classes/functions.php' );
70
+ require_once( $plugin_path . 'classes/replacer.php' );
71
+ require_once( $plugin_path . 'classes/uihelper.php' );
72
+ //require_once( $plugin_path . 'classes/file.php' );
73
+ require_once( $plugin_path . 'classes/cache.php' );
74
+ require_once( $plugin_path . 'classes/api.php' );
75
+ require_once( $plugin_path . 'classes/ajax.php' );
76
+ require_once( $plugin_path . 'classes/emr-plugin.php' );
77
+ require_once( $plugin_path . 'classes/installHelper.php' );
78
 
79
+ require_once( $plugin_path . 'classes/externals.php' );
80
+ require_once( $plugin_path . 'classes/external/elementor.php' );
81
+ require_once( $plugin_path . 'classes/external/wpbakery.php' );
82
+ require_once( $plugin_path . 'classes/external/upsell_installer.php' );
83
+ require_once( $plugin_path . 'thumbnail_updater.php' );
84
 
85
+ function emr()
86
  {
87
+ return EnableMediaReplacePlugin::get();
 
88
  }
89
+ emr(); // runtime.
90
+
91
+ //register_uninstall_hook( __FILE__, '\EnableMediaReplace\emr_uninstall' );
92
+ register_deactivation_hook( __FILE__, array('\EnableMediaReplace\InstallHelper','deactivatePlugin') );
93
+ register_uninstall_hook(__FILE__, array('\EnableMediaReplace\InstallHelper','uninstallPlugin') );
js/emr_admin.js CHANGED
@@ -50,6 +50,7 @@
50
  this.updateTextLayer(source, false);
51
  this.showReplaceOptions();
52
 
 
53
  }
54
  this.loadDatePicker = function()
55
  {
50
  this.updateTextLayer(source, false);
51
  this.showReplaceOptions();
52
 
53
+
54
  }
55
  this.loadDatePicker = function()
56
  {
js/remove_bg.js ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function ($) {
2
+
3
+ // Init
4
+ $('input[type=radio][name=background_type]').on('change', backgroundInputs);
5
+ $('#bg_transparency').on('input', transparancyOptions);
6
+
7
+ backgroundInputs(); // init initial
8
+ transparancyOptions();
9
+
10
+ $('.replace_type.wrapper input').on('change', function () {
11
+ $('#replace_image_button').prop('disabled', 'disabled');
12
+ });
13
+
14
+ // Remove bg click
15
+ $('#remove_background_button').on('click', () => {
16
+ const method = 'POST'
17
+ const url = emrObject.ajax_url;
18
+ // const image = emrObject.base_url;
19
+ const nonce = emrObject.nonce;
20
+ const attachment_id = $('input[name="ID"]').val();
21
+ const action = 'emr_remove_background';
22
+ const bgType = $('input[type=radio][name=background_type]:checked').val();
23
+ const cLvl = $('input[type=radio][name=compression_level]:checked').val();
24
+ let background = {
25
+ type: "transparent"
26
+ }
27
+
28
+ background = {
29
+ type: bgType,
30
+ color: $('#bg_color').val(),
31
+ transparency: $('#bg_transparency').val()
32
+ }
33
+
34
+ $.ajax({
35
+ method,
36
+ url,
37
+ data: {
38
+ action,
39
+ nonce,
40
+ attachment_id,
41
+ background,
42
+ compression_level : cLvl
43
+ },
44
+ beforeSend: function () {
45
+ $('html, body').animate({
46
+ scrollTop: $(".emr_upload_form").offset().top
47
+ }, 1000);
48
+ $('input[type=radio][name=background_type]').attr('disabled', 'disabled')
49
+ $('input[type=radio][name=compression_level]').attr('disabled', 'disabled')
50
+ $('#remove_background_button').attr('disabled', 'disabled')
51
+ $('#overlay').css('visibility', 'visible');
52
+ var preview = $('.image_placeholder').last();
53
+ preview.find('img').remove();
54
+ // $('#preview-area').hide();
55
+ },
56
+ success: function (response) {
57
+ var preview = $('.image_placeholder').last();
58
+
59
+ if (response.success) {
60
+
61
+
62
+ $('#overlay').css('visibility', 'hidden');
63
+ preview.find('img').remove();
64
+ preview.removeClass('is_image not_image is_document');
65
+
66
+ $('#replace_image_button').prop('disabled', false);
67
+
68
+ var img = new Image();
69
+ img.src = response.image;
70
+ img.setAttribute('style', 'height: inherit;');
71
+
72
+ preview.prepend(img);
73
+ // preview.removeClass('not_image');
74
+ preview.addClass('is_image');
75
+
76
+ $('input[name="key"]').val(response.key);
77
+ $('input[type=radio][name=background_type]').attr('disabled', false);
78
+ $('input[type=radio][name=compression_level]').attr('disabled', false);
79
+ $('#remove_background_button').attr('disabled', false);
80
+
81
+ var badBg = document.getElementById('bad-background-link');
82
+ var href = badBg.dataset.link;
83
+ href = href.replace('{url}', response.url);
84
+ href = href.replace('{settings}', response.settings);
85
+
86
+ badBg.setAttribute('href', href);
87
+
88
+ badBg.style.visibility = 'visible';
89
+ /* $('#removed_image').html(`
90
+ <div class="img-comp-container">
91
+ <div class="img-comp-img">
92
+ <img src="${image}" width="${width}" height="${height}" />
93
+ </div>
94
+
95
+ </div>
96
+ `); */
97
+ // initComparisons();
98
+ }else{
99
+
100
+ preview.prepend(`<h1>${response.message}</h1>`);
101
+ $('#remove_background_button').attr('disabled', false)
102
+ $('input[type=radio][name=background_type]').attr('disabled', false)
103
+ $('input[type=radio][name=compression_level]').attr('disabled', false)
104
+ $('#overlay').css('visibility', 'hidden');
105
+ //$('#preview-area').show();
106
+ }
107
+ }
108
+ })
109
+ });
110
+
111
+ function backgroundInputs () {
112
+ const bgInputs = $('#solid_selecter');
113
+ var input = $('input[type=radio][name=background_type]:checked');
114
+ if (input.val() === 'solid') {
115
+ bgInputs.show();
116
+ } else {
117
+ bgInputs.hide();
118
+ }
119
+
120
+ };
121
+
122
+ $('#bg_display_picker').on('input', function () {
123
+ $('#color_range').html($(this).val());
124
+ $('#bg_color').val($(this).val());
125
+ });
126
+
127
+ function transparancyOptions() {
128
+ $('#transparency_range').html($('#bg_transparency').val());
129
+ };
130
+
131
+ });
readme.txt CHANGED
@@ -1,52 +1,80 @@
1
  === Enable Media Replace ===
2
  Contributors: ShortPixel
3
  Donate link: https://www.paypal.me/resizeImage
4
- Tags: replace, attachment, media, files, replace image, replace jpg, change media, replace media, image, file
5
  Requires at least: 4.9.7
6
  Tested up to: 6.0
7
  Requires PHP: 5.6
8
- Stable tag: 3.6.3
9
 
10
  Easily replace any attached image/file by simply uploading a new file in the Media Library edit view - a real time saver!
11
 
12
  == Description ==
13
 
14
  **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!
 
15
  Supported by the friendly team that created <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/" target="_blank">ShortPixel</a> :)**
16
 
17
  #### A real timesaver
18
 
19
- Don't you find it tedious and complicated to have to first delete a file and then upload one with the exact same name every time you want to update an image or any uploaded file inside the WordPress media library?
20
 
21
  Well, no longer!
22
 
23
- Now you'll be able to replace any uploaded file from the media "edit" view, where it should be. Media replacement can be done in one of two ways:
24
 
25
  #### It's simple to replace a file
26
 
27
- 1. Just replace the file. This option requires you to upload a file of the same type as the one you are replacing. The name of the attachment will stay the same no matter what the file you upload is called.
28
- 1. Replace the file, use new file name and update all links. If you check this option, the name and type of the file you are about to upload will replace the old file. All links pointing to the current file will be updated to point to the new file name. Additional options for the folder where to place the new file, or the date of the new file are also available on the replace screen.
29
 
30
- This plugin is very powerful and a must-have for any larger sites built with WordPress. It now also comes with a preview of the replaced image!
31
 
32
- #### Display file modification time
 
 
33
 
34
- There is a shortcode available which picks up the file modification date and displays it in a post or a page. The code is:
 
 
35
  `[file_modified id=XX format=XXXX]` where the "id" is required and the "format" is optional and defaults to your current WordPress settings for date and time format.
36
 
37
- So `[file_modified id=870]` would display the last time the file with ID 870 was updated on your site. To get the ID for a file, check the URL when editing a file in the media library (see screenshot #4)
 
 
38
 
39
- 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).
40
 
 
 
 
 
 
 
41
 
42
- #### Compatible and recommended Plugins =
43
 
44
- * [ShortPixel Image Optimization](https://wordpress.org/plugins/shortpixel-image-optimiser/) - Enable Media Replace is fully compatible with this plugin. Once enabled, ShortPixel will automatically optimize the images you replace using Enable Media Replace.
45
- * [Resize Image After Upload plugin](https://wordpress.org/plugins/resize-image-after-upload/) - automatically resize images upon upload to save traffic & disk space. Good for SEO and compatible with EMR.
46
- * [Regenerate Thumbnails Advanced](https://wordpress.org/plugins/regenerate-thumbnails-advanced/) - Fast, free and simple to use plugin to regenerate the thumbnails for your site after changing a theme (for example). Supported & maintained by [ShortPixel](https://ShortPixel.com)
 
47
 
48
  == Changelog ==
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  = 3.6.3 =
51
 
52
  Release date: November 25th, 2021
1
  === Enable Media Replace ===
2
  Contributors: ShortPixel
3
  Donate link: https://www.paypal.me/resizeImage
4
+ Tags: replace, attachment, media, files, replace image, remove background, replace jpg, change media, replace media, image, file
5
  Requires at least: 4.9.7
6
  Tested up to: 6.0
7
  Requires PHP: 5.6
8
+ Stable tag: 4.0.0
9
 
10
  Easily replace any attached image/file by simply uploading a new file in the Media Library edit view - a real time saver!
11
 
12
  == Description ==
13
 
14
  **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!
15
+ New beta feature! You can now remove the background of your images for better integration with eCommerce solutions!
16
  Supported by the friendly team that created <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/" target="_blank">ShortPixel</a> :)**
17
 
18
  #### A real timesaver
19
 
20
+ Don't you find it tedious and complicated to first delete a file and then upload another one with the exact same name every time you want to update an image or other uploaded file in the WordPress media library?
21
 
22
  Well, no longer!
23
 
24
+ Now you can replace any uploaded file in the Edit Media view, where it should be. Replacing Media can be done in two ways:
25
 
26
  #### It's simple to replace a file
27
 
28
+ 1. Simply replace the file. This option requires you to upload a file of the same type as the file you want to replace. The attachment name remains the same regardless of what the file you upload is called.
29
+ 2. Replace the file, use the new file name, and update all links. If you check this option, the old file will be replaced with the name and type of the file you are uploading. All links pointing to the current file will be updated to point to the new file name. Additional options for the folder to put the new file in or the date of the new file are also available on the replace screen.
30
 
31
+ This plugin is very powerful and a must-have for all major websites built with WordPress. It also offers a preview of the replaced image!
32
 
33
+ #### New beta feature: You can now remove the background of any image!
34
+ 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.
35
+ The background removal feature is still in beta and will be free of charge for a reasonable usage.
36
 
37
+ #### Show file modification time
38
+
39
+ There is a shortcode that takes the file modification date and displays it in a post or on a page. The code is:
40
  `[file_modified id=XX format=XXXX]` where the "id" is required and the "format" is optional and defaults to your current WordPress settings for date and time format.
41
 
42
+ So `[file_modified id=870]` would show the last time the file with ID 870 was updated on your site. To get the ID for a file, check the URL when editing a file in the media library (see screenshot #4)
43
+
44
+ If you want more control over the format in which the time is shown, you can use the format option. So `[file_modified id=870 format=Y-m-d]` would show the date the file was modified but not the time. The format string uses [the standard PHP date() formatting tags](http://php.net/manual/en/function.date.php).
45
 
46
+ **Other plugins by [ShortPixel](https://shortpixel.com):**
47
 
48
+ * [ShortPixel Image Optimizer](https://wordpress.org/plugins/shortpixel-image-optimiser/) - Image optimization & compression for all the images on your website, including WebP & AVIF delivery
49
+ * [ShortPixel Adaptive Images](https://wordpress.org/plugins/shortpixel-adaptive-images/) - On-the-fly image optimization & CDN delivery
50
+ * [Resize Image After Upload](https://wordpress.org/plugins/resize-image-after-upload/) - Automatically resize each uploaded image
51
+ * [reGenerate Thumbnails Advanced](https://wordpress.org/plugins/regenerate-thumbnails-advanced/) - Easily regenerate thumbnails
52
+ * [WP SVG Images](https://wordpress.org/plugins/wp-svg-images/) - Secure upload of SVG files to Media Library
53
+ * [ShortPixel Critical CSS](https://wordpress.org/plugins/shortpixel-critical-css/) - Automatically generate above-the-fold CSS for fatster loading times and better SEO scores
54
 
55
+ **Get in touch!**
56
 
57
+ * Email <a href="https://shortpixel.com/contact" target="_blank">https://shortpixel.com/contact</a>
58
+ * Twitter <a href="https://twitter.com/shortpixel" target="_blank">https://twitter.com/shortpixel</a>
59
+ * Facebook <a href="https://www.facebook.com/ShortPixel" target="_blank">https://www.facebook.com/ShortPixel</a>
60
+ * LinkedIn <a href="https://www.linkedin.com/company/shortpixel" target="_blank">https://www.linkedin.com/company/shortpixel</a>
61
 
62
  == Changelog ==
63
 
64
+ = 4.0.0 =
65
+
66
+ Release date: September 5th, 2022
67
+ * New: added the functionality to remove the background for any image;
68
+ * Fix: images added to the new block-style widgets were not replaced;
69
+ * Fix: the original file was not removed after replacement if a multilingual plugin was installed;
70
+ * Fix: additional checks were added to the new upload path for replacements, to avoid possible vulnerabilities, kudos to @soulseekah;
71
+ * Fix: an object cache flush was added after an image was replaced to prevent the content from still being cached in the post editor;
72
+ * Fix: if there was no `_wp_attached_file` in the postmeta table a fatal error was thrown;
73
+ * Fix: the time zone was not displayed correctly on the Replace Media screen;
74
+ * Fix: added some additional checks for file path to avoid `open_basedir` restrictions;
75
+ * Fix: added titles for the Replace Media and Remove Background screens;
76
+ * Fix: various small CSS/JS fixes, wording updates and code cleanups;
77
+
78
  = 3.6.3 =
79
 
80
  Release date: November 25th, 2021
scss/admin.scss CHANGED
@@ -11,7 +11,12 @@
11
  }
12
  }
13
 
14
- .wrapper
 
 
 
 
 
15
  {
16
  // margin: 15px 0;
17
  padding: 18px;
@@ -81,7 +86,7 @@
81
  margin-bottom: 10px;
82
  border: 1px solid #ddd;
83
  vertical-align: top;
84
- max-height: 500px;
85
  .textlayer
86
  {
87
  font-size: 25px;
11
  }
12
  }
13
 
14
+ .editor-wrapper
15
+ {
16
+ width: 100%;
17
+ }
18
+
19
+ .wrapper
20
  {
21
  // margin: 15px 0;
22
  padding: 18px;
86
  margin-bottom: 10px;
87
  border: 1px solid #ddd;
88
  vertical-align: top;
89
+ // max-height: 500px;
90
  .textlayer
91
  {
92
  font-size: 25px;
scss/remove_background.scss ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #remove-background-form
2
+ {
3
+ form {
4
+ display: flex;
5
+
6
+ }
7
+ .image_chooser.wrapper {
8
+ min-height: 0;
9
+ }
10
+ .image_placeholder.is_image
11
+ {
12
+ border:none;
13
+ width:45%;
14
+ height:auto;
15
+ .textlayer, .image_size {
16
+ display: none;
17
+ }
18
+ }
19
+
20
+ .bad-button
21
+ {
22
+ text-align: right;
23
+ margin-right: 25px;
24
+ a {
25
+ visibility: hidden;
26
+ }
27
+ }
28
+
29
+ .overlay{
30
+ visibility: hidden;
31
+ display: flex;
32
+ flex-direction: column;
33
+ justify-content: center;
34
+ align-items: center;
35
+ width: 100%;
36
+ height: 100%;
37
+ background-color: rgba(0,0,0,0.2);
38
+ position: absolute;
39
+ top: 0;
40
+ }
41
+ .lds-spinner {
42
+ color: official;
43
+ display: inline-block;
44
+ position: relative;
45
+ width: 80px;
46
+ height: 80px;
47
+ }
48
+ .lds-spinner div {
49
+ transform-origin: 40px 40px;
50
+ animation: lds-spinner 1.2s linear infinite;
51
+ }
52
+ .lds-spinner div:after {
53
+ content: " ";
54
+ display: block;
55
+ position: absolute;
56
+ top: 3px;
57
+ left: 37px;
58
+ width: 6px;
59
+ height: 18px;
60
+ border-radius: 20%;
61
+ background: #fff;
62
+ }
63
+ .lds-spinner div:nth-child(1) {
64
+ transform: rotate(0deg);
65
+ animation-delay: -1.1s;
66
+ }
67
+ .lds-spinner div:nth-child(2) {
68
+ transform: rotate(30deg);
69
+ animation-delay: -1s;
70
+ }
71
+ .lds-spinner div:nth-child(3) {
72
+ transform: rotate(60deg);
73
+ animation-delay: -0.9s;
74
+ }
75
+ .lds-spinner div:nth-child(4) {
76
+ transform: rotate(90deg);
77
+ animation-delay: -0.8s;
78
+ }
79
+ .lds-spinner div:nth-child(5) {
80
+ transform: rotate(120deg);
81
+ animation-delay: -0.7s;
82
+ }
83
+ .lds-spinner div:nth-child(6) {
84
+ transform: rotate(150deg);
85
+ animation-delay: -0.6s;
86
+ }
87
+ .lds-spinner div:nth-child(7) {
88
+ transform: rotate(180deg);
89
+ animation-delay: -0.5s;
90
+ }
91
+ .lds-spinner div:nth-child(8) {
92
+ transform: rotate(210deg);
93
+ animation-delay: -0.4s;
94
+ }
95
+ .lds-spinner div:nth-child(9) {
96
+ transform: rotate(240deg);
97
+ animation-delay: -0.3s;
98
+ }
99
+ .lds-spinner div:nth-child(10) {
100
+ transform: rotate(270deg);
101
+ animation-delay: -0.2s;
102
+ }
103
+ .lds-spinner div:nth-child(11) {
104
+ transform: rotate(300deg);
105
+ animation-delay: -0.1s;
106
+ }
107
+ .lds-spinner div:nth-child(12) {
108
+ transform: rotate(330deg);
109
+ animation-delay: 0s;
110
+ }
111
+ @keyframes lds-spinner {
112
+ 0% {
113
+ opacity: 1;
114
+ }
115
+ 100% {
116
+ opacity: 0;
117
+ }
118
+ }
119
+
120
+
121
+
122
+ * {
123
+ box-sizing: border-box;
124
+ }
125
+
126
+ .img-comp-container {
127
+ position: relative;
128
+ height: 200px; /*should be the same height as the images*/
129
+ }
130
+
131
+ .img-comp-img {
132
+ position: absolute;
133
+ width: auto;
134
+ height: auto;
135
+ overflow:hidden;
136
+ }
137
+
138
+ .img-comp-img img {
139
+ display:block;
140
+ }
141
+
142
+ .img-comp-slider {
143
+ position: absolute;
144
+ z-index:9;
145
+ cursor: ew-resize;
146
+ /*set the appearance of the slider:*/
147
+ width: 20px;
148
+ height: 20px;
149
+ background-color: #2196F3;
150
+ opacity: 0.7;
151
+ border-radius: 50%;
152
+ }
153
+
154
+ .preview-area{
155
+ display:flex;
156
+ justify-content: center;
157
+ align-items: center;
158
+ width: 100%;
159
+ height: 100%;
160
+ }
161
+ .preview-area h1{
162
+ color : red !important;
163
+ }
164
+
165
+ }
views/do-replace-background.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace;
3
+
4
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
+ use EnableMediaReplace\Notices\NoticeController as Notices;
6
+ use \EnableMediaReplace\Replacer as Replacer;
7
+
8
+
9
+ if (! defined('ABSPATH')) {
10
+ exit; // Exit if accessed directly.
11
+ }
12
+
13
+ if (! check_admin_referer('do_background_replace'))
14
+ {
15
+ wp_die(esc_html__('Nonce in form failed. Go back, refresh and try again.', 'enable-media-replace'));
16
+ }
17
+
18
+ $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : null;
19
+
20
+ if (is_null($key) || strlen($key) == 0)
21
+ {
22
+ wp_die(esc_html__('Error while sending form (no key). Please try again.', 'enable-media-replace'));
23
+ }
24
+
25
+ $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id.
26
+ if (is_null($post_id)) {
27
+ wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));
28
+ }
29
+
30
+ $attachment = get_post($post_id);
31
+
32
+ if (! emr()->checkImagePermission($attachment->post_author, $attachment->ID)) {
33
+ wp_die(esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace'));
34
+ }
35
+
36
+ $uiHelper = emr()->uiHelper();
37
+
38
+ $replacer = new Replacer($post_id);
39
+ $replacer->setMode(\EnableMediaReplace\Replacer::MODE_REPLACE);
40
+
41
+ $datetime = current_time('mysql');
42
+ $replacer->setTimeMode( \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED, $datetime);
43
+
44
+
45
+
46
+
47
+ $api = new Api();
48
+ $result = $api->handleDownload($key);
49
+
50
+ if (! $result->success)
51
+ {
52
+ die($result->message);
53
+ }
54
+
55
+ // When are 1-1 replacing.
56
+ $source = $replacer->getSourceFile();
57
+
58
+ $redirect_error = $uiHelper->getFailedRedirect($post_id);
59
+ $redirect_success = $uiHelper->getSuccesRedirect($post_id);
60
+
61
+ if (! file_exists($result->image))
62
+ {
63
+ Log::addError('Download File not here', $result->image);
64
+ exit(__('Temp file does not exist', 'enable-media-replace'));
65
+ }
66
+
67
+ try {
68
+ $result = $replacer->replaceWith($result->image, $source->getFileName() , true);
69
+ } catch (\RunTimeException $e) {
70
+ print_r($e->getMessage());
71
+ Log::addError($e->getMessage());
72
+ die;
73
+
74
+ }
75
+
76
+ if (is_null($result)) {
77
+ wp_safe_redirect($redirect_error);
78
+ exit();
79
+ }
80
+
81
+ $noticeController = Notices::getInstance();
82
+ $notice = Notices::addSuccess('<p>' . __('File successfully replaced', 'enable-media-replace') . '</p>');
83
+ $notice->is_removable = false;
84
+ $noticeController->update();
85
+
86
+ wp_redirect($redirect_success);
87
+ exit();
views/popup.php CHANGED
@@ -24,13 +24,13 @@ if (!current_user_can('upload_files'))
24
 
25
  global $wpdb;
26
 
27
- $emr = EnableMediaReplacePlugin::get();
28
 
29
  $table_name = $wpdb->prefix . "posts";
30
  $attachment_id = intval($_GET['attachment_id']);
31
  $attachment = get_post($attachment_id);
32
 
33
- if (! $emr->checkImagePermission($attachment->post_author, $attachment_id))
34
  {
35
  wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') );
36
  }
@@ -38,12 +38,12 @@ if (! $emr->checkImagePermission($attachment->post_author, $attachment_id))
38
  $replacer = new Replacer($attachment_id);
39
 
40
  $file = $replacer->getSourceFile();
41
- $filepath = $file->getFullFilePath();
42
  $filename = $file->getFileName();
43
- $filetype = $file->getFileExtension();
44
  $source_mime = get_post_mime_type($attachment_id);
45
 
46
- $uiHelper = new UIHelper();
47
  $uiHelper->setPreviewSizes();
48
  $uiHelper->setSourceSizes($attachment_id);
49
 
@@ -58,6 +58,8 @@ $defaults = array(
58
  );
59
  $settings = get_option('enable_media_replace', $defaults);
60
 
 
 
61
  ?>
62
 
63
  <div class="wrap emr_upload_form">
@@ -76,7 +78,7 @@ $url = $uiHelper->getFormUrl($attachment_id);
76
 
77
  <div class='editor-wrapper'>
78
  <section class='image_chooser wrapper'>
79
- <div class='section-header'> <?php _e('Choose Replacement Media', 'enable-media-replace'); ?></div>
80
 
81
  <div id="message" class=""><strong><?php printf( esc_html__('NOTE: You are about to replace the media file "%s". There is no undo. Think about it!', "enable-media-replace"), $filename ); ?></strong></div>
82
 
@@ -87,11 +89,11 @@ $url = $uiHelper->getFormUrl($attachment_id);
87
  <div class='form-error filesize'><p><?php printf(__('%s f %s exceeds the maximum upload size for this site.', 'enable-media-replace'), '<span class="fn">', '</span>'); ?></p>
88
  </div>
89
 
90
- <div class='form-warning filetype'><p><?php printf(__('Replacement file is not the same filetype. This might cause unexpected issues ( %s )', 'enable-media-replace'), '<span class="source_type"></span> - <span class="target_type"></span>'); ?>
91
 
92
  </p></div>
93
 
94
- <div class='form-warning mimetype'><p><?php printf(__('Replacement file type doesn\'t seem to be allowed by WordPress. This might cause unexpected issues')); ?></p></div>
95
 
96
  <div class='emr_drop_area'>
97
  <div class='drop-wrapper'>
@@ -122,12 +124,32 @@ $url = $uiHelper->getFormUrl($attachment_id);
122
  }
123
  ?>
124
  </div>
 
 
 
 
 
 
 
 
125
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  </section>
127
 
128
  <div class='option-flex-wrapper'>
129
  <section class='replace_type wrapper'>
130
- <div class='section-header'> <?php _e('Replacement Options', 'enable-media-replace'); ?></div>
131
 
132
  <?php
133
  // these are also used in externals, for checks.
@@ -142,7 +164,7 @@ $url = $uiHelper->getFormUrl($attachment_id);
142
  </label>
143
 
144
  <p class="howto">
145
- <?php printf( esc_html__("Note: This option requires you to upload a file of the same type (%s) as the one you are replacing. The name of the attachment will stay the same (%s) no matter what the file you upload is called.", "enable-media-replace"), $filetype, $filename ); ?>
146
  </p>
147
 
148
  <p class='form-warning filetype'><?php _e('If you replace the file with a different filetype, this file might become unreadable and / or cause unexpected issues', 'enable-media-replace'); ?>
@@ -156,12 +178,12 @@ $url = $uiHelper->getFormUrl($attachment_id);
156
  ?>
157
 
158
  <div class="option searchreplace <?php echo $searchreplace_disabled ?>">
159
- <label for="replace_type_2"><input id="replace_type_2" <?php checked('replace_and_search', $settings['replace_type']) ?> type="radio" name="replace_type" value="replace_and_search" <?php echo $searchreplace_disabled ?> > <?php echo __("Replace the file, use new file name and update all links", "enable-media-replace"); ?>
160
  </label>
161
 
162
- <p class="howto"><?php printf( esc_html__("Note: If you check this option, the name and type of the file you are about to upload will replace the old file. All links pointing to the current file (%s) will be updated to point to the new file name. (If any other websites link to the file directly, those links will no longer work. Be careful.)", "enable-media-replace"), $filename ); ?></p>
163
 
164
- <!-- <p class="howto"><?php echo esc_html__("Please note that if you upload a new image, only embeds/links of the original size image will be replaced in your posts.", "enable-media-replace"); ?></p> -->
165
 
166
  <?php do_action('emr_after_replace_type_options'); ?>
167
  </div>
@@ -172,13 +194,13 @@ $url = $uiHelper->getFormUrl($attachment_id);
172
  <div class='option timestamp'>
173
  <?php
174
  $attachment_current_date = date_i18n('d/M/Y H:i', strtotime($attachment->post_date) );
175
- $attachment_now_date = date_i18n('d/M/Y H:i', time() );
 
176
  $time = current_time('mysql');
177
  $date = $nowDate = new \dateTime($time); // default to now.
178
- // var_dump(strtotime($attachment->post_date));
179
- // exit();
180
  $attachmentDate = new \dateTime($attachment->post_date);
181
 
 
182
  if ($settings['timestamp_replace'] == \EnableMediaReplace\Replacer::TIME_CUSTOM)
183
  {
184
  $date = new \dateTime($settings['custom_date']);
@@ -186,7 +208,7 @@ $url = $uiHelper->getFormUrl($attachment_id);
186
  ?>
187
  <p><?php _e('When replacing the media, do you want to:', 'enable-media-replace'); ?></p>
188
  <ul>
189
- <li><label><input type='radio' <?php checked('1', $settings['timestamp_replace']) ?> name='timestamp_replace' value='1' /><?php printf(__('Replace the date with current date %s(%s)%s', 'enable-media-replace'), "<span class='small'>", $attachment_now_date, "</span>") ; ?></label></li>
190
  <li><label><input type='radio' <?php checked('2', $settings['timestamp_replace']) ?> name='timestamp_replace' value='2' /><?php printf(__('Keep the date %s(%s)%s', 'enable-media-replace'), "<span class='small'>", $attachment_current_date, "</span>"); ?></label></li>
191
  <li><label><input type='radio' <?php checked('3', $settings['timestamp_replace']) ?> name='timestamp_replace' value='3' /><?php _e('Set a Custom Date', 'enable-media-replace'); ?></label></li>
192
  </ul>
@@ -214,7 +236,7 @@ $url = $uiHelper->getFormUrl($attachment_id);
214
  $subdir = $settings['new_location_dir'];
215
  ?>
216
  <div class='location_option'>
217
- <label><input type="checkbox" name="new_location" value="1" <?php checked($settings['new_location'], 1); ?> /> <?php _e('Put new Upload in Updated Folder: ', 'enable-media-replace'); ?></label>
218
  <input type="text" name="location_dir" value="<?php echo $subdir ?>" />
219
  </div>
220
  <?php endif; ?>
24
 
25
  global $wpdb;
26
 
27
+ //$emr = EnableMediaReplacePlugin::get();
28
 
29
  $table_name = $wpdb->prefix . "posts";
30
  $attachment_id = intval($_GET['attachment_id']);
31
  $attachment = get_post($attachment_id);
32
 
33
+ if (! emr()->checkImagePermission($attachment->post_author, $attachment_id))
34
  {
35
  wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') );
36
  }
38
  $replacer = new Replacer($attachment_id);
39
 
40
  $file = $replacer->getSourceFile();
41
+ $filepath = $file->getFullPath();
42
  $filename = $file->getFileName();
43
+ $filetype = $file->getExtension();
44
  $source_mime = get_post_mime_type($attachment_id);
45
 
46
+ $uiHelper = emr()->uiHelper();
47
  $uiHelper->setPreviewSizes();
48
  $uiHelper->setSourceSizes($attachment_id);
49
 
58
  );
59
  $settings = get_option('enable_media_replace', $defaults);
60
 
61
+ $settings = array_merge($defaults, $settings); // might miss some
62
+
63
  ?>
64
 
65
  <div class="wrap emr_upload_form">
78
 
79
  <div class='editor-wrapper'>
80
  <section class='image_chooser wrapper'>
81
+ <div class='section-header'> <?php _e('Select Replacement Media', 'enable-media-replace'); ?></div>
82
 
83
  <div id="message" class=""><strong><?php printf( esc_html__('NOTE: You are about to replace the media file "%s". There is no undo. Think about it!', "enable-media-replace"), $filename ); ?></strong></div>
84
 
89
  <div class='form-error filesize'><p><?php printf(__('%s f %s exceeds the maximum upload size for this site.', 'enable-media-replace'), '<span class="fn">', '</span>'); ?></p>
90
  </div>
91
 
92
+ <div class='form-warning filetype'><p><?php printf(__('The replacement file does not have the same file type. This can lead to unexpected issues ( %s )', 'enable-media-replace'), '<span class="source_type"></span> - <span class="target_type"></span>'); ?>
93
 
94
  </p></div>
95
 
96
+ <div class='form-warning mimetype'><p><?php printf(__('The replacement file type does not seem to be allowed by WordPress. This can lead to unexpected issues')); ?></p></div>
97
 
98
  <div class='emr_drop_area'>
99
  <div class='drop-wrapper'>
124
  }
125
  ?>
126
  </div>
127
+ <?php
128
+ $url = admin_url("upload.php");
129
+ $url = add_query_arg(array(
130
+ 'page' => 'enable-media-replace/enable-media-replace.php',
131
+ 'action' => 'emr_prepare_remove',
132
+ 'attachment_id' => $attachment_id,
133
+ ), $url);
134
+ ?>
135
 
136
+ <p>&nbsp;</p>
137
+ <?php if ($uiHelper->isBackgroundRemovable($attachment)): ?>
138
+ <div>
139
+
140
+ <a href="<?php echo wp_nonce_url( $url , 'emr_prepare_remove' ); ?>">
141
+ <?php _e('New! Click here to remove the background of this image!', 'enable-media-replace'); ?></a>
142
+ <br>
143
+ <br>
144
+ <input type="checkbox" id="remove_after_progress" name="remove_after_progress" value="<?php echo $attachment_id;?>">
145
+ <label for="remove_after_progress"><?php _e('Remove the background after replacing this image!' ,'enable-media-replace'); ?> </label>
146
+ </div>
147
+ <?php endif; ?>
148
  </section>
149
 
150
  <div class='option-flex-wrapper'>
151
  <section class='replace_type wrapper'>
152
+ <div class='section-header'> <?php _e('Replace Options', 'enable-media-replace'); ?></div>
153
 
154
  <?php
155
  // these are also used in externals, for checks.
164
  </label>
165
 
166
  <p class="howto">
167
+ <?php printf( esc_html__("Note: This option requires you to upload a file of the same type (%s) as the file you want to replace. The attachment name will remain the same (%s) regardless of what the file you upload is called. If a CDN is used, remember to clear the cache for this image!", "enable-media-replace"), $filetype, $filename ); ?>
168
  </p>
169
 
170
  <p class='form-warning filetype'><?php _e('If you replace the file with a different filetype, this file might become unreadable and / or cause unexpected issues', 'enable-media-replace'); ?>
178
  ?>
179
 
180
  <div class="option searchreplace <?php echo $searchreplace_disabled ?>">
181
+ <label for="replace_type_2"><input id="replace_type_2" <?php checked('replace_and_search', $settings['replace_type']) ?> type="radio" name="replace_type" value="replace_and_search" <?php echo $searchreplace_disabled ?> > <?php echo __("Replace the file, use the new file name, and update all links", "enable-media-replace"); ?>
182
  </label>
183
 
184
+ <p class="howto"><?php printf( esc_html__("Note: If you enable this option, the name and type of the file you are uploading will replace the old file. All links pointing to the current file (%s) will be updated to point to the new file name. (If other websites link directly to the file, those links will no longer work. Be careful!)", "enable-media-replace"), $filename ); ?></p>
185
 
186
+ <!-- <p class="howto"><?php echo esc_html__("Please note that if you upload a new image, only the embeds/links of the original size image will be replaced in your posts.", "enable-media-replace"); ?></p> -->
187
 
188
  <?php do_action('emr_after_replace_type_options'); ?>
189
  </div>
194
  <div class='option timestamp'>
195
  <?php
196
  $attachment_current_date = date_i18n('d/M/Y H:i', strtotime($attachment->post_date) );
197
+ $attachment_now_date = date_i18n('d/M/Y H:i' );
198
+
199
  $time = current_time('mysql');
200
  $date = $nowDate = new \dateTime($time); // default to now.
 
 
201
  $attachmentDate = new \dateTime($attachment->post_date);
202
 
203
+
204
  if ($settings['timestamp_replace'] == \EnableMediaReplace\Replacer::TIME_CUSTOM)
205
  {
206
  $date = new \dateTime($settings['custom_date']);
208
  ?>
209
  <p><?php _e('When replacing the media, do you want to:', 'enable-media-replace'); ?></p>
210
  <ul>
211
+ <li><label><input type='radio' <?php checked('1', $settings['timestamp_replace']) ?> name='timestamp_replace' value='1' /><?php printf(__('Replace the date with the current date %s(%s)%s', 'enable-media-replace'), "<span class='small'>", $attachment_now_date, "</span>") ; ?></label></li>
212
  <li><label><input type='radio' <?php checked('2', $settings['timestamp_replace']) ?> name='timestamp_replace' value='2' /><?php printf(__('Keep the date %s(%s)%s', 'enable-media-replace'), "<span class='small'>", $attachment_current_date, "</span>"); ?></label></li>
213
  <li><label><input type='radio' <?php checked('3', $settings['timestamp_replace']) ?> name='timestamp_replace' value='3' /><?php _e('Set a Custom Date', 'enable-media-replace'); ?></label></li>
214
  </ul>
236
  $subdir = $settings['new_location_dir'];
237
  ?>
238
  <div class='location_option'>
239
+ <label><input type="checkbox" name="new_location" value="1" <?php checked($settings['new_location'], 1); ?> /> <?php _e('Place the newly uploaded file in this folder: ', 'enable-media-replace'); ?></label>
240
  <input type="text" name="location_dir" value="<?php echo $subdir ?>" />
241
  </div>
242
  <?php endif; ?>
views/prepare-remove-background.php ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EnableMediaReplace;
3
+
4
+ use EnableMediaReplace\EnableMediaReplacePlugin;
5
+ use EnableMediaReplace\UIHelper;
6
+
7
+ if (! defined('ABSPATH')) {
8
+ exit; // Exit if accessed directly.
9
+ }
10
+
11
+ $date = new \dateTime();
12
+ $uiHelper = emr()->uiHelper();
13
+
14
+ $attachment_id = intval($_GET['attachment_id']);
15
+ $attachment = get_post($attachment_id);
16
+
17
+ $replacer = new Replacer($attachment_id);
18
+
19
+ $file = $replacer->getSourceFile();
20
+
21
+ $uiHelper->setPreviewSizes();
22
+ $uiHelper->setSourceSizes($attachment_id);
23
+
24
+ $base_image = $uiHelper->getPreviewImage($attachment_id, $file );
25
+ $replace_image = $uiHelper->getPreviewImage(-1, $file, array('remove_bg_ui' => true) );
26
+
27
+ $formurl = $uiHelper->getFormUrl($attachment_id, 'do_background_replace');
28
+ $formurl = wp_nonce_url( $formurl, "do_background_replace" );
29
+
30
+ $linebreak = '%0D%0A';
31
+ $linebreak_double = $linebreak . $linebreak;
32
+ $email_subject = __('Bad remove of background report', 'enable-media-replace');
33
+ $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);
34
+
35
+
36
+ $replace_url = add_query_arg(array(
37
+ 'page' => 'enable-media-replace/enable-media-replace.php',
38
+ 'action' => 'media_replace',
39
+ 'attachment_id' => $attachment_id,
40
+ ), admin_url("upload.php"));
41
+
42
+ $defaults = array(
43
+ 'bg_type' => 'transparent',
44
+ 'bg_color' => '#ffffff',
45
+ 'bg_transparency' => 100,
46
+ );
47
+ $settings = get_option('enable_media_replace', $defaults);
48
+
49
+ $settings = array_merge($defaults, $settings); // might miss some
50
+ ?>
51
+ <div class="wrap emr_upload_form" id="remove-background-form">
52
+
53
+ <form id="emr_replace_form" enctype="multipart/form-data" method="POST" action="<?php
54
+ echo $formurl; ?>" >
55
+ <input type="hidden" name="ID" value="<?php echo intval($attachment_id); ?>" />
56
+ <input type='hidden' name='key' value='' />
57
+
58
+ <div class="editor-wrapper" >
59
+ <section class='image_chooser wrapper'>
60
+ <div class='section-header'> <?php esc_html_e( 'Remove Media Background', 'enable-media-replace' ); ?></div>
61
+ <div class='image_previews'>
62
+ <?php echo $base_image; ?>
63
+ <?php echo $replace_image ?>
64
+
65
+ </div>
66
+
67
+ <div class='bad-button'>
68
+ <a href="" data-link="mailto:support@shortpixel.com?subject=<?php echo esc_attr($email_subject) ?>&body=<?php echo esc_attr($email_body) ?>" id="bad-background-link" class="button"><?php esc_html_e('Report bad background removal','enable-media-replace'); ?></a>
69
+
70
+ </div>
71
+
72
+ </section>
73
+
74
+ <p><a href="<?php echo esc_attr(wp_nonce_url($replace_url, 'media_replace')); ?>">Replace this image with another one instead!</a></p>
75
+ <div class="option-flex-wrapper">
76
+ <section class="replace_type wrapper">
77
+ <div class="section-header"><?php esc_html_e('Background Removal Options'); ?></div>
78
+ <div class="option replace ">
79
+ <p>
80
+ <?php esc_html_e('If a CDN is used, remember to clear the cache for this image!', 'enable-media-replace'); ?>
81
+ </p>
82
+ <label for="transparent_background">
83
+ <input id="transparent_background" type="radio" name="background_type" value="transparent" <?php checked('transparent', $settings['bg_type']); ?> >
84
+ <?php esc_html_e('Transparent/white background', 'enable-media-replace'); ?>
85
+ </label>
86
+ <p class="howto">
87
+ <?php esc_html_e('Returns a transparent background if it is a PNG image, or a white one if it is a JPG image.', 'enable-media-replace'); ?>
88
+ </p>
89
+ </div>
90
+ <div class="option searchreplace">
91
+ <label for="solid_background">
92
+ <input id="solid_background" type="radio" name="background_type" value="solid" <?php checked('solid', $settings['bg_type']); ?>>
93
+ <?php esc_html_e('Solid background', 'enable-media-replace'); ?>
94
+ </label>
95
+ <p class="howto">
96
+ <?php esc_html_e('If you select this option, the image will have a solid color background and you can choose the color code from the color picker below.', 'enable-media-replace'); ?>
97
+ </p>
98
+ <div id="solid_selecter" style="display:none;">
99
+ <label for="bg_display_picker">
100
+ <p><?php esc_html_e('Background Color:','enable-media-replace'); ?> <strong><span style="text-transform: uppercase;" id="color_range"><?php echo esc_attr($settings['bg_color']); ?></span></strong></p>
101
+ <input type="color" value="<?php echo esc_attr($settings['bg_color']); ?>" name="bg_display_picker" id="bg_display_picker" />
102
+ <input type="hidden" value="<?php echo esc_attr($settings['bg_color']); ?>" name="bg_color" id="bg_color" />
103
+ </label>
104
+ <hr>
105
+ <label for="bg_transparency">
106
+ <p><?php esc_html_e('Opacity:', 'enable-media-replace'); ?> <strong><span id="transparency_range"><?php echo esc_attr($settings['bg_transparency']); ?></span>%</strong></p>
107
+ <input type="range" min="0" max="100" value="<?php echo esc_attr($settings['bg_transparency']); ?>" id="bg_transparency" />
108
+ </label>
109
+ </div>
110
+ </div>
111
+ </section>
112
+
113
+ <!--
114
+ <section class="options wrapper">
115
+
116
+
117
+ <div class="section-header"><?php esc_html_e('Image Compression', 'enable-media-replace'); ?></div>
118
+ <div class="option replace">
119
+ <label for="lossy">
120
+ <input id="lossy" type="radio" name="compression_level" value="1">
121
+ <?php esc_html_e('Lossy compression','enable-media-replace'); ?>
122
+ </label>
123
+ <p class="howto">
124
+ <?php esc_html_e('Lossy has a better compression rate than lossless compression. The resulting image is not 100% identical with the original. Works well for photos taken with your camera.', 'enable-media-replace'); ?>
125
+ </p>
126
+ </div>
127
+ <div class="option searchreplace">
128
+ <label for="glossy">
129
+ <input id="glossy" type="radio" name="compression_level" value="2">
130
+ <?php esc_html_e('Glossy compression', 'enable-media-replace'); ?>
131
+ </label>
132
+ <p class="howto">
133
+ <?php esc_html_e('Creates images that are almost pixel-perfect identical to the originals. Best option for photographers and other professionals that use very high quality images on their sites and want best compression while keeping the quality untouched.', 'enable-media-replace'); ?>
134
+ </p>
135
+ </div>
136
+ <div class="option searchreplace">
137
+ <label for="lossless">
138
+ <input checked="checked" id="lossless" type="radio" name="compression_level" value="0">
139
+ <?php esc_html_e('Lossless compression', 'enable-media-replace'); ?>
140
+ </label>
141
+ <p class="howto">
142
+ <?php esc_html_e('The shrunk image will be identical with the original and smaller in size. Use this when you do not want to loose any of the original image\'s details. Works best for technical drawings, clip art and comics.', 'enable-media-replace'); ?>
143
+ </p>
144
+ </div>
145
+ </section> -->
146
+ </div>
147
+ <button type="button" class="button button-primary" id="remove_background_button"><?php esc_html_e('Preview', 'enable-media-replace'); ?></button>
148
+ <button type="submit" class="button button-primary" id="replace_image_button" disabled><?php esc_html_e('Replace', 'enable-media-replace'); ?></button>
149
+ <a class="button" href="javascript:history.back()"><?php esc_html_e('Cancel', 'enable-media-replace'); ?></a>
150
+ </div> <!--- editor wrapper -->
151
+ <?php include_once( 'upsell.php' ); ?>
152
+ </form>
153
+ </div>
views/upload.php CHANGED
@@ -1,192 +1,189 @@
1
  <?php
2
  namespace EnableMediaReplace;
3
 
4
- if ( ! defined( 'ABSPATH' ) )
5
- exit; // Exit if accessed directly.
 
6
 
7
  use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
8
  use EnableMediaReplace\Notices\NoticeController as Notices;
9
-
10
- if (!current_user_can('upload_files'))
11
- wp_die( esc_html__('You do not have permission to upload files.', 'enable-media-replace') );
12
-
13
- /*require_once('classes/replacer.php');
14
- require_once('classes/file.php'); */
15
-
16
  use \EnableMediaReplace\Replacer as Replacer;
17
 
 
 
 
 
18
  // Define DB table names
19
  global $wpdb;
20
  $table_name = $wpdb->prefix . "posts";
21
  $postmeta_table_name = $wpdb->prefix . "postmeta";
22
 
23
  // Starts processing.
24
- $uihelper = new UIHelper();
25
- $emr = EnableMediaReplacePlugin::get();
26
 
27
  // Get old guid and filetype from DB
28
  $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id.
29
- if (is_null($post_id))
30
- {
31
- wp_die( esc_html__('Error in request. Please try again', 'enable-media-replace') );
32
  }
33
  $attachment = get_post($post_id);
34
 
35
- if (! $emr->checkImagePermission($attachment->post_author, $attachment->ID))
36
- wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') );
 
37
 
38
  $replacer = new Replacer($post_id);
39
 
40
  // Massage a bunch of vars
41
  $ID = intval($_POST["ID"]); // legacy
42
  $replace_type = isset($_POST["replace_type"]) ? sanitize_text_field($_POST["replace_type"]) : false;
43
- $timestamp_replace = intval($_POST['timestamp_replace']);
44
 
45
  $redirect_error = $uihelper->getFailedRedirect($post_id);
46
  $redirect_success = $uihelper->getSuccesRedirect($post_id);
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  $do_new_location = isset($_POST['new_location']) ? sanitize_text_field($_POST['new_location']) : false;
49
  $new_location_dir = isset($_POST['location_dir']) ? sanitize_text_field($_POST['location_dir']) : null;
50
 
51
- $settings = array(); // save settings and show last loaded.
52
  $settings['replace_type'] = $replace_type;
53
  $settings['timestamp_replace'] = $timestamp_replace;
54
  $settings['new_location'] = $do_new_location;
55
  $settings['new_location_dir'] = $new_location_dir;
56
 
57
- switch($timestamp_replace)
58
- {
59
- case \EnableMediaReplace\Replacer::TIME_UPDATEALL:
60
- case \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED:
61
- $datetime = current_time('mysql');
62
- break;
63
- case \EnableMediaReplace\Replacer::TIME_CUSTOM:
64
- $custom_date = $_POST['custom_date_formatted'];
65
- $custom_hour = str_pad($_POST['custom_hour'],2,0, STR_PAD_LEFT);
66
- $custom_minute = str_pad($_POST['custom_minute'], 2, 0, STR_PAD_LEFT);
67
-
68
- // create a mysql time representation from what we have.
69
- Log::addDebug($_POST);
70
- Log::addDebug('Custom Date - ' . $custom_date . ' ' . $custom_hour . ':' . $custom_minute );
71
- $custom_date = \DateTime::createFromFormat('Y-m-d G:i', $custom_date . ' ' . $custom_hour . ':' . $custom_minute );
72
- if ($custom_date === false)
73
- {
74
-
75
- wp_safe_redirect($redirect_error);
76
- $errors = \DateTime::getLastErrors();
77
- $error = '';
78
- if (isset($errors['errors']))
79
- {
80
- $error = implode(',', $errors['errors']);
81
- }
82
- Notices::addError(sprintf(__('Invalid Custom Date. Please custom date values (%s)', 'enable-media-replace'), $error));
83
-
84
- exit();
85
- }
86
- $datetime = $custom_date->format("Y-m-d H:i:s");
87
- $settings['custom_date'] = $datetime;
88
- break;
89
  }
90
 
91
  update_option('enable_media_replace', $settings, false);
92
 
93
  // We have two types: replace / replace_and_search
94
- if ($replace_type == 'replace')
95
- {
96
- $replacer->setMode(\EnableMediaReplace\Replacer::MODE_REPLACE);
97
- $mode = \EnableMediaReplace\Replacer::MODE_REPLACE;
98
- }
99
- elseif ( 'replace_and_search' == $replace_type && apply_filters( 'emr_enable_replace_and_search', true ) )
100
- {
101
- $replacer->setMode(\EnableMediaReplace\Replacer::MODE_SEARCHREPLACE);
102
- $mode = \EnableMediaReplace\Replacer::MODE_SEARCHREPLACE;
103
-
104
- if ($do_new_location && ! is_null($new_location_dir))
105
- {
106
- $result = $replacer->setNewTargetLocation($new_location_dir);
107
- if (! $result)
108
- {
109
- wp_safe_redirect($redirect_error);
110
- exit();
111
- }
112
- }
113
  }
114
 
115
  $replacer->setTimeMode($timestamp_replace, $datetime);
116
 
117
  /** Check if file is uploaded properly **/
118
- if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) {
119
-
120
- Log::addDebug($_FILES['userfile']);
121
-
122
- // New method for validating that the uploaded file is allowed, using WP:s internal wp_check_filetype_and_ext() function.
123
- $filedata = wp_check_filetype_and_ext($_FILES["userfile"]["tmp_name"], $_FILES["userfile"]["name"]);
124
-
125
- Log::addDebug('Data after check', $filedata);
126
- if (isset($_FILES['userfile']['error']) && $_FILES['userfile']['error'] > 0)
127
- {
128
- $e = new RunTimeException('File Uploaded Failed');
129
- Notices::addError($e->getMessage());
130
- wp_safe_redirect($redirect_error);
131
- exit();
132
- }
133
-
134
-
135
- if ($filedata["ext"] == false && ! current_user_can( 'unfiltered_upload' )) {
136
-
137
- Notices::addError(esc_html__("File type does not meet security guidelines. Try another.", 'enable-media-replace') );
138
- wp_safe_redirect($redirect_error);
139
- exit();
140
- }
141
-
142
- // Here we have the uploaded file
143
- $new_filename = $_FILES["userfile"]["name"];
144
- //$new_filesize = $_FILES["userfile"]["size"]; // Seems not to be in use.
145
- $new_filetype = $filedata["type"];
146
-
147
- // Gather all functions that both options do.
148
- do_action('wp_handle_replace', array('post_id' => $post_id));
149
-
150
-
151
- /* if ($mode = \EnableMediaReplace\Replacer::MODE_SEARCHREPLACE && $do_new_location && ! is_null($new_location_dir))
152
- {
153
- exit($new_filename);
154
- $newdirfile = $replacer->newTargetLocation($new_location_dir);
155
- }
156
  */
157
- try
158
- {
159
- $result = $replacer->replaceWith($_FILES["userfile"]["tmp_name"], $new_filename);
160
- }
161
- catch(\RunTimeException $e)
162
- {
163
- Log::addError($e->getMessage());
164
- // exit($e->getMessage());
165
- }
166
-
167
- if (is_null($result))
168
- {
169
- wp_safe_redirect($redirect_error);
170
- exit();
171
- }
172
- // $returnurl = admin_url("/post.php?post={$_POST["ID"]}&action=edit&message=1");
173
-
174
- // Execute hook actions - thanks rubious for the suggestion!
175
-
176
  } else {
177
- //TODO Better error handling when no file is selected.
178
- //For now just go back to media management
179
- //$returnurl = admin_url("upload.php");
180
- Log::addInfo('Failed. Redirecting - '. $redirect_error);
181
- Notices::addError(__('File Upload seems to have failed. No files were returned by system','enable-media-replace'));
182
- wp_safe_redirect($redirect_error);
183
- exit();
184
  }
185
 
186
- Notices::addSuccess(__('File successfully replaced','enable-media-replace'));
 
 
 
187
 
188
  // Allow developers to override $returnurl
189
  //$returnurl = apply_filters('emr_returnurl', $returnurl);
190
  wp_redirect($redirect_success);
191
  exit();
192
- ?>
1
  <?php
2
  namespace EnableMediaReplace;
3
 
4
+ if (! defined('ABSPATH')) {
5
+ exit; // Exit if accessed directly.
6
+ }
7
 
8
  use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
9
  use EnableMediaReplace\Notices\NoticeController as Notices;
 
 
 
 
 
 
 
10
  use \EnableMediaReplace\Replacer as Replacer;
11
 
12
+ if (!current_user_can('upload_files')) {
13
+ wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace'));
14
+ }
15
+
16
  // Define DB table names
17
  global $wpdb;
18
  $table_name = $wpdb->prefix . "posts";
19
  $postmeta_table_name = $wpdb->prefix . "postmeta";
20
 
21
  // Starts processing.
22
+ $uihelper = emr()->uiHelper();
23
+ //$emr = EnableMediaReplacePlugin::get();
24
 
25
  // Get old guid and filetype from DB
26
  $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id.
27
+ if (is_null($post_id)) {
28
+ wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));
 
29
  }
30
  $attachment = get_post($post_id);
31
 
32
+ if (! emr()->checkImagePermission($attachment->post_author, $attachment->ID)) {
33
+ wp_die(esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace'));
34
+ }
35
 
36
  $replacer = new Replacer($post_id);
37
 
38
  // Massage a bunch of vars
39
  $ID = intval($_POST["ID"]); // legacy
40
  $replace_type = isset($_POST["replace_type"]) ? sanitize_text_field($_POST["replace_type"]) : false;
41
+ $timestamp_replace = isset($_POST['timestamp_replace']) ? intval($_POST['timestamp_replace']) : Replacer::TIME_UPDATEMODIFIED;
42
 
43
  $redirect_error = $uihelper->getFailedRedirect($post_id);
44
  $redirect_success = $uihelper->getSuccesRedirect($post_id);
45
+ if ( isset( $_POST['remove_after_progress'] ) ) {
46
+ $url = admin_url("upload.php");
47
+ $url = add_query_arg(array(
48
+ 'page' => 'enable-media-replace/enable-media-replace.php',
49
+ 'action' => 'emr_prepare_remove',
50
+ 'attachment_id' => $ID,
51
+ '_wpnonce' => wp_create_nonce('emr_prepare_remove')
52
+ ), $url);
53
+
54
+ $redirect_success = $url;
55
+ }
56
 
57
  $do_new_location = isset($_POST['new_location']) ? sanitize_text_field($_POST['new_location']) : false;
58
  $new_location_dir = isset($_POST['location_dir']) ? sanitize_text_field($_POST['location_dir']) : null;
59
 
60
+ $settings = get_option('enable_media_replace', array()); // save settings and show last loaded.
61
  $settings['replace_type'] = $replace_type;
62
  $settings['timestamp_replace'] = $timestamp_replace;
63
  $settings['new_location'] = $do_new_location;
64
  $settings['new_location_dir'] = $new_location_dir;
65
 
66
+ switch ($timestamp_replace) {
67
+ case \EnableMediaReplace\Replacer::TIME_UPDATEALL:
68
+ case \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED:
69
+ $datetime = current_time('mysql');
70
+ break;
71
+ case \EnableMediaReplace\Replacer::TIME_CUSTOM:
72
+ $custom_date = $_POST['custom_date_formatted'];
73
+ $custom_hour = str_pad($_POST['custom_hour'], 2, 0, STR_PAD_LEFT);
74
+ $custom_minute = str_pad($_POST['custom_minute'], 2, 0, STR_PAD_LEFT);
75
+
76
+ // create a mysql time representation from what we have.
77
+ Log::addDebug($_POST);
78
+ Log::addDebug('Custom Date - ' . $custom_date . ' ' . $custom_hour . ':' . $custom_minute);
79
+ $custom_date = \DateTime::createFromFormat('Y-m-d G:i', $custom_date . ' ' . $custom_hour . ':' . $custom_minute);
80
+ if ($custom_date === false) {
81
+ wp_safe_redirect($redirect_error);
82
+ $errors = \DateTime::getLastErrors();
83
+ $error = '';
84
+ if (isset($errors['errors'])) {
85
+ $error = implode(',', $errors['errors']);
86
+ }
87
+ Notices::addError(sprintf(__('Invalid Custom Date. Please check the custom date values: (%s)', 'enable-media-replace'), $error));
88
+
89
+ exit();
90
+ }
91
+ $datetime = $custom_date->format("Y-m-d H:i:s");
92
+ $settings['custom_date'] = $datetime;
93
+ break;
 
 
 
 
94
  }
95
 
96
  update_option('enable_media_replace', $settings, false);
97
 
98
  // We have two types: replace / replace_and_search
99
+ if ($replace_type == 'replace') {
100
+ $replacer->setMode(\EnableMediaReplace\Replacer::MODE_REPLACE);
101
+ $mode = \EnableMediaReplace\Replacer::MODE_REPLACE;
102
+ } elseif ('replace_and_search' == $replace_type && apply_filters('emr_enable_replace_and_search', true)) {
103
+ $replacer->setMode(\EnableMediaReplace\Replacer::MODE_SEARCHREPLACE);
104
+ $mode = \EnableMediaReplace\Replacer::MODE_SEARCHREPLACE;
105
+
106
+ if ($do_new_location && ! is_null($new_location_dir)) {
107
+ $result = $replacer->setNewTargetLocation($new_location_dir);
108
+ if (! $result) {
109
+ wp_safe_redirect($redirect_error);
110
+ exit();
111
+ }
112
+ }
 
 
 
 
 
113
  }
114
 
115
  $replacer->setTimeMode($timestamp_replace, $datetime);
116
 
117
  /** Check if file is uploaded properly **/
118
+ // @todo Post remove Bg should be removed.
119
+ if (is_uploaded_file($_FILES["userfile"]["tmp_name"]) || isset($_POST["remove_bg"])) {
120
+ Log::addDebug($_FILES['userfile']);
121
+
122
+ // New method for validating that the uploaded file is allowed, using WP:s internal wp_check_filetype_and_ext() function.
123
+ $filedata = wp_check_filetype_and_ext($_FILES["userfile"]["tmp_name"], $_FILES["userfile"]["name"]);
124
+
125
+ Log::addDebug('Data after check', $filedata);
126
+ if (isset($_FILES['userfile']['error']) && $_FILES['userfile']['error'] > 0) {
127
+ $e = new RunTimeException('File Uploaded Failed');
128
+ Notices::addError($e->getMessage());
129
+ wp_safe_redirect($redirect_error);
130
+ exit();
131
+ }
132
+
133
+
134
+ if ($filedata["ext"] == false && ! current_user_can('unfiltered_upload') && ! isset($_POST["remove_bg"])) {
135
+ Notices::addError(esc_html__("File type does not meet security guidelines. Try another.", 'enable-media-replace'));
136
+ wp_safe_redirect($redirect_error);
137
+ exit();
138
+ }
139
+
140
+ // Here we have the uploaded file
141
+ $new_filename = $_FILES["userfile"]["name"];
142
+ //$new_filesize = $_FILES["userfile"]["size"]; // Seems not to be in use.
143
+ $new_filetype = $filedata["type"] ? $filedata["type"] : $_FILES['userfile']['type'];
144
+
145
+ // Gather all functions that both options do.
146
+ do_action('wp_handle_replace', array('post_id' => $post_id));
147
+
148
+
149
+ /* if ($mode = \EnableMediaReplace\Replacer::MODE_SEARCHREPLACE && $do_new_location && ! is_null($new_location_dir))
150
+ {
151
+ exit($new_filename);
152
+ $newdirfile = $replacer->newTargetLocation($new_location_dir);
153
+ }
 
 
154
  */
155
+ try {
156
+ $result = $replacer->replaceWith($_FILES["userfile"]["tmp_name"], $new_filename , isset($_POST["remove_bg"]));
157
+ } catch (\RunTimeException $e) {
158
+ var_dump($e->getMessage());
159
+ die;
160
+ Log::addError($e->getMessage());
161
+ // exit($e->getMessage());
162
+ }
163
+
164
+ if (is_null($result)) {
165
+ wp_safe_redirect($redirect_error);
166
+ exit();
167
+ }
168
+ // $returnurl = admin_url("/post.php?post={$_POST["ID"]}&action=edit&message=1");
169
+
170
+ // Execute hook actions - thanks rubious for the suggestion!
 
 
 
171
  } else {
172
+ //TODO Better error handling when no file is selected.
173
+ //For now just go back to media management
174
+ //$returnurl = admin_url("upload.php");
175
+ Log::addInfo('Failed. Redirecting - '. $redirect_error);
176
+ Notices::addError(__('File Upload seems to have failed. No files were returned by system', 'enable-media-replace'));
177
+ wp_safe_redirect($redirect_error);
178
+ exit();
179
  }
180
 
181
+ $noticeController = Notices::getInstance();
182
+ $notice = Notices::addSuccess('<p>' . __('File successfully replaced', 'enable-media-replace') . '</p>');
183
+ $notice->is_removable = false;
184
+ $noticeController->update();
185
 
186
  // Allow developers to override $returnurl
187
  //$returnurl = apply_filters('emr_returnurl', $returnurl);
188
  wp_redirect($redirect_success);
189
  exit();
 
views/upsell.php CHANGED
@@ -36,7 +36,7 @@ if (! apply_filters('emr/upsell', current_user_can('install_plugins')))
36
 
37
  <div class='shortpixel-offer spio'>
38
  <div class='img-wrapper'>
39
- <img width="40" height="40" src="<?php echo $emr->getPluginURL('img/sp-logo-regular.svg') ?>" alt="ShortPixel">
40
  </div>
41
  <h4 class="grey">
42
  <?php echo esc_html__("ShortPixel Image Optimizer", "enable-media-replace"); ?>
@@ -69,7 +69,7 @@ if (! apply_filters('emr/upsell', current_user_can('install_plugins')))
69
 
70
  <div class='shortpixel-offer spai'>
71
  <div class='img-wrapper'>
72
- <img width="40" height="40" src="<?php echo $emr->getPluginURL('img/spai-logo.svg') ?>" alt="ShortPixel">
73
  </div>
74
  <h4 class="grey">
75
  <?php echo esc_html__("ShortPixel Adaptive Images", "enable-media-replace"); ?>
@@ -100,7 +100,7 @@ if (! apply_filters('emr/upsell', current_user_can('install_plugins')))
100
 
101
  <!--- WPSO -->
102
  <div class='shortpixel-offer site-speed'>
103
- <p class='img-wrapper'><img width="40" height="40" src="<?php echo $emr->getPluginURL('img/sp-logo-wink.svg'); ?>" alt='ShortPixel'></p>
104
  <h3><?php printf(__('GET AN ASSESSMENT FOR %s YOUR WEBSITE %s AND %s %s FIND OUT HOW TO MAKE IT FASTER %s', 'enable-media-replace'),'<br>', '<br>','<br>', '<span class="red">','</span>'); ?></h3>
105
 
106
  <p class='button-wrapper'><a href='https://wso.shortpixel.com/?utm_source=EMR' target="_blank"><?php _e('FIND OUT MORE', 'enable-media-replace') ?></a></p>
@@ -111,7 +111,7 @@ if (! apply_filters('emr/upsell', current_user_can('install_plugins')))
111
  <?php if (! $envira_pro_active): ?>
112
  <div class='envira-shortpixel-install shortpixel-offer'>
113
 
114
- <p class='img-wrapper'><img src="<?php echo $emr->getPluginURL('img/envira-logo.png'); ?>" alt='Envira Gallery'></p>
115
  <p><?php esc_html_e('Create beautiful, fast-loading photo & video galleries for your site in minutes.', 'enable-media-replace' ); ?></p>
116
 
117
  <?php
36
 
37
  <div class='shortpixel-offer spio'>
38
  <div class='img-wrapper'>
39
+ <img width="40" height="40" src="<?php echo emr()->getPluginURL('img/sp-logo-regular.svg') ?>" alt="ShortPixel">
40
  </div>
41
  <h4 class="grey">
42
  <?php echo esc_html__("ShortPixel Image Optimizer", "enable-media-replace"); ?>
69
 
70
  <div class='shortpixel-offer spai'>
71
  <div class='img-wrapper'>
72
+ <img width="40" height="40" src="<?php echo emr()->getPluginURL('img/spai-logo.svg') ?>" alt="ShortPixel">
73
  </div>
74
  <h4 class="grey">
75
  <?php echo esc_html__("ShortPixel Adaptive Images", "enable-media-replace"); ?>
100
 
101
  <!--- WPSO -->
102
  <div class='shortpixel-offer site-speed'>
103
+ <p class='img-wrapper'><img width="40" height="40" src="<?php echo emr()->getPluginURL('img/sp-logo-wink.svg'); ?>" alt='ShortPixel'></p>
104
  <h3><?php printf(__('GET AN ASSESSMENT FOR %s YOUR WEBSITE %s AND %s %s FIND OUT HOW TO MAKE IT FASTER %s', 'enable-media-replace'),'<br>', '<br>','<br>', '<span class="red">','</span>'); ?></h3>
105
 
106
  <p class='button-wrapper'><a href='https://wso.shortpixel.com/?utm_source=EMR' target="_blank"><?php _e('FIND OUT MORE', 'enable-media-replace') ?></a></p>
111
  <?php if (! $envira_pro_active): ?>
112
  <div class='envira-shortpixel-install shortpixel-offer'>
113
 
114
+ <p class='img-wrapper'><img src="<?php echo emr()->getPluginURL('img/envira-logo.png'); ?>" alt='Envira Gallery'></p>
115
  <p><?php esc_html_e('Create beautiful, fast-loading photo & video galleries for your site in minutes.', 'enable-media-replace' ); ?></p>
116
 
117
  <?php