c1b76676 by Jeff Balicki

media replace

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent 50aa63ba
Showing 111 changed files with 17346 additions and 0 deletions
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12 freedom to share and change it. By contrast, the GNU General Public
13 License is intended to guarantee your freedom to share and change free
14 software--to make sure the software is free for all its users. This
15 General Public License applies to most of the Free Software
16 Foundation's software and to any other program whose authors commit to
17 using it. (Some other Free Software Foundation software is covered by
18 the GNU Lesser General Public License instead.) You can apply it to
19 your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22 price. Our General Public Licenses are designed to make sure that you
23 have the freedom to distribute copies of free software (and charge for
24 this service if you wish), that you receive source code or can get it
25 if you want it, that you can change the software or use pieces of it
26 in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29 anyone to deny you these rights or to ask you to surrender the rights.
30 These restrictions translate to certain responsibilities for you if you
31 distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34 gratis or for a fee, you must give the recipients all the rights that
35 you have. You must make sure that they, too, receive or can get the
36 source code. And you must show them these terms so they know their
37 rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40 (2) offer you this license which gives you legal permission to copy,
41 distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44 that everyone understands that there is no warranty for this free
45 software. If the software is modified by someone else and passed on, we
46 want its recipients to know that what they have is not the original, so
47 that any problems introduced by others will not reflect on the original
48 authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51 patents. We wish to avoid the danger that redistributors of a free
52 program will individually obtain patent licenses, in effect making the
53 program proprietary. To prevent this, we have made it clear that any
54 patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57 modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63 a notice placed by the copyright holder saying it may be distributed
64 under the terms of this General Public License. The "Program", below,
65 refers to any such program or work, and a "work based on the Program"
66 means either the Program or any derivative work under copyright law:
67 that is to say, a work containing the Program or a portion of it,
68 either verbatim or with modifications and/or translated into another
69 language. (Hereinafter, translation is included without limitation in
70 the term "modification".) Each licensee is addressed as "you".
71
72 Activities other than copying, distribution and modification are not
73 covered by this License; they are outside its scope. The act of
74 running the Program is not restricted, and the output from the Program
75 is covered only if its contents constitute a work based on the
76 Program (independent of having been made by running the Program).
77 Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80 source code as you receive it, in any medium, provided that you
81 conspicuously and appropriately publish on each copy an appropriate
82 copyright notice and disclaimer of warranty; keep intact all the
83 notices that refer to this License and to the absence of any warranty;
84 and give any other recipients of the Program a copy of this License
85 along with the Program.
86
87 You may charge a fee for the physical act of transferring a copy, and
88 you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91 of it, thus forming a work based on the Program, and copy and
92 distribute such modifications or work under the terms of Section 1
93 above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114 These requirements apply to the modified work as a whole. If
115 identifiable sections of that work are not derived from the Program,
116 and can be reasonably considered independent and separate works in
117 themselves, then this License, and its terms, do not apply to those
118 sections when you distribute them as separate works. But when you
119 distribute the same sections as part of a whole which is a work based
120 on the Program, the distribution of the whole must be on the terms of
121 this License, whose permissions for other licensees extend to the
122 entire whole, and thus to each and every part regardless of who wrote it.
123
124 Thus, it is not the intent of this section to claim rights or contest
125 your rights to work written entirely by you; rather, the intent is to
126 exercise the right to control the distribution of derivative or
127 collective works based on the Program.
128
129 In addition, mere aggregation of another work not based on the Program
130 with the Program (or with a work based on the Program) on a volume of
131 a storage or distribution medium does not bring the other work under
132 the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135 under Section 2) in object code or executable form under the terms of
136 Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155 The source code for a work means the preferred form of the work for
156 making modifications to it. For an executable work, complete source
157 code means all the source code for all modules it contains, plus any
158 associated interface definition files, plus the scripts used to
159 control compilation and installation of the executable. However, as a
160 special exception, the source code distributed need not include
161 anything that is normally distributed (in either source or binary
162 form) with the major components (compiler, kernel, and so on) of the
163 operating system on which the executable runs, unless that component
164 itself accompanies the executable.
165
166 If distribution of executable or object code is made by offering
167 access to copy from a designated place, then offering equivalent
168 access to copy the source code from the same place counts as
169 distribution of the source code, even though third parties are not
170 compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173 except as expressly provided under this License. Any attempt
174 otherwise to copy, modify, sublicense or distribute the Program is
175 void, and will automatically terminate your rights under this License.
176 However, parties who have received copies, or rights, from you under
177 this License will not have their licenses terminated so long as such
178 parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181 signed it. However, nothing else grants you permission to modify or
182 distribute the Program or its derivative works. These actions are
183 prohibited by law if you do not accept this License. Therefore, by
184 modifying or distributing the Program (or any work based on the
185 Program), you indicate your acceptance of this License to do so, and
186 all its terms and conditions for copying, distributing or modifying
187 the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190 Program), the recipient automatically receives a license from the
191 original licensor to copy, distribute or modify the Program subject to
192 these terms and conditions. You may not impose any further
193 restrictions on the recipients' exercise of the rights granted herein.
194 You are not responsible for enforcing compliance by third parties to
195 this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198 infringement or for any other reason (not limited to patent issues),
199 conditions are imposed on you (whether by court order, agreement or
200 otherwise) that contradict the conditions of this License, they do not
201 excuse you from the conditions of this License. If you cannot
202 distribute so as to satisfy simultaneously your obligations under this
203 License and any other pertinent obligations, then as a consequence you
204 may not distribute the Program at all. For example, if a patent
205 license would not permit royalty-free redistribution of the Program by
206 all those who receive copies directly or indirectly through you, then
207 the only way you could satisfy both it and this License would be to
208 refrain entirely from distribution of the Program.
209
210 If any portion of this section is held invalid or unenforceable under
211 any particular circumstance, the balance of the section is intended to
212 apply and the section as a whole is intended to apply in other
213 circumstances.
214
215 It is not the purpose of this section to induce you to infringe any
216 patents or other property right claims or to contest validity of any
217 such claims; this section has the sole purpose of protecting the
218 integrity of the free software distribution system, which is
219 implemented by public license practices. Many people have made
220 generous contributions to the wide range of software distributed
221 through that system in reliance on consistent application of that
222 system; it is up to the author/donor to decide if he or she is willing
223 to distribute software through any other system and a licensee cannot
224 impose that choice.
225
226 This section is intended to make thoroughly clear what is believed to
227 be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230 certain countries either by patents or by copyrighted interfaces, the
231 original copyright holder who places the Program under this License
232 may add an explicit geographical distribution limitation excluding
233 those countries, so that distribution is permitted only in or among
234 countries not thus excluded. In such case, this License incorporates
235 the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238 of the General Public License from time to time. Such new versions will
239 be similar in spirit to the present version, but may differ in detail to
240 address new problems or concerns.
241
242 Each version is given a distinguishing version number. If the Program
243 specifies a version number of this License which applies to it and "any
244 later version", you have the option of following the terms and conditions
245 either of that version or of any later version published by the Free
246 Software Foundation. If the Program does not specify a version number of
247 this License, you may choose any version ever published by the Free Software
248 Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251 programs whose distribution conditions are different, write to the author
252 to ask for permission. For software which is copyrighted by the Free
253 Software Foundation, write to the Free Software Foundation; we sometimes
254 make exceptions for this. Our decision will be guided by the two goals
255 of preserving the free status of all derivatives of our free software and
256 of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285 possible use to the public, the best way to achieve this is to make it
286 free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289 to attach them to the start of each source file to most effectively
290 convey the exclusion of warranty; and each file should have at least
291 the "copyright" line and a pointer to where the full notice is found.
292
293 {description}
294 Copyright (C) {year} {fullname}
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License along
307 with this program; if not, write to the Free Software Foundation, Inc.,
308 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
310 Also add information on how to contact you by electronic and paper mail.
311
312 If the program is interactive, make it output a short notice like this
313 when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320 The hypothetical commands `show w' and `show c' should show the appropriate
321 parts of the General Public License. Of course, the commands you use may
322 be called something other than `show w' and `show c'; they could even be
323 mouse-clicks or menu items--whatever suits your program.
324
325 You should also get your employer (if you work as a programmer) or your
326 school, if any, to sign a "copyright disclaimer" for the program, if
327 necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 {signature of Ty Coon}, 1 April 1989
333 Ty Coon, President of Vice
334
335 This General Public License does not permit incorporating your program into
336 proprietary programs. If your program is a subroutine library, you may
337 consider it more useful to permit linking proprietary applications with the
338 library. If this is what you want to do, use the GNU Lesser General
339 Public License instead of this License.
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2 namespace EnableMediaReplace\Build;
3
4 class PackageLoader
5 {
6 public $dir;
7 public $composerFile = false;
8
9 public function __construct()
10 {
11
12 }
13
14 public function setComposerFile($filePath)
15 {
16 $this->composerFile = json_decode(file_get_contents($filePath),1);
17 }
18
19 public function getComposerFile($filePath = false )
20 {
21 if (! $this->composerFile)
22 $this->composerFile = json_decode(file_get_contents($this->dir."/composer.json"), 1);
23
24 return $this->composerFile;
25 }
26
27 public function load($dir)
28 {
29 $this->dir = $dir;
30 $composer = $this->getComposerFile();
31
32
33 if(isset($composer["autoload"]["psr-4"])){
34 $this->loadPSR4($composer['autoload']['psr-4']);
35 }
36 if(isset($composer["autoload"]["psr-0"])){
37 $this->loadPSR0($composer['autoload']['psr-0']);
38 }
39 if(isset($composer["autoload"]["files"])){
40 $this->loadFiles($composer["autoload"]["files"]);
41 }
42 }
43
44 public function loadFiles($files){
45 foreach($files as $file){
46 $fullpath = $this->dir."/".$file;
47 if(file_exists($fullpath)){
48 include_once($fullpath);
49 }
50 }
51 }
52
53 public function loadPSR4($namespaces)
54 {
55 $this->loadPSR($namespaces, true);
56 }
57
58 public function loadPSR0($namespaces)
59 {
60 $this->loadPSR($namespaces, false);
61 }
62
63 public function loadPSR($namespaces, $psr4)
64 {
65 $dir = $this->dir;
66 // Foreach namespace specified in the composer, load the given classes
67 foreach ($namespaces as $namespace => $classpaths) {
68 if (!is_array($classpaths)) {
69 $classpaths = array($classpaths);
70 }
71 spl_autoload_register(function ($classname) use ($namespace, $classpaths, $dir, $psr4) {
72 // Check if the namespace matches the class we are looking for
73 if (preg_match("#^".preg_quote($namespace)."#", $classname)) {
74 // Remove the namespace from the file path since it's psr4
75 if ($psr4) {
76 $classname = str_replace($namespace, "", $classname);
77 }
78
79 // $filename = preg_replace("#\\\\#", "", $classname).".php";
80 // This is fix for nested classes which were losing a /
81 $filename = ltrim($classname .'.php', '\\');
82 $filename = str_replace('\\','/', $filename);
83
84 foreach ($classpaths as $classpath) {
85 $fullpath = trailingslashit($dir) . trailingslashit($classpath) .$filename;
86 if (file_exists($fullpath)) {
87 include_once $fullpath;
88 }
89 }
90 }
91 });
92 }
93 }
94 }
1 <?php
2 require_once (__DIR__ . "/PackageLoader.php");
3 $loader = new EnableMediaReplace\Build\PackageLoader();
4 $loader->load(__DIR__);
5
...\ No newline at end of file ...\ No newline at end of file
1 {"name":"EnableMediaReplace\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"EnableMediaReplace\\Notices":"notices\/src","EnableMediaReplace\\ShortPixelLogger":"log\/src","EnableMediaReplace\\FileSystem":"filesystem\/src","EnableMediaReplace\\Replacer":"replacer\/src"}}}
...\ No newline at end of file ...\ No newline at end of file
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 }
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 public function getFullPathForWP(FileModel $file)
79 {
80 $fullpath = $file->getFullPath();
81 $abspath = $this->getWPAbsPath();
82
83 if (! strpos($abspath, $fullpath))
84 {
85
86 }
87
88 }
89
90
91 /** Utility function that tries to convert a file-path to a webURL.
92 *
93 * If possible, rely on other better methods to find URL ( e.g. via WP functions ).
94 */
95 public function pathToUrl(FileModel $file)
96 {
97 $filepath = $file->getFullPath();
98 $directory = $file->getFileDir();
99
100 $is_multi_site = (function_exists("is_multisite") && is_multisite()) ? true : false;
101 $is_main_site = is_main_site();
102
103 //$is_multi_site = $this->env->is_multisite;
104 //$is_main_site = $this->env->is_mainsite;
105
106 // stolen from wp_get_attachment_url
107 if ( ( $uploads = wp_get_upload_dir() ) && (false === $uploads['error'] || strlen(trim($uploads['error'])) == 0 ) ) {
108 // Check that the upload base exists in the file location.
109 if ( 0 === strpos( $filepath, $uploads['basedir'] ) ) { // Simple as it should, filepath and basedir share.
110 // Replace file location with url location.
111 $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $filepath );
112 }
113 // Multisite backups are stored under uploads/ShortpixelBackups/etc , but basedir would include uploads/sites/2 etc, not matching above
114 // If this is case, test if removing the last two directories will result in a 'clean' uploads reference.
115 // This is used by getting preview path ( backup pathToUrl) in bulk and for comparer..
116 elseif ($is_multi_site && ! $is_main_site && 0 === strpos($filepath, dirname(dirname($uploads['basedir']))) )
117 {
118
119 $url = str_replace( dirname(dirname($uploads['basedir'])), dirname(dirname($uploads['baseurl'])), $filepath );
120 $homeUrl = home_url();
121
122 // The result didn't end in a full URL because URL might have less subdirs ( dirname dirname) .
123 // This happens when site has blogs.dir (sigh) on a subdomain . Try to substitue the ABSPATH root with the home_url
124 if (strpos($url, $homeUrl) === false)
125 {
126 $url = str_replace( trailingslashit(ABSPATH), trailingslashit($homeUrl), $filepath);
127 }
128
129 } elseif ( false !== strpos( $filepath, 'wp-content/uploads' ) ) {
130 // Get the directory name relative to the basedir (back compat for pre-2.7 uploads)
131 //$relativePath = $this->getFile(_wp_get_attachment_relative_path( $filepath ) );
132 //$basename = wp_basename($relativePath->getFullPath());
133
134 $url = trailingslashit( $uploads['baseurl'] . '/' . _wp_get_attachment_relative_path( $filepath ) ) . wp_basename( $filepath );
135 } else {
136 // It's a newly-uploaded file, therefore $file is relative to the basedir.
137 $url = $uploads['baseurl'] . "/$filepath";
138 }
139 }
140
141 $wp_home_path = (string) $this->getWPAbsPath();
142 // If the whole WP homepath is still in URL, assume the replace when wrong ( not replaced w/ URL)
143 // This happens when file is outside of wp_uploads_dir
144 if (strpos($url, $wp_home_path) !== false)
145 {
146 // 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
147 // (1) ** This is a fix for a real-life issue, do not change if this causes issues, another fix is needed then.
148 // (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.
149 if ($is_multi_site && ! $is_main_site)
150 {
151 $wp_home_path = trailingslashit($uploads['basedir']);
152 $home_url = trailingslashit($uploads['baseurl']);
153 }
154 else
155 $home_url = trailingslashit(get_site_url()); // (1)
156 $url = str_replace($wp_home_path, $home_url, $filepath);
157 }
158
159 // can happen if there are WP path errors.
160 if (is_null($url))
161 return false;
162
163 $parsed = parse_url($url); // returns array, null, or false.
164
165 // 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.
166 if ( !isset($parsed['scheme']) ) {//no absolute URLs used -> we implement a hack
167
168 if (isset($parsed['host'])) // This is for URL's for // without http or https. hackhack.
169 {
170 $scheme = is_ssl() ? 'https:' : 'http:';
171 return $scheme. $url;
172 }
173 else
174 {
175 // From Metafacade. Multiple solutions /hacks.
176 $home_url = trailingslashit((function_exists("is_multisite") && is_multisite()) ? trim(network_site_url("/")) : trim(home_url()));
177 return $home_url . ltrim($url,'/');//get the file URL
178 }
179 }
180
181 if (! is_null($parsed) && $parsed !== false)
182 return $url;
183
184 return false;
185 }
186
187 /** Utility function to check if a path is an URL
188 * Checks if this path looks like an URL.
189 * @param $path String Path to check
190 * @return Boolean If path seems domain.
191 */
192 public function pathIsUrl($path)
193 {
194 $is_http = (substr($path, 0, 4) == 'http') ? true : false;
195 $is_https = (substr($path, 0, 5) == 'https') ? true : false;
196 $is_neutralscheme = (substr($path, 0, 2) == '//') ? true : false; // when URL is relative like //wp-content/etc
197 $has_urldots = (strpos($path, '://') !== false) ? true : false; // Like S3 offloads
198
199 if ($is_http || $is_https || $is_neutralscheme || $has_urldots)
200 return true;
201 else
202 return false;
203 }
204
205 /** Sort files / directories in a certain way.
206 * Future dev to include options via arg.
207 */
208 public function sortFiles($array, $args = array() )
209 {
210 if (count($array) == 0)
211 return $array;
212
213 // what are we sorting.
214 $class = get_class($array[0]);
215 $is_files = ($class == 'EnableMediaReplace\FileModel') ? true : false; // if not files, then dirs.
216
217 usort($array, function ($a, $b) use ($is_files)
218 {
219 if ($is_files)
220 return strcmp($a->getFileName(), $b->getFileName());
221 else {
222 return strcmp($a->getName(), $b->getName());
223 }
224 }
225 );
226
227 return $array;
228
229 }
230
231 public function downloadFile($url, $destinationPath)
232 {
233
234 //$downloadTimeout = defined(SHORTPIXEL_MAX_EXECUTION_TIME) ? max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15) :;
235 $max_exec = intval(ini_get('max_execution_time'));
236 if ($max_exec === 0) // max execution time of zero means infinite. Quantify.
237 $max_exec = 60;
238 elseif($max_exec < 0) // some hosts like to set negative figures on this. Ignore that.
239 $max_exec = 30;
240
241 $downloadTimeout = $max_exec;
242
243
244 $destinationFile = $this->getFile($destinationPath);
245
246 $args_for_get = array(
247 'stream' => true,
248 'filename' => $destinationPath,
249 );
250
251 $response = wp_remote_get( $url, $args_for_get );
252
253 if(is_wp_error( $response )) {
254 Log::addError('Download file failed', array($url, $response->get_error_messages(), $response->get_error_codes() ));
255
256 // Try to get it then via this way.
257 $response = download_url($url, $downloadTimeout);
258 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.
259 {
260 $tmpFile = $this->getFile($response);
261 $result = $tmpFile->move($destinationFile);
262
263 } // download_url ..
264 else {
265 Log::addError('Secondary download failed', array($url, $response->get_error_messages(), $response->get_error_codes() ));
266 }
267 }
268 else { // success, at least the download.
269 $destinationFile = $this->getFile($response['filename']);
270 }
271
272 Log::addDebug('Remote Download attempt result', array($url, $destinationPath));
273 if ($destinationFile->exists())
274 return true;
275 else
276 return false;
277 }
278
279
280
281 /** Get all files from a directory tree, starting at given dir.
282 * @param DirectoryModel $dir to recursive into
283 * @param Array $filters Collection of optional filters as accepted by FileFilter in directoryModel
284 * @return Array Array of FileModel Objects
285 **/
286 public function getFilesRecursive(DirectoryModel $dir, $filters = array() )
287 {
288 $fileArray = array();
289
290 if (! $dir->exists())
291 return $fileArray;
292
293 $files = $dir->getFiles($filters);
294 $fileArray = array_merge($fileArray, $files);
295
296 $subdirs = $dir->getSubDirectories();
297
298 foreach($subdirs as $subdir)
299 {
300 $fileArray = array_merge($fileArray, $this->getFilesRecursive($subdir, $filters));
301 }
302
303 return $fileArray;
304 }
305
306 // Url very sparingly.
307 public function url_exists($url)
308 {
309 if (! function_exists('curl_init'))
310 {
311 return null;
312 }
313
314 $ch = curl_init($url);
315 curl_setopt($ch, CURLOPT_NOBODY, true);
316 curl_exec($ch);
317 $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
318 curl_close($ch);
319
320 if ($responseCode == 200)
321 {
322 return true;
323 }
324 else {
325 return false;
326 }
327
328 }
329 }
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 }
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 $rawfullpath = null;
21 protected $filename = null; // filename + extension
22 protected $filebase = null; // filename without extension
23 protected $directory = null;
24 protected $extension = null;
25 protected $mime = null;
26 protected $permissions = null;
27
28 // File Status
29 protected $exists = null;
30 protected $is_writable = null;
31 protected $is_readable = null;
32 protected $is_file = null;
33 protected $is_virtual = false;
34
35 protected $status;
36
37 protected $backupDirectory;
38
39 const FILE_OK = 1;
40 const FILE_UNKNOWN_ERROR = 2;
41
42
43 /** Creates a file model object. FileModel files don't need to exist on FileSystem */
44 public function __construct($path)
45 {
46
47 $this->fullpath = trim($path);
48 $this->rawfullpath = $this->fullpath; // path without any doing.
49 $fs = $this->getFS();
50 if ($fs->pathIsUrl($path)) // Asap check for URL's to prevent remote wrappers from running.
51 {
52
53 $this->UrlToPath($path);
54 }
55 }
56
57 private function getFS()
58 {
59 return new \EnableMediaReplace\FileSystem\Controller\FileSystemController();
60 }
61
62 /* Get a string representation of file, the fullpath
63 * Note - this might be risky, without processedpath, in cases.
64 * @return String Full path processed or unprocessed.
65 */
66 public function __toString()
67 {
68 return (string) $this->fullpath;
69 }
70
71 protected function setFileInfo()
72 {
73 $processed_path = $this->processPath($this->fullpath);
74 if ($processed_path !== false)
75 $this->fullpath = $processed_path; // set processed path if that went alright
76
77
78 $info = $this->mb_pathinfo($this->fullpath);
79 // Todo, maybe replace this with splFileINfo.
80 if ($this->is_file()) // only set fileinfo when it's an actual file.
81 {
82 $this->filename = isset($info['basename']) ? $info['basename'] : null; // filename + extension
83 $this->filebase = isset($info['filename']) ? $info['filename'] : null; // only filename
84 $this->extension = isset($info['extension']) ? strtolower($info['extension']) : null; // only (last) extension
85 }
86
87 }
88
89 /** Call when file status changed, so writable / readable / exists are not reliable anymore */
90 public function resetStatus()
91 {
92 $this->is_writable = null;
93 $this->is_readable = null;
94 $this->is_file = null;
95 $this->exists = null;
96 $this->is_virtual = null;
97 }
98
99 public function exists()
100 {
101 if (is_null($this->exists))
102 {
103 $this->exists = (@file_exists($this->fullpath) && is_file($this->fullpath));
104 }
105
106 $this->exists = apply_filters('shortpixel_image_exists', $this->exists, $this->fullpath, $this); //legacy
107 $this->exists = apply_filters('shortpixel/file/exists', $this->exists, $this->fullpath, $this);
108 return $this->exists;
109 }
110
111 public function is_writable()
112 {
113 if ($this->is_virtual())
114 {
115 $this->is_writable = false; // can't write to remote files
116 }
117 elseif (is_null($this->is_writable))
118 {
119 if ($this->exists())
120 {
121 $this->is_writable = @is_writable($this->fullpath);
122 }
123 else // quite expensive check to see if file is writable.
124 {
125 $res = $this->create();
126 $this->delete();
127 $this->is_writable = $res;
128 }
129
130 }
131
132 return $this->is_writable;
133 }
134
135 public function is_readable()
136 {
137 if (is_null($this->is_readable))
138 $this->is_readable = @is_readable($this->fullpath);
139
140 return $this->is_readable;
141 }
142
143 // A file is virtual when the file is remote with URL and no local alternative is present.
144 public function is_virtual()
145 {
146 if ( is_null($this->is_virtual))
147 $this->is_virtual = false; // return bool
148 return $this->is_virtual;
149 }
150
151 /* Function checks if path is actually a file. This can be used to check possible confusion if a directory path is given to filemodel */
152 public function is_file()
153 {
154 if ($this->is_virtual()) // don't look further when virtual
155 {
156 $this->is_file = true;
157 return $this->is_file;
158 }
159 elseif (is_null($this->is_file))
160 {
161 if ($this->exists())
162 {
163 if (basename($this->fullpath) == '..' || basename($this->fullpath) == '.')
164 $this->is_file = false;
165 else
166 $this->is_file = is_file($this->fullpath);
167 }
168 else // file can not exist, but still have a valid filepath format. In that case, if file should return true.
169 {
170
171 /* 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. */
172
173 if (basename($this->fullpath) == '..' || basename($this->fullpath) == '.') // don't see this as file.
174 {
175 $this->is_file = false;
176 }
177 else if (! file_exists($this->fullpath) && ! is_dir($this->fullpath))
178 {
179 $this->is_file = true;
180 }
181 else //if (! is_file($this->fullpath)) // can be a non-existing directory. /
182 {
183 $this->is_file = false;
184 }
185
186 }
187 }
188 return $this->is_file;
189 }
190
191 public function getModified()
192 {
193 return filemtime($this->fullpath);
194 }
195
196 public function hasBackup()
197 {
198 $directory = $this->getBackupDirectory();
199 if (! $directory)
200 return false;
201
202 $backupFile = $directory . $this->getFileName();
203
204 if (file_exists($backupFile) && ! is_dir($backupFile) )
205 return true;
206 else {
207 return false;
208 }
209 }
210
211
212 /** Returns the Directory Model this file resides in
213 *
214 * @return DirectoryModel Directorymodel Object
215 */
216 public function getFileDir()
217 {
218 $fullpath = $this->getFullPath(); // triggers a file lookup if needed.
219 // create this only when needed.
220 if (is_null($this->directory) && strlen($fullpath) > 0)
221 {
222 // 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 )
223 $this->directory = new DirectoryModel($fullpath);
224 }
225
226 return $this->directory;
227 }
228
229 public function getFileSize()
230 {
231 if ($this->exists() && false === $this->is_virtual() )
232 {
233 return filesize($this->fullpath);
234 }
235 elseif (true === $this->is_virtual())
236 {
237 return -1;
238 }
239 else
240 return 0;
241 }
242
243 // Creates an empty file
244 public function create()
245 {
246 if (! $this->exists() )
247 {
248 $fileDir = $this->getFileDir();
249
250 if (! is_null($fileDir) && $fileDir->exists())
251 {
252 $res = @touch($this->fullpath);
253 $this->exists = $res;
254 return $res;
255 }
256 }
257 else
258 Log::addWarn('Could not create/write file: ' . $this->fullpath);
259
260 return false;
261 }
262
263 public function append($message)
264 {
265 if (! $this->exists() )
266 $this->create();
267
268 if (! $this->is_writable() )
269 {
270 Log::addWarn('File append failed on ' . $this->getFullPath() . ' - not writable');
271 return false;
272 }
273 $handle = fopen($this->getFullPath(), 'a');
274 fwrite($handle, $message);
275 fclose($handle);
276
277 return true;
278 }
279
280
281 /** Copy a file to somewhere
282 *
283 * @param $destination String Full Path to new file.
284 */
285 public function copy(FileModel $destination)
286 {
287 $sourcePath = $this->getFullPath();
288 $destinationPath = $destination->getFullPath();
289 Log::addDebug("Copy from $sourcePath to $destinationPath ");
290
291 if (! strlen($sourcePath) > 0 || ! strlen($destinationPath) > 0)
292 {
293 Log::addWarn('Attempted Copy on Empty Path', array($sourcePath, $destinationPath));
294 return false;
295 }
296
297 if (! $this->exists())
298 {
299 Log::addWarn('Tried to copy non-existing file - ' . $sourcePath);
300 return false;
301 }
302
303 $is_new = ($destination->exists()) ? false : true;
304 $status = @copy($sourcePath, $destinationPath);
305
306 if (! $status)
307 {
308 Log::addWarn('Could not copy file ' . $sourcePath . ' to' . $destinationPath);
309 }
310 else
311 {
312 $destination->resetStatus();
313 $destination->setFileInfo(); // refresh info.
314 }
315 //
316 do_action('shortpixel/filesystem/addfile', array($destinationPath, $destination, $this, $is_new));
317 return $status;
318 }
319
320 /** Move a file to somewhere
321 * This uses copy and delete functions and will fail if any of those fail.
322 * @param $destination String Full Path to new file.
323 */
324 public function move(FileModel $destination)
325 {
326 $result = false;
327 if ($this->copy($destination))
328 {
329 $result = $this->delete();
330 if ($result == false)
331 {
332 Log::addError('Move can\'t remove file ' . $this->getFullPath());
333 }
334
335 $this->resetStatus();
336 $destination->resetStatus();
337 }
338 return $result;
339 }
340
341 /** Deletes current file
342 * This uses the WP function since it has a filter that might be useful
343 */
344 public function delete()
345 {
346 if ($this->exists())
347 {
348 \wp_delete_file($this->fullpath); // delete file hook via wp_delete_file
349 }
350 else
351 {
352 Log::addWarn('Trying to remove non-existing file: ' . $this->getFullPath());
353 }
354
355 if (! file_exists($this->fullpath))
356 {
357 $this->resetStatus();
358 return true;
359 }
360 else {
361 $writable = ($this->is_writable()) ? 'true' : 'false';
362 Log::addWarn('File seems not removed - ' . $this->getFullPath() . ' (writable:' . $writable . ')');
363 return false;
364 }
365
366 }
367
368 public function getContents()
369 {
370 return file_get_contents($this->getFullPath());
371 }
372
373 public function getFullPath()
374 {
375 // filename here since fullpath is set unchecked in constructor, but might be a different take
376 if (is_null($this->filename))
377 {
378 $this->setFileInfo();
379 }
380
381 return $this->fullpath;
382 }
383
384 // 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.
385 // This function should *only* be used when processing mega amounts of files while not doing optimization or any processing.
386 // So far, testing use for file Filter */
387 public function getRawFullPath()
388 {
389 return $this->rawfullpath;
390 }
391
392 public function getFileName()
393 {
394 if (is_null($this->filename))
395 $this->setFileInfo();
396
397 return $this->filename;
398 }
399
400 public function getFileBase()
401 {
402 if (is_null($this->filebase))
403 $this->setFileInfo();
404
405 return $this->filebase;
406 }
407
408
409 public function getExtension()
410 {
411 if (is_null($this->extension))
412 $this->setFileInfo();
413
414 return $this->extension;
415 }
416
417 public function getMime()
418 {
419 if (is_null($this->mime))
420 $this->setFileInfo();
421
422 if ($this->exists() && ! $this->is_virtual() )
423 {
424 $this->mime = wp_get_image_mime($this->fullpath);
425 if (false === $this->mime)
426 {
427 $image_data = wp_check_filetype_and_ext($this->getFullPath(), $this->getFileName());
428 if (is_array($image_data) && isset($image_data['type']) && strlen($image_data['type']) > 0)
429 {
430 $this->mime = $image_data['type'];
431 }
432
433 }
434 }
435 else
436 $this->mime = false;
437
438 return $this->mime;
439 }
440
441
442 /* Internal function to check if path is a real path
443 * - Test for URL's based on http / https
444 * - Test if given path is absolute, from the filesystem root.
445 * @param $path String The file path
446 * @param String The Fixed filepath.
447 */
448 protected function processPath($path)
449 {
450 $original_path = $path;
451 $fs = $this->getFS();
452
453 if ($fs->pathIsUrl($path))
454 {
455 $path = $this->UrlToPath($path);
456 }
457
458 if ($path === false) // don't process further
459 return false;
460
461 //$path = wp_normalize_path($path);
462 $abspath = $fs->getWPAbsPath();
463
464 if ( is_file($path) && ! is_dir($path) ) // if path and file exist, all should be okish.
465 {
466 return $path;
467 }
468 // If attempted file does not exist, but the file is in a dir that exists, that is good enough.
469 elseif ( ! is_dir($path) && is_dir(dirname($path)) )
470 {
471 return $path;
472 }
473 // If path is not in the abspath, it might be relative.
474 elseif (strpos($path, $abspath->getPath()) === false)
475 {
476 // if path does not contain basepath.
477 //$uploadDir = $fs->getWPUploadBase();
478 //$abspath = $fs->getWPAbsPath();
479
480 $path = $this->relativeToFullPath($path);
481 }
482 $path = apply_filters('shortpixel/filesystem/processFilePath', $path, $original_path);
483 /* This needs some check here on malformed path's, but can't be test for existing since that's not a requirement.
484 if (file_exists($path) === false) // failed to process path to something workable.
485 {
486 // Log::addInfo('Failed to process path', array($path));
487 $path = false;
488 } */
489
490 return $path;
491 }
492
493
494
495 /** Resolve an URL to a local path
496 * This partially comes from WordPress functions attempting the same
497 * @param String $url The URL to resolve
498 * @return String/Boolean - False is this seems an external domain, otherwise resolved path.
499 */
500 private function UrlToPath($url)
501 {
502 //$uploadDir = wp_upload_dir();
503
504 $site_url = str_replace('http:', '', home_url('', 'http'));
505 $url = str_replace(array('http:', 'https:'), '', $url);
506 $fs = $this->getFS();
507
508 if (strpos($url, $site_url) !== false)
509 {
510 // try to replace URL for Path
511 $abspath = $this->getFS()->getWPAbsPath();
512 $path = str_replace($site_url, rtrim($abspath->getPath(),'/'), $url);
513
514
515 if (! $fs->pathIsUrl($path)) // test again.
516 {
517 return $path;
518 }
519 }
520
521 $this->is_virtual = true;
522
523 // This filter checks if some supplier will be able to handle the file when needed.
524 $path = apply_filters('shortpixel/image/urltopath', false, $url);
525
526 if ($path !== false)
527 {
528 $this->exists = true;
529 $this->is_readable = true;
530 $this->is_file = true;
531 }
532 else
533 {
534 $this->exists = false;
535 $this->is_readable = false;
536 $this->is_file = false;
537 }
538
539
540 return false; // seems URL from other server, use virtual mode.
541 }
542
543 /** Tries to find the full path for a perceived relative path.
544 *
545 * Relative path is detected on basis of WordPress ABSPATH. If this doesn't appear in the file path, it might be a relative path.
546 * Function checks for expections on this rule ( tmp path ) and returns modified - or not - path.
547 * @param $path The path for the file_exists
548 * @returns String The updated path, if that was possible.
549 */
550 private function relativeToFullPath($path)
551 {
552 $originalPath = $path; // for safe-keeping
553
554 // A file with no path, can never be created to a fullpath.
555 if (strlen($path) == 0)
556 return $path;
557
558 // if the file plainly exists, it's usable /**
559 if (file_exists($path))
560 {
561 return $path;
562 }
563
564 // Test if our 'relative' path is not a path to /tmp directory.
565
566 // This ini value might not exist.
567 $tempdirini = ini_get('upload_tmp_dir');
568 if ( (strlen($tempdirini) > 0) && strpos($path, $tempdirini) !== false)
569 return $path;
570
571 $tempdir = sys_get_temp_dir();
572 if ( (strlen($tempdir) > 0) && strpos($path, $tempdir) !== false)
573 return $path;
574
575 // Path contains upload basedir. This happens when upload dir is outside of usual WP.
576 $fs = $this->getFS();
577 $uploadDir = $fs->getWPUploadBase();
578 $abspath = $fs->getWPAbsPath();
579
580 if (strpos($path, $uploadDir->getPath()) !== false) // If upload Dir is feature in path, consider it ok.
581 {
582 return $path;
583 }
584 elseif (file_exists($abspath->getPath() . $path)) // If upload dir is abspath plus return path. Exceptions.
585 {
586 return $abspath->getPath() . $path;
587 }
588 elseif(file_exists($uploadDir->getPath() . $path)) // This happens when upload_dir is not properly prepended in get_attachment_file due to WP errors
589 {
590 return $uploadDir->getPath() . $path;
591 }
592
593 // this is probably a bit of a sharp corner to take.
594 // if path starts with / remove it due to trailingslashing ABSPATH
595 $path = ltrim($path, '/');
596 $fullpath = $abspath->getPath() . $path;
597
598 // We can't test for file_exists here, since file_model allows non-existing files.
599 // Test if directory exists, perhaps. Otherwise we are in for a failure anyhow.
600 //if (is_dir(dirname($fullpath)))
601 return $fullpath;
602 //else
603 // return $originalPath;
604 }
605
606 public function getPermissions()
607 {
608 if (is_null($this->permissions))
609 $this->permissions = fileperms($this->getFullPath()) & 0777;
610
611 return $this->permissions;
612 }
613
614 // @tozo Lazy IMplementation / copy, should be brought in line w/ other attributes.
615 public function setPermissions($permissions)
616 {
617 @chmod($this->fullpath, $permissions);
618 }
619
620
621 /** Fix for multibyte pathnames and pathinfo which doesn't take into regard the locale.
622 * This snippet taken from PHPMailer.
623 */
624 private function mb_pathinfo($path, $options = null)
625 {
626 $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
627 $pathinfo = [];
628 if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) {
629 if (array_key_exists(1, $pathinfo)) {
630 $ret['dirname'] = $pathinfo[1];
631 }
632 if (array_key_exists(2, $pathinfo)) {
633 $ret['basename'] = $pathinfo[2];
634 }
635 if (array_key_exists(5, $pathinfo)) {
636 $ret['extension'] = $pathinfo[5];
637 }
638 if (array_key_exists(3, $pathinfo)) {
639 $ret['filename'] = $pathinfo[3];
640 }
641 }
642 switch ($options) {
643 case PATHINFO_DIRNAME:
644 case 'dirname':
645 return $ret['dirname'];
646 case PATHINFO_BASENAME:
647 case 'basename':
648 return $ret['basename'];
649 case PATHINFO_EXTENSION:
650 case 'extension':
651 return $ret['extension'];
652 case PATHINFO_FILENAME:
653 case 'filename':
654 return $ret['filename'];
655 default:
656 return $ret;
657 }
658 }
659
660 public function __debuginfo()
661 {
662 return [
663 'fullpath' => $this->fullpath,
664 'filename' => $this->filename,
665 'filebase' => $this->filebase,
666 'exists' => $this->exists,
667 'is_writable' => $this->is_writable,
668 'is_readable' => $this->is_readable,
669 'is_virtual' => $this->is_virtual,
670 ];
671 }
672
673
674 } // FileModel Class
1 {
2 "name": "shortpixel/log",
3 "description": "ShortPixel Logging",
4 "version": "1.1.3",
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\\ShortPixelLogger\\" : "src" }
17 }
18 }
1 <?php
2 // The data models.
3 namespace EnableMediaReplace\ShortPixelLogger;
4
5
6 class DebugItem
7 {
8 protected $time;
9 protected $level;
10 protected $message;
11 protected $data = array();
12 protected $caller = false; // array when filled
13
14 protected $model;
15
16 const LEVEL_ERROR = 1;
17 const LEVEL_WARN = 2;
18 const LEVEL_INFO = 3;
19 const LEVEL_DEBUG = 4;
20
21 public function __construct($message, $args)
22 {
23 $this->level = $args['level'];
24 $data = $args['data'];
25
26 $this->message = $message;
27 $this->time = microtime(true);
28
29 $this->setCaller();
30
31 // Add message to data if it seems to be some debug variable.
32 if (is_object($this->message) || is_array($this->message))
33 {
34 $data[] = $this->message;
35 $this->message = __('[Data]');
36 }
37 if (is_array($data) && count($data) > 0)
38 {
39 $dataType = $this->getDataType($data);
40 if ($dataType == 1) // singular
41 {
42 $this->data[] = print_r($data, true);
43 }
44 if ($dataType == 2) //array or object.
45 {
46 $count = false;
47 if (gettype($data) == 'array')
48 $count = count($data);
49 elseif(gettype($data) == 'object')
50 $count = count(get_object_vars($data));
51
52 $firstLine = ucfirst(gettype($data)) . ':';
53 if ($count !== false)
54 $firstLine .= ' (' . $count . ')';
55
56 $this->data[] = $firstLine;
57
58 foreach($data as $index => $item)
59 {
60 if (is_object($item) || is_array($item))
61 {
62 $this->data[] = print_r($index, true) . ' ( ' . ucfirst(gettype($item)) . ') => ' . print_r($item, true);
63 }
64 }
65 }
66 } // if
67 elseif (! is_array($data)) // this leaves out empty default arrays
68 {
69 $this->data[] = print_r($data, true);
70 }
71 }
72
73 public function getData()
74 {
75 return array('time' => $this->time, 'level' => $this->level, 'message' => $this->message, 'data' => $this->data, 'caller' => $this->caller);
76 }
77
78 /** Test Data Array for possible values
79 *
80 * Data can be a collection of several debug vars, a single var, or just an normal array. Test if array has single types,
81 * which is a sign the array is not a collection.
82 */
83 protected function getDataType($data)
84 {
85 $single_type = array('integer', 'boolean', 'string');
86 if (in_array(gettype(reset($data)), $single_type))
87 {
88 return 1;
89 }
90 else
91 {
92 return 2;
93 }
94 }
95
96 public function getForFormat()
97 {
98 $data = $this->getData();
99 switch($this->level)
100 {
101 case self::LEVEL_ERROR:
102 $level = 'ERR';
103 $color = "\033[31m";
104 break;
105 case self::LEVEL_WARN:
106 $level = 'WRN';
107 $color = "\033[33m";
108 break;
109 case self::LEVEL_INFO:
110 $level = 'INF';
111 $color = "\033[37m";
112 break;
113 case self::LEVEL_DEBUG:
114 $level = 'DBG';
115 $color = "\033[37m";
116 break;
117
118 }
119 $color_end = "\033[0m";
120
121 $data['color'] = $color;
122 $data['color_end'] = $color_end;
123 $data['level'] = $level;
124
125 return $data;
126
127 //return array('time' => $this->time, 'level' => $level, 'message' => $this->message, 'data' => $this->data, 'color' => $color, 'color_end' => $color_end, 'caller' => $this->caller);
128
129 }
130
131 protected function setCaller()
132 {
133 if(PHP_VERSION_ID < 50400) {
134 $debug=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
135 } else {
136 $debug=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,5);
137 }
138
139 $i = 4;
140 if (isset($debug[$i]))
141 {
142 $info = $debug[$i];
143 $line = isset($info['line']) ? $info['line'] : 'Line unknown';
144 $file = isset($info['file']) ? basename($info['file']) : 'File not set';
145
146 $this->caller = array('line' => $line, 'file' => $file, 'function' => $info['function']);
147 }
148
149
150 }
151
152
153 }
1 <?php
2 namespace EnableMediaReplace\ShortPixelLogger;
3
4 /*** Logger class
5 *
6 * Class uses the debug data model for keeping log entries.
7 * Logger should not be called before init hook!
8 */
9 class ShortPixelLogger
10 {
11 static protected $instance = null;
12 protected $start_time;
13
14 protected $is_active = false;
15 protected $is_manual_request = false;
16 protected $show_debug_view = false;
17
18 protected $items = array();
19 protected $logPath = false;
20 protected $logMode = FILE_APPEND;
21
22 protected $logLevel;
23 protected $format = "[ %%time%% ] %%color%% %%level%% %%color_end%% \t %%message%% \t %%caller%% ( %%time_passed%% )";
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),
32 'shortpixel_image_urls' => array('numargs' => 2),
33 ); // @todo monitor hooks, but this should be more dynamic. Do when moving to module via config.
34 */
35
36 // utility
37 private $namespace;
38 private $view;
39
40 protected $template = 'view-debug-box';
41
42 /** Debugger constructor
43 * Two ways to activate the debugger. 1) Define SHORTPIXEL_DEBUG in wp-config.php. Either must be true or a number corresponding to required LogLevel
44 * 2) Put SHORTPIXEL_DEBUG in the request. Either true or number.
45 */
46 public function __construct()
47 {
48 $this->start_time = microtime(true);
49 $this->logLevel = DebugItem::LEVEL_WARN;
50
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
70 }
71 else if ( (defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG > 0) )
72 {
73 $this->is_active = true;
74 if (SHORTPIXEL_DEBUG === true)
75 $this->logLevel = DebugItem::LEVEL_INFO;
76 else {
77 $this->logLevel = intval(SHORTPIXEL_DEBUG);
78 }
79 }
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
86 }
87
88 if ($this->is_active)
89 {
90 /* On Early init, this function might not exist, then queue it when needed */
91 if (! function_exists('wp_get_current_user'))
92 add_action('init', array($this, 'initView'));
93 else
94 $this->initView();
95 }
96
97 if ($this->is_active && count($this->hooks) > 0)
98 $this->monitorHooks();
99 }
100
101 /** Init the view when needed. Private function ( public because of WP_HOOK )
102 * Never call directly */
103 public function initView()
104 {
105 $user_is_administrator = (current_user_can('manage_options')) ? true : false;
106
107 if ($this->is_active && $this->is_manual_request && $user_is_administrator )
108 {
109
110 $logPath = $this->logPath;
111 $uploads = wp_get_upload_dir();
112
113
114 if ( 0 === strpos( $logPath, $uploads['basedir'] ) ) { // Simple as it should, filepath and basedir share.
115 // Replace file location with url location.
116 $logLink = str_replace( $uploads['basedir'], $uploads['baseurl'], $logPath );
117 }
118
119
120 $this->view = new \stdClass;
121 $this->view->logLink = 'view-source:' . esc_url($logLink);
122 add_action('admin_footer', array($this, 'loadView'));
123 }
124 }
125
126 public static function getInstance()
127 {
128 if ( self::$instance === null)
129 {
130 self::$instance = new ShortPixelLogger();
131 }
132 return self::$instance;
133 }
134
135 public function setLogPath($logPath)
136 {
137 $this->logPath = $logPath;
138 $this->getWriteFile(true); // reset the writeFile here.
139 }
140 protected function addLog($message, $level, $data = array())
141 {
142 // $log = self::getInstance();
143
144 // don't log anything too low or when not active.
145 if ($this->logLevel < $level || ! $this->is_active)
146 {
147 return;
148 }
149
150 // Force administrator on manuals.
151 if ( $this->is_manual_request )
152 {
153 if (! function_exists('wp_get_current_user')) // not loaded yet
154 return false;
155
156 $user_is_administrator = (current_user_can('manage_options')) ? true : false;
157 if (! $user_is_administrator)
158 return false;
159 }
160
161 // Check where to log to.
162 if ($this->logPath === false)
163 {
164 $upload_dir = wp_upload_dir(null,false,false);
165 $this->logPath = $this->setLogPath($upload_dir['basedir'] . '/' . $this->namespace . ".log");
166 }
167
168 $arg = array();
169 $args['level'] = $level;
170 $args['data'] = $data;
171
172 $newItem = new DebugItem($message, $args);
173 $this->items[] = $newItem;
174
175 if ($this->is_active)
176 {
177 $this->write($newItem);
178 }
179 }
180
181 /** Writes to log File. */
182 protected function write($debugItem, $mode = 'file')
183 {
184 $items = $debugItem->getForFormat();
185 $items['time_passed'] = round ( ($items['time'] - $this->start_time), 5);
186 $items['time'] = date('Y-m-d H:i:s', (int) $items['time'] );
187
188 if ( ($items['caller']) && is_array($items['caller']) && count($items['caller']) > 0)
189 {
190 $caller = $items['caller'];
191 $items['caller'] = $caller['file'] . ' in ' . $caller['function'] . '(' . $caller['line'] . ')';
192 }
193
194 $line = $this->formatLine($items);
195
196 $file = $this->getWriteFile();
197
198 // try to write to file. Don't write if directory doesn't exists (leads to notices)
199 if ($file )
200 {
201 fwrite($file, $line);
202 // file_put_contents($this->logPath,$line, FILE_APPEND);
203 }
204 else {
205 // error_log($line);
206 }
207 }
208
209 protected function getWriteFile($reset = false)
210 {
211 if (! is_null($this->logFile) && $reset === false)
212 {
213 return $this->logFile;
214 }
215 elseif(is_object($this->logFile))
216 {
217 fclose($this->logFile);
218 }
219
220 $logDir = dirname($this->logPath);
221 if (! is_dir($logDir) || ! is_writable($logDir))
222 {
223 error_log('ShortpixelLogger: Log Directory is not writable');
224 $this->logFile = false;
225 return false;
226 }
227
228 $file = fopen($this->logPath, 'a');
229 if ($file === false)
230 {
231 error_log('ShortpixelLogger: File could not be opened / created: ' . $this->logPath);
232 $this->logFile = false;
233 return $file;
234 }
235
236 $this->logFile = $file;
237 return $file;
238 }
239
240 protected function formatLine($args = array() )
241 {
242 $line= $this->format;
243 foreach($args as $key => $value)
244 {
245 if (! is_array($value) && ! is_object($value))
246 $line = str_replace('%%' . $key . '%%', $value, $line);
247 }
248
249 $line .= PHP_EOL;
250
251 if (isset($args['data']))
252 {
253 $data = array_filter($args['data']);
254 if (count($data) > 0)
255 {
256 foreach($data as $item)
257 {
258 $line .= $item . PHP_EOL;
259 }
260 }
261 }
262
263 return $line;
264 }
265
266 protected function setLogLevel($level)
267 {
268 $this->logLevel = $level;
269 }
270
271 protected function getEnv($name)
272 {
273 if (isset($this->{$name}))
274 {
275 return $this->{$name};
276 }
277 else {
278 return false;
279 }
280 }
281
282 public static function addError($message, $args = array())
283 {
284 $level = DebugItem::LEVEL_ERROR;
285 $log = self::getInstance();
286 $log->addLog($message, $level, $args);
287 }
288 public static function addWarn($message, $args = array())
289 {
290 $level = DebugItem::LEVEL_WARN;
291 $log = self::getInstance();
292 $log->addLog($message, $level, $args);
293 }
294 // Alias, since it goes wrong so often.
295 public static function addWarning($message, $args = array())
296 {
297 self::addWarn($message, $args);
298 }
299 public static function addInfo($message, $args = array())
300 {
301 $level = DebugItem::LEVEL_INFO;
302 $log = self::getInstance();
303 $log->addLog($message, $level, $args);
304 }
305 public static function addDebug($message, $args = array())
306 {
307 $level = DebugItem::LEVEL_DEBUG;
308 $log = self::getInstance();
309 $log->addLog($message, $level, $args);
310 }
311
312 /** These should be removed every release. They are temporary only for d'bugging the current release */
313 public static function addTemp($message, $args = array())
314 {
315 self::addDebug($message, $args);
316 }
317
318 public static function logLevel($level)
319 {
320 $log = self::getInstance();
321 static::addInfo('Changing Log level' . $level);
322 $log->setLogLevel($level);
323 }
324
325 public static function getLogLevel()
326 {
327 $log = self::getInstance();
328 return $log->getEnv('logLevel');
329 }
330
331 public static function isManualDebug()
332 {
333 $log = self::getInstance();
334 return $log->getEnv('is_manual_request');
335 }
336
337 public static function getLogPath()
338 {
339 $log = self::getInstance();
340 return $log->getEnv('logPath');
341 }
342
343 /** Function to test if the debugger is active
344 * @return boolean true when active.
345 */
346 public static function debugIsActive()
347 {
348 $log = self::getInstance();
349 return $log->getEnv('is_active');
350 }
351
352 protected function monitorHooks()
353 {
354
355 foreach($this->hooks as $hook => $data)
356 {
357 $numargs = isset($data['numargs']) ? $data['numargs'] : 1;
358 $prio = isset($data['priority']) ? $data['priority'] : 10;
359
360 add_filter($hook, function($value) use ($hook) {
361 $args = func_get_args();
362 return $this->logHook($hook, $value, $args); }, $prio, $numargs);
363 }
364 }
365
366 public function logHook($hook, $value, $args)
367 {
368 array_shift($args);
369 self::addInfo('[Hook] - ' . $hook . ' with ' . var_export($value,true), $args);
370 return $value;
371 }
372
373 public function loadView()
374 {
375 // load either param or class template.
376 $template = $this->template;
377
378 $view = $this->view;
379 $view->namespace = $this->namespace;
380 $controller = $this;
381
382 $template_path = __DIR__ . '/' . $this->template . '.php';
383 if (file_exists($template_path))
384 {
385
386 include($template_path);
387 }
388 else {
389 self::addError("View $template for ShortPixelLogger could not be found in " . $template_path,
390 array('class' => get_class($this)));
391 }
392 }
393
394
395 } // class debugController
1 <?php
2 // Debug Box to load Log File
3 namespace EnableMediaReplace\ShortPixelLogger;
4 wp_enqueue_script( 'jquery-ui-draggable' );
5
6 ?>
7
8 <style>
9 .sp_debug_wrap
10 {
11 position: relative;
12 clear: both;
13 }
14 .sp_debug_box
15 {
16 position: absolute;
17 right: 0px;
18 top: 50px;
19 background-color: #fff;
20 width: 150px;
21 z-index: 1000000;
22 border: 1px solid #000;
23
24 }
25 .sp_debug_box .header
26 {
27 min-height: 10px;
28 background: #000;
29 color: #fff;
30 padding: 8px
31 }
32 .sp_debug_box .content_box
33 {
34 background: #ccc;
35 }
36 .content_box
37 {
38 padding: 8px;
39 }
40 </style>
41
42 <script language='javascript'>
43 jQuery(document).ready(function($)
44 {
45 $( ".sp_debug_box" ).draggable();
46
47 });
48 </script>
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 $view->logLink ?>'>Logfile</a>
54 <div class='content_box'>
55
56 </div>
57 </div>
1 {
2 "name": "shortpixel/notices",
3 "description": "ShortPixel WordPress Notice System",
4 "version": "1.5",
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 "shortpixel/log" : "1.1.*"
16 },
17 "repositories": [
18 {
19 "packagist.org": false,
20 "type": "path",
21 "url": "../modules/",
22 "options": {
23 "symlink": true
24 }
25 }
26 ],
27
28 "autoload": {
29 "psr-4": { "ShortPixel\\Notices\\" : "src" }
30 }
31 }
1 <?php
2 namespace EnableMediaReplace\Notices;
3 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4
5 class NoticeController //extends ShortPixelController
6 {
7 protected static $notices = array();
8 protected static $instance = null;
9 protected static $cssHookLoaded = false; // prevent css output more than once.
10
11 protected $notice_displayed = array();
12
13 public $notice_count = 0;
14
15 protected $has_stored = false;
16
17 protected $notice_option = ''; // The wp_options name for notices here.
18
19 /** For backward compat. Never call constructor directly. */
20 public function __construct()
21 {
22 $ns = __NAMESPACE__;
23 $ns = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
24 $this->notice_option = $ns . '-notices';
25
26 add_action('wp_ajax_' . $this->notice_option, array($this, 'ajax_action'));
27
28 $this->loadNotices();
29 //$this->loadConfig();
30 }
31
32 public static function getInstance()
33 {
34 if ( self::$instance === null)
35 {
36 self::$instance = new NoticeController();
37 }
38
39 return self::$instance;
40 }
41
42 /** Reset all notices, before loading them, to ensure on updates / activations one starts fresh */
43 public static function resetNotices()
44 {
45 $ns = __NAMESPACE__;
46 $ns = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
47 $result = delete_option($ns . '-notices');
48 }
49
50 /** Load Notices Config File, if any
51 *
52 * [ Future Use ]
53 */
54 public function loadConfig()
55 {
56 return;
57 if (file_exists('../notice_config.json'))
58 {
59 $config = file_get_contents('../notice_config.json');
60 $json_config = json_decode($config);
61 }
62 }
63
64 public function loadIcons($icons)
65 {
66 foreach($icons as $name => $icon)
67 NoticeModel::setIcon($name, $icon);
68 }
69
70
71 protected function loadNotices()
72 {
73 $notices = get_option($this->notice_option, false);
74 $cnotice = (is_array($notices)) ? count($notices) : 0;
75
76 if ($notices !== false && is_array($notices))
77 {
78 $checked = array();
79 foreach($notices as $noticeObj)
80 {
81 if (is_object($noticeObj) && $noticeObj instanceOf NoticeModel)
82 {
83 $checked[] = $noticeObj;
84 }
85 }
86 self::$notices = $checked;
87 $this->has_stored = true;
88 }
89 else {
90 self::$notices = array();
91 $this->has_stored = false;
92 }
93 $this->countNotices();
94 }
95
96
97 protected function addNotice($message, $code, $unique)
98 {
99 $notice = new NoticeModel($message, $code);
100
101 if ($unique)
102 {
103 foreach(self::$notices as $nitem)
104 {
105 if ($nitem->message == $notice->message && $nitem->code == $notice->code) // same message.
106 return $nitem; // return the notice with the same message.
107 }
108 }
109 self::$notices[] = $notice;
110 $this->countNotices();
111
112 $this->update();
113 return $notice;
114 }
115
116 /** Update the notices to store, check what to remove, returns count. */
117 public function update()
118 {
119 if (! is_array(self::$notices) || count(self::$notices) == 0)
120 {
121 if ($this->has_stored)
122 delete_option($this->notice_option);
123
124 return 0;
125 }
126
127 $new_notices = array();
128 foreach(self::$notices as $item)
129 {
130 if (! $item->isDone() )
131 {
132 $new_notices[] = $item;
133 }
134 }
135
136 update_option($this->notice_option, $new_notices);
137 self::$notices = $new_notices;
138
139 return $this->countNotices();
140 }
141
142 public function countNotices()
143 {
144 $this->notice_count = count(self::$notices);
145 return $this->notice_count;
146 }
147
148
149 public function getNotices()
150 {
151 return self::$notices;
152 }
153
154 public function getNoticesForDisplay()
155 {
156 $newNotices = array();
157
158 foreach(self::$notices as $notice)
159 {
160 if ($notice->isDismissed()) // dismissed never displays.
161 continue;
162
163 if ($notice->isPersistent())
164 {
165 $id = $notice->getID();
166 if (! is_null($id) && ! in_array($id, $this->notice_displayed))
167 {
168 $notice->notice_action = $this->notice_option;
169 $newNotices[] = $notice;
170 $this->notice_displayed[] = $id;
171 }
172
173 }
174 else
175 $newNotices[] = $notice;
176
177
178 }
179 return $newNotices;
180 }
181
182
183 public function getNoticeByID($id)
184 {
185 foreach(self::$notices as $notice)
186 {
187 if ($notice->getID() == $id)
188 return $notice;
189 }
190
191 return false;
192 }
193
194 public static function removeNoticeByID($id)
195 {
196 $noticeController = self::getInstance();
197
198 for($i = 0; $i < count(self::$notices); $i++)
199 {
200 $item = self::$notices[$i];
201 if (is_object($item) && $item->getID() == $id)
202 {
203 Log::addDebug('Removing notice with ID ' . $id);
204 unset(self::$notices[$i]);
205 }
206 //if ($notice_item )
207 }
208 $noticeController->update();
209 }
210
211 public function ajax_action()
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();
235 $response['result'] = true;
236 }
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 }
245
246 }
247 else
248 {
249 Log::addError('Wrong Nonce when dismissed notice. ');
250 $response['reason'] = 'wrong nonce';
251 }
252 wp_send_json($response);
253 }
254
255 /** Adds a notice, quick and fast method
256 * @param String $message The Message you want to notify
257 * @param Boolean $unique If unique, check to not repeat notice exact same text in notices. Discard if so
258 * @param int $code A value of messageType as defined in model
259 * @returm Object Instance of noticeModel
260 */
261
262 public static function addNormal($message, $unique = false)
263 {
264 $noticeController = self::getInstance();
265 $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_NORMAL, $unique);
266 return $notice;
267
268 }
269
270 public static function addError($message, $unique = false)
271 {
272 $noticeController = self::getInstance();
273 $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_ERROR, $unique);
274 return $notice;
275
276 }
277
278 public static function addWarning($message, $unique = false)
279 {
280 $noticeController = self::getInstance();
281 $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_WARNING, $unique);
282 return $notice;
283 }
284
285 public static function addSuccess($message, $unique = false)
286 {
287 $noticeController = self::getInstance();
288 $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_SUCCESS, $unique);
289 return $notice;
290
291 }
292
293 public static function addDetail($notice, $detail)
294 {
295 $noticeController = self::getInstance();
296 $notice->addDetail($detail);
297
298 // $notice_id = spl_object_id($notice);
299
300 $noticeController->update();
301 }
302
303 /** Make a regular notice persistent across multiple page loads
304 * @param $notice NoticeModel The Notice to make Persistent
305 * @param $key String Identifier of the persistent notice.
306 * @param $suppress Int When dismissed, time to stay dismissed
307 * @param $callback Function Callable function
308 */
309 public static function makePersistent($notice, $key, $suppress = -1, $callback = null)
310 {
311 $noticeController = self::getInstance();
312 $existing = $noticeController->getNoticeByID($key);
313
314 // if this key already exists, don't allow the new notice to be entered into the array. Remove it since it's already created.
315 if ($existing)
316 {
317 for($i = 0; $i < count(self::$notices); $i++)
318 {
319 $item = self::$notices[$i];
320
321 if ($item->message == $notice->message && $item->getID() == null)
322 {
323 if ($item->message != $existing->message) // allow the persistent message to be updated, if something else is served on this ID
324 {
325 $existing->message = $item->message;
326 }
327 unset(self::$notices[$i]);
328 }
329 //if ($notice_item )
330 }
331 }
332 else
333 {
334 $notice->setPersistent($key, $suppress, $callback); // set this notice persistent.
335 }
336
337 $noticeController->update();
338 }
339
340 public function admin_notices()
341 {
342 if ($this->countNotices() > 0)
343 {
344 if (! self::$cssHookLoaded)
345 {
346 add_action('admin_print_footer_scripts', array($this, 'printNoticeStyle'));
347 self::$cssHookLoaded = true;
348 }
349 foreach($this->getNoticesForDisplay() as $notice)
350 {
351 echo $notice->getForDisplay();
352 }
353 }
354 $this->update(); // puts views, and updates
355 }
356
357
358 public function printNoticeStyle()
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');
366 }
367 }
368
369
370
371
372 }
1 <?php
2 namespace EnableMediaReplace\Notices;
3
4 class NoticeModel //extends ShortPixelModel
5 {
6 public $message; // The message we want to convey.
7 public $details = array(); // extra details, like the files involved. Something could be hideable in the future.
8 public $code;
9
10 private $id = null; // used for persistent messages.
11 protected $viewed = false; // was this notice viewed?
12 protected $is_persistent = false; // This is a fatal issue, display until something was fixed.
13 protected $is_dismissed = false; // for persistent notices,
14 protected $suppress_until = null;
15 protected $suppress_period = -1;
16 protected $include_screens = array();
17 protected $exclude_screens = array();
18 public $is_removable = true; // if removable, display a notice dialog with red X or so.
19 public $messageType = self::NOTICE_NORMAL;
20
21 public $notice_action; // empty unless for display. Ajax action to talk back to controller.
22 protected $callback; // empty unless callback is needed
23
24 public static $icons = array();
25
26 private static $jsDismissLoaded;
27
28 const NOTICE_NORMAL = 1;
29 const NOTICE_ERROR = 2;
30 const NOTICE_SUCCESS = 3;
31 const NOTICE_WARNING = 4;
32
33 /** Use this model in conjunction with NoticeController, do not call directly */
34 public function __construct($message, $messageType = self::NOTICE_NORMAL)
35 {
36 $this->message = $message;
37 $this->messageType = $messageType;
38 }
39
40 public function isDone()
41 {
42 // check suppressed
43 if ($this->is_dismissed && ! is_null($this->suppress_until))
44 {
45 if (time() >= $this->suppress_until)
46 {
47 $this->is_persistent = false; // unpersist, so it will be cleaned and dropped.
48 }
49 }
50
51 if ($this->viewed && ! $this->is_persistent)
52 return true;
53 else
54 return false;
55 }
56
57 public function getID()
58 {
59 return $this->id;
60 }
61
62 public function isPersistent()
63 {
64 return $this->is_persistent;
65 }
66
67 public function isDismissed()
68 {
69 return $this->is_dismissed;
70 }
71
72 public function dismiss()
73 {
74 $this->is_dismissed = true;
75 $this->suppress_until = time() + $this->suppress_period;
76 }
77
78 public function unDismiss()
79 {
80 $this->is_dismissed = false;
81 }
82
83 public function setDismissedUntil($timestamp)
84 {
85 $this->suppress_until = $timestamp;
86 }
87
88 /** Support for extra information beyond the message.
89 * Can help to not overwhelm users w/ the same message but different file /circumstances.
90 */
91 public function addDetail($detail, $clean = false)
92 {
93 if ($clean)
94 $this->details = array();
95
96 if (! in_array($detail, $this->details) )
97 $this->details[] = $detail;
98 }
99
100 /**
101 * @param $method String Include or Exclude
102 * @param $includes String|Array Screen Names to Include / Exclude either string, or array
103 */
104 public function limitScreens($method, $screens)
105 {
106 if ($method == 'exclude')
107 {
108 $var = 'exclude_screens';
109 }
110 else {
111 $var = 'include_screens';
112 }
113
114 if (is_array($screens))
115 {
116 $this->$var = array_merge($this->$var, $screens);
117 }
118 else {
119 $this->{$var}[] = $screens; // strange syntax is PHP 5.6 compat.
120 }
121 }
122
123 /* Checks if Notice is allowed on this screen
124 * @param @screen_id String The screen Id to check ( most likely current one, via EnvironmentModel)
125 */
126 public function checkScreen($screen_id)
127 {
128 if (in_array($screen_id, $this->exclude_screens))
129 {
130 return false;
131 }
132 if (in_array($screen_id, $this->include_screens))
133 {
134 return true;
135 }
136
137 // if include is set, don't show if not screen included.
138 if (count($this->include_screens) == 0)
139 {
140 return true;
141 }
142 else {
143 return false;
144 }
145 }
146
147
148
149 /** Set a notice persistent. Meaning it shows every page load until dismissed.
150 * @param $key Unique Key of this message. Required
151 * @param $suppress When dismissed do not show this message again for X amount of time. When -1 it will just be dropped from the Notices and not suppressed
152 */
153 public function setPersistent($key, $suppress = -1, $callback = null)
154 {
155 $this->id = $key;
156 $this->is_persistent = true;
157 $this->suppress_period = $suppress;
158 if ( ! is_null($callback) && is_callable($callback))
159 {
160 $this->callback = $callback;
161 }
162 }
163
164 public static function setIcon($notice_type, $icon)
165 {
166 switch($notice_type)
167 {
168 case 'error':
169 $type = self::NOTICE_ERROR;
170 break;
171 case 'success':
172 $type = self::NOTICE_SUCCESS;
173 break;
174 case 'warning':
175 $type = self::NOTICE_WARNING;
176 break;
177 case 'normal':
178 default:
179 $type = self::NOTICE_NORMAL;
180 break;
181 }
182 self::$icons[$type] = $icon;
183 }
184
185 public function _debug_getvar($var)
186 {
187 if (property_exists($this, $var))
188 {
189 return $this->$var;
190 }
191 }
192
193 private function checkIncomplete($var)
194 {
195 return ($var instanceof \__PHP_Incomplete_Class);
196 }
197
198 public function getForDisplay()
199 {
200 $this->viewed = true;
201 $class = 'shortpixel shortpixel-notice ';
202
203 $icon = '';
204
205 if ($this->callback)
206 {
207 if (is_array($this->callback))
208 {
209 foreach($this->callback as $part)
210 {
211 if ($this->checkIncomplete($part) === true)
212 {
213 return false;
214 }
215 }
216 } elseif (is_object($this->callback))
217 {
218 if ($this->checkIncomplete($part) === true)
219 return false;
220 }
221
222 if (! is_callable($this->callback))
223 {
224 return;
225 }
226 else {
227 $return = call_user_func($this->callback, $this);
228 if ($return === false) // don't display is callback returns false explicitly.
229 return;
230
231 }
232 }
233
234 switch($this->messageType)
235 {
236 case self::NOTICE_ERROR:
237 $class .= 'notice-error ';
238 $icon = isset(self::$icons[self::NOTICE_ERROR]) ? self::$icons[self::NOTICE_ERROR] : '';
239 //$icon = 'scared';
240 break;
241 case self::NOTICE_SUCCESS:
242 $class .= 'notice-success ';
243 $icon = isset(self::$icons[self::NOTICE_SUCCESS]) ? self::$icons[self::NOTICE_SUCCESS] : '';
244 break;
245 case self::NOTICE_WARNING:
246 $class .= 'notice-warning ';
247 $icon = isset(self::$icons[self::NOTICE_WARNING]) ? self::$icons[self::NOTICE_WARNING] : '';
248 break;
249 case self::NOTICE_NORMAL:
250 $class .= 'notice-info ';
251 $icon = isset(self::$icons[self::NOTICE_NORMAL]) ? self::$icons[self::NOTICE_NORMAL] : '';
252 break;
253 default:
254 $class .= 'notice-info ';
255 $icon = '';
256 break;
257 }
258
259
260 if ($this->is_removable)
261 {
262 $class .= 'is-dismissible ';
263 }
264
265 if ($this->is_persistent)
266 {
267 $class .= 'is-persistent ';
268 }
269
270 $id = ! is_null($this->id) ? $this->id : uniqid();
271 //'id="' . $this->id . '"'
272 $output = "<div id='$id' class='$class'><span class='icon'> " . $icon . "</span> <span class='content'>" . $this->message;
273 if ($this->hasDetails())
274 {
275 $output .= '<div class="details-wrapper">
276 <input type="checkbox" name="detailhider" id="check-' . $id .'">
277 <label for="check-' . $id . '" class="show-details"><span>' . __('See Details', 'shortpixel-image-optimiser') . '</span>
278 </label>';
279
280 $output .= "<div class='detail-content-wrapper'><p class='detail-content'>" . $this->parseDetails() . "</p></div>";
281 $output .= '<label for="check-' . $id . '" class="hide-details"><span>' . __('Hide Details', 'shortpixel-image-optimiser') . '</span></label>';
282
283 $output .= '</div>'; // detail wrapper
284
285 }
286 $output .= "</span>";
287
288 if ($this->is_removable)
289 {
290 $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>';
291
292 if (! $this->is_persistent)
293 {
294 $output .= "<script type='text/javascript'>\n
295 document.getElementById('button-$id').onclick = function()
296 {
297 var el = document.getElementById('$id');
298 jQuery(el).fadeTo(100,0,function() {
299 jQuery(el).slideUp(100, 0, function () {
300 jQuery(el).remove();
301 })
302 });
303 } </script>";
304 }
305 }
306
307 $output .= "</div>";
308
309 if ($this->is_persistent && $this->is_removable)
310 {
311 $output .= "<script type='text/javascript'>\n" . $this->getDismissJS() . "\n</script>";
312 }
313 return $output;
314
315 }
316
317 protected function hasDetails()
318 {
319 if (is_array($this->details) && count($this->details) > 0)
320 return true;
321 else
322 return false;
323 }
324
325 protected function parseDetails()
326 {
327 return implode('<BR>', $this->details);
328 }
329
330 private function getDismissJS()
331 {
332
333 $js = '';
334 if (is_null(self::$jsDismissLoaded))
335 {
336 $nonce = wp_create_nonce('dismiss');
337 $url = wp_json_encode(admin_url('admin-ajax.php'));
338 $js = "function shortpixel_notice_dismiss(event) {
339 event.preventDefault();
340 var ev = event.detail;
341 var target = event.target;
342 var parent = target.parentElement;
343
344 var data = {
345 'plugin_action': 'dismiss',
346 'action' : '$this->notice_action',
347 'nonce' : '$nonce',
348 }
349 data.time = target.getAttribute('data-dismiss');
350 data.id = parent.getAttribute('id');
351 jQuery.post($url,data);
352
353 jQuery(parent).fadeTo(100,0,function() {
354 jQuery(parent).slideUp(100, 0, function () {
355 jQuery(parent).remove();
356 })
357 });
358 }";
359 }
360
361 $js .= ' jQuery("#' . $this->id . '").find(".notice-dismiss").on("click", shortpixel_notice_dismiss); ';
362
363 return "\n jQuery(document).ready(function(){ \n" . $js . "\n});";
364 }
365
366 }
1 .shortpixel.shortpixel-notice {
2 min-height: 75px;
3 padding: 8px;
4 display: flex;
5 align-items: center;
6 background: #fff;
7 padding: 1px 12px;
8 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
9 border: 1px solid #c3c4c7;
10 margin: 15px 0;
11 border-left-width: 4px;
12 border-left-color: #72aee6;
13 position: relative;
14 }
15 .shortpixel.shortpixel-notice span {
16 vertical-align: middle;
17 }
18 .shortpixel.shortpixel-notice span.icon {
19 margin: 0 25px 0 0;
20 width: 80px;
21 }
22 .shortpixel.shortpixel-notice span.content {
23 padding: 8px 0;
24 word-wrap: break-word;
25 overflow: hidden;
26 }
27 .shortpixel.shortpixel-notice img {
28 display: inline-block;
29 margin: 0 25px 0 0;
30 max-height: 50px;
31 }
32 .shortpixel.shortpixel-notice .notice-dismiss {
33 margin-top: 6px;
34 }
35 .shortpixel.shortpixel-notice.notice-success {
36 border-left-color: #00a32a;
37 }
38 .shortpixel.shortpixel-notice.notice-warning {
39 border-left-color: #dba617;
40 }
41 .shortpixel.shortpixel-notice.notice-error {
42 border-left-color: #ff0000;
43 }
44 .shortpixel.shortpixel-notice.notice-info {
45 border-left-color: #72aee6;
46 }
47
48 /* In-view notice ( not on top, between the options ) - styled after WP notice */
49 .view-notice {
50 box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
51 border: 4px solid #fff;
52 padding: 1px 12px;
53 }
54 .view-notice p {
55 margin: 1em 0 !important;
56 }
57 .view-notice.warning {
58 border-left-color: #ffb900;
59 }
60
61 .view-notice-row {
62 display: none;
63 }
64
65 /*# sourceMappingURL=notices.css.map */
1 {"version":3,"sourceRoot":"","sources":["notices.scss"],"names":[],"mappings":"AACA;EAGC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEC;;AACA;EACC;EACA;;AAED;EAEC;EACA;EACA;;AAKA;EAEE;EACA;EACA;;AAEF;EAEE;;AAGH;EAEC;;AAED;EAEC;;AAED;EAEC;;AAED;EAEE;;;AAIJ;AACA;EAGE;EACA;EAEA;;AACA;EACE;;AAEF;EAEE;;;AAIJ;EAEE","file":"notices.css"}
...\ No newline at end of file ...\ No newline at end of file
1
2 .shortpixel.shortpixel-notice
3 {
4
5 min-height: 75px;
6 padding: 8px;
7 display: flex;
8 align-items: center;
9 background: #fff;
10 padding: 1px 12px;
11 box-shadow: 0 1px 1px rgba(0,0,0,0.04);
12 border: 1px solid #c3c4c7;
13 margin: 15px 0;
14 border-left-width: 4px;
15 border-left-color: #72aee6;
16 position: relative;
17
18 span
19 {
20 vertical-align: middle;
21 &.icon {
22 margin: 0 25px 0 0;
23 width: 80px;
24 }
25 &.content
26 {
27 padding: 8px 0;
28 word-wrap: break-word;
29 overflow: hidden;
30 //display: flex; // magically fixes verticality issues
31 }
32 }
33
34 img
35 {
36 display:inline-block;
37 margin: 0 25px 0 0;
38 max-height: 50px;
39 }
40 .notice-dismiss
41 {
42 margin-top: 6px;
43 }
44
45 &.notice-success
46 {
47 border-left-color: #00a32a;
48 }
49 &.notice-warning
50 {
51 border-left-color: #dba617;
52 }
53 &.notice-error
54 {
55 border-left-color: #ff0000;
56 }
57 &.notice-info
58 {
59 border-left-color: #72aee6;
60 }
61 }
62
63 /* In-view notice ( not on top, between the options ) - styled after WP notice */
64 .view-notice
65 {
66
67 box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
68 border: 4px solid #fff;
69
70 padding: 1px 12px;
71 p {
72 margin: 1em 0 !important;
73 }
74 &.warning
75 {
76 border-left-color: #ffb900;
77 }
78 }
79
80 .view-notice-row
81 {
82 display: none;
83 }
1 {
2 "name": "shortpixel/replacer",
3 "description": "Content Replacer",
4 "version": 1.1,
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\\Replacer\\" : "src" }
17 }
18 }
1 <?php
2 namespace EnableMediaReplace\Replacer\Libraries\Unserialize;
3
4
5 /**
6 * Worker implementation for identifying and skipping false-positives
7 * not to be substituted - like nested serializations in string literals.
8 *
9 * @internal This class should only be used by \Brumann\Polyfill\Unserialize
10 */
11 final class DisallowedClassesSubstitutor
12 {
13 const PATTERN_STRING = '#s:(\d+):(")#';
14 const PATTERN_OBJECT = '#(^|;)O:\d+:"([^"]*)":(\d+):\{#';
15
16 /**
17 * @var string
18 */
19 private $serialized;
20
21 /**
22 * @var string[]
23 */
24 private $allowedClasses;
25
26 /**
27 * Each array item consists of `[<offset-start>, <offset-end>]` and
28 * marks start and end positions of items to be ignored.
29 *
30 * @var array[]
31 */
32 private $ignoreItems = array();
33
34 /**
35 * @param string $serialized
36 * @param string[] $allowedClasses
37 */
38 public function __construct($serialized, array $allowedClasses)
39 {
40 $this->serialized = $serialized;
41 $this->allowedClasses = $allowedClasses;
42
43 $this->buildIgnoreItems();
44 $this->substituteObjects();
45 }
46
47 /**
48 * @return string
49 */
50 public function getSubstitutedSerialized()
51 {
52 return $this->serialized;
53 }
54
55 /**
56 * Identifies items to be ignored - like nested serializations in string literals.
57 */
58 private function buildIgnoreItems()
59 {
60 $offset = 0;
61 while (preg_match(self::PATTERN_STRING, $this->serialized, $matches, PREG_OFFSET_CAPTURE, $offset)) {
62 $length = (int)$matches[1][0]; // given length in serialized data (e.g. `s:123:"` --> 123)
63 $start = $matches[2][1]; // offset position of quote character
64 $end = $start + $length + 1;
65 $offset = $end + 1;
66
67 // serialized string nested in outer serialized string
68 if ($this->ignore($start, $end)) {
69 continue;
70 }
71
72 $this->ignoreItems[] = array($start, $end);
73 }
74 }
75
76 /**
77 * Substitutes disallowed object class names and respects items to be ignored.
78 */
79 private function substituteObjects()
80 {
81 $offset = 0;
82 while (preg_match(self::PATTERN_OBJECT, $this->serialized, $matches, PREG_OFFSET_CAPTURE, $offset)) {
83 $completeMatch = (string)$matches[0][0];
84 $completeLength = strlen($completeMatch);
85 $start = $matches[0][1];
86 $end = $start + $completeLength;
87 $leftBorder = (string)$matches[1][0];
88 $className = (string)$matches[2][0];
89 $objectSize = (int)$matches[3][0];
90 $offset = $end + 1;
91
92 // class name is actually allowed - skip this item
93 if (in_array($className, $this->allowedClasses, true)) {
94 continue;
95 }
96 // serialized object nested in outer serialized string
97 if ($this->ignore($start, $end)) {
98 continue;
99 }
100
101 $incompleteItem = $this->sanitizeItem($className, $leftBorder, $objectSize);
102 $incompleteItemLength = strlen($incompleteItem);
103 $offset = $start + $incompleteItemLength + 1;
104
105 $this->replace($incompleteItem, $start, $end);
106 $this->shift($end, $incompleteItemLength - $completeLength);
107 }
108 }
109
110 /**
111 * Replaces sanitized object class names in serialized data.
112 *
113 * @param string $replacement Sanitized object data
114 * @param int $start Start offset in serialized data
115 * @param int $end End offset in serialized data
116 */
117 private function replace($replacement, $start, $end)
118 {
119 $this->serialized = substr($this->serialized, 0, $start)
120 . $replacement . substr($this->serialized, $end);
121 }
122
123 /**
124 * Whether given offset positions should be ignored.
125 *
126 * @param int $start
127 * @param int $end
128 * @return bool
129 */
130 private function ignore($start, $end)
131 {
132 foreach ($this->ignoreItems as $ignoreItem) {
133 if ($ignoreItem[0] <= $start && $ignoreItem[1] >= $end) {
134 return true;
135 }
136 }
137
138 return false;
139 }
140
141 /**
142 * Shifts offset positions of ignore items by `$size`.
143 * This is necessary whenever object class names have been
144 * substituted which have a different length than before.
145 *
146 * @param int $offset
147 * @param int $size
148 */
149 private function shift($offset, $size)
150 {
151 foreach ($this->ignoreItems as &$ignoreItem) {
152 // only focus on items starting after given offset
153 if ($ignoreItem[0] < $offset) {
154 continue;
155 }
156 $ignoreItem[0] += $size;
157 $ignoreItem[1] += $size;
158 }
159 }
160
161 /**
162 * Sanitizes object class item.
163 *
164 * @param string $className
165 * @param int $leftBorder
166 * @param int $objectSize
167 * @return string
168 */
169 private function sanitizeItem($className, $leftBorder, $objectSize)
170 {
171 return sprintf(
172 '%sO:22:"__PHP_Incomplete_Class":%d:{s:27:"__PHP_Incomplete_Class_Name";%s',
173 $leftBorder,
174 $objectSize + 1, // size of object + 1 for added string
175 \serialize($className)
176 );
177 }
178 }
1 MIT License
2
3 Copyright (c) 2016-2019 Denis Brumann
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 SOFTWARE.
1 Polyfill unserialize [![Build Status](https://travis-ci.org/dbrumann/polyfill-unserialize.svg?branch=master)](https://travis-ci.org/dbrumann/polyfill-unserialize)
2 ===
3
4 Backports unserialize options introduced in PHP 7.0 to older PHP versions.
5 This was originally designed as a Proof of Concept for Symfony Issue
6 [#21090](https://github.com/symfony/symfony/pull/21090).
7
8 You can use this package in projects that rely on PHP versions older than
9 PHP 7.0. In case you are using PHP 7.0+ the original `unserialize()` will be
10 used instead.
11
12 From the [documentation](https://secure.php.net/manual/en/function.unserialize.php):
13
14 > **Warning**
15 >
16 > Do not pass untrusted user input to unserialize() regardless of the options
17 > value of allowed_classes. Unserialization can result in code being loaded and
18 > executed due to object instantiation and autoloading, and a malicious user
19 > may be able to exploit this. Use a safe, standard data interchange format
20 > such as JSON (via json_decode() and json_encode()) if you need to pass
21 > serialized data to the user.
22
23 Requirements
24 ------------
25
26 - PHP 5.3+
27
28 Installation
29 ------------
30
31 You can install this package via composer:
32
33 ```bash
34 composer require brumann/polyfill-unserialize "^2.0"
35 ```
36
37 Older versions
38 --------------
39
40 You can find the most recent 1.x versions in the branch with the same name:
41
42 * [dbrumann/polyfill-unserialize/tree/1.x](https://github.com/dbrumann/polyfill-unserialize/tree/1.x)
43
44 Upgrading
45 ---------
46
47 Upgrading from 1.x to 2.0 should be seamless and require no changes to code
48 using the library. There are no changes to the public API, i.e. the names for
49 classes, methods and arguments as well as argument order and types remain the
50 same. Version 2.x uses a completely different approach for substituting
51 disallowed classes, which is why we chose to use a new major release to prevent
52 issues from unknown side effects in existing installations.
53
54 Known Issues
55 ------------
56
57 There is a mismatch in behavior when `allowed_classes` in `$options` is not
58 of the correct type (array or boolean). PHP 7.0 will not issue a warning that
59 an invalid type was provided. This library will trigger a warning, similar to
60 the one PHP 7.1+ will raise and then continue, assuming `false` to make sure
61 no classes are deserialized by accident.
62
63 Tests
64 -----
65
66 You can run the test suite using PHPUnit. It is intentionally not bundled as
67 dev dependency to make sure this package has the lowest restrictions on the
68 implementing system as possible.
69
70 Please read the [PHPUnit Manual](https://phpunit.de/manual/current/en/installation.html)
71 for information how to install it on your system.
72
73 Please make sure to pick a compatible version. If you use PHP 5.6 you should
74 use PHPUnit 5.7.27 and for older PHP versions you should use PHPUnit 4.8.36.
75 Older versions of PHPUnit might not support namespaces, meaning they will not
76 work with the tests. Newer versions only support PHP 7.0+, where this library
77 is not needed anymore.
78
79 You can run the test suite as follows:
80
81 ```bash
82 phpunit -c phpunit.xml.dist tests/
83 ```
84
85 Contributing
86 ------------
87
88 This package is considered feature complete. As such I will likely not update
89 it unless there are security issues.
90
91 Should you find any bugs or have questions, feel free to submit an Issue or a
92 Pull Request on GitHub.
93
94 Development setup
95 -----------------
96
97 This library contains a docker setup for development purposes. This allows
98 running the code on an older PHP version without having to install it locally.
99
100 You can use the setup as follows:
101
102 1. Go into the project directory
103
104 1. Build the docker image
105
106 ```
107 docker build -t polyfill-unserialize .
108 ```
109
110 This will download a debian/jessie container with PHP 5.6 installed. Then
111 it will download an appropriate version of phpunit for this PHP version.
112 It will also download composer. It will set the working directory to `/opt/app`.
113 The resulting image is tagged as `polyfill-unserialize`, which is the name
114 we will refer to, when running the container.
115
116 1. You can then run a container based on the image, which will run your tests
117
118 ```
119 docker run -it --rm --name polyfill-unserialize-dev -v "$PWD":/opt/app polyfill-unserialize
120 ```
121
122 This will run a docker container based on our previously built image.
123 The container will automatically be removed after phpunit finishes.
124 We name the image `polyfill-unserialize-dev`. This makes sure only one
125 instance is running and that we can easily identify a running container by
126 its name, e.g. in order to remove it manually.
127 We mount our current directory into the container's working directory.
128 This ensures that tests run on our current project's state.
129
130 You can repeat the final step as often as you like in order to run the tests.
131 The output should look something like this:
132
133 ```bash
134 dbr:polyfill-unserialize/ (improvement/dev_setup*) $ docker run -it --rm --name polyfill-unserialize-dev -v "$PWD":/opt/app polyfill-unserialize
135 Loading composer repositories with package information
136 Installing dependencies (including require-dev) from lock file
137 Nothing to install or update
138 Generating autoload files
139 PHPUnit 5.7.27 by Sebastian Bergmann and contributors.
140
141 ...................... 22 / 22 (100%)
142
143 Time: 167 ms, Memory: 13.25MB
144
145 OK (22 tests, 31 assertions)
146 ```
147
148 When you are done working on the project you can free up disk space by removing
149 the initially built image:
150
151 ```
152 docker image rm polyfill-unserialize
153 ```
1 <?php
2 namespace EnableMediaReplace\Replacer\Libraries\Unserialize;
3
4 // Taken from : https://github.com/dbrumann/polyfill-unserialize/
5 final class Unserialize
6 {
7 /**
8 * @see https://secure.php.net/manual/en/function.unserialize.php
9 *
10 * @param string $serialized Serialized data
11 * @param array $options Associative array containing options
12 *
13 * @return mixed
14 */
15 public static function unserialize($serialized, array $options = array())
16 {
17 if (PHP_VERSION_ID >= 70000) {
18 return \unserialize($serialized, $options);
19 }
20 if (!array_key_exists('allowed_classes', $options) || true === $options['allowed_classes']) {
21 return \unserialize($serialized);
22 }
23 $allowedClasses = $options['allowed_classes'];
24 if (false === $allowedClasses) {
25 $allowedClasses = array();
26 }
27 if (!is_array($allowedClasses)) {
28 $allowedClasses = array();
29 trigger_error(
30 'unserialize(): allowed_classes option should be array or boolean',
31 E_USER_WARNING
32 );
33 }
34
35 $worker = new DisallowedClassesSubstitutor($serialized, $allowedClasses);
36
37 return \unserialize($worker->getSubstitutedSerialized());
38 }
39 }
1 <?php
2 namespace EnableMediaReplace\Replacer\Modules;
3
4 class Elementor
5 {
6 private static $instance;
7
8 protected $queryKey = 'elementor';
9
10 public static function getInstance()
11 {
12 if (is_null(self::$instance))
13 self::$instance = new Elementor();
14
15 return self::$instance;
16 }
17
18 public function __construct()
19 {
20 if ($this->elementor_is_active()) // elementor is active
21 {
22 add_filter('shortpixel/replacer/custom_replace_query', array($this, 'addElementor'), 10, 4); // custom query for elementor \ // problem
23 // @todo Fix this for SPIO
24 //add_action('enable-media-replace-upload-done', array($this, 'removeCache') );
25 }
26 }
27
28 public function addElementor($items, $base_url, $search_urls, $replace_urls)
29 {
30 $base_url = $this->addSlash($base_url);
31 $el_search_urls = $search_urls; //array_map(array($this, 'addslash'), $search_urls);
32 $el_replace_urls = $replace_urls; //array_map(array($this, 'addslash'), $replace_urls);
33 $items[$this->queryKey] = array('base_url' => $base_url, 'search_urls' => $el_search_urls, 'replace_urls' => $el_replace_urls);
34 return $items;
35 }
36
37 public function addSlash($value)
38 {
39 global $wpdb;
40 $value= ltrim($value, '/'); // for some reason the left / isn't picked up by Mysql.
41 $value= str_replace('/', '\/', $value);
42 $value = $wpdb->esc_like(($value)); //(wp_slash) / str_replace('/', '\/', $value);
43
44 return $value;
45 }
46
47 protected function elementor_is_active()
48 {
49 $bool = false;
50
51 if (defined('ELEMENTOR_VERSION'))
52 $bool = true;
53
54 return apply_filters('emr/externals/elementor_is_active', $bool); // manual override
55 }
56
57 public function removeCache()
58 {
59 \Elementor\Plugin::$instance->files_manager->clear_cache();
60 }
61 }
1 <?php
2 namespace EnableMediaReplace\Replacer\Modules;
3 // Note! This class doubles as integration for both Visual Composer *and* WP Bakery. They both need URLENCODE.
4 class WpBakery
5 {
6 private static $instance;
7
8 protected $queryKey = 'wpbakery';
9
10 public static function getInstance()
11 {
12 if (is_null(self::$instance))
13 self::$instance = new WpBakery();
14
15 return self::$instance;
16 }
17
18 public function __construct()
19 {
20 if ($this->bakery_is_active()) // elementor is active
21 {
22 add_filter('shortpixel/replacer/custom_replace_query', array($this, 'addURLEncoded'), 10, 4); // custom query for elementor \ // problem
23 }
24 }
25
26 public function addUrlEncoded($items, $base_url, $search_urls, $replace_urls)
27 {
28 $base_url = $this->addEncode($base_url);
29 $el_search_urls = array_map(array($this, 'addEncode'), $search_urls);
30 $el_replace_urls = array_map(array($this, 'addEncode'), $replace_urls);
31 $items[$this->queryKey] = array('base_url' => $base_url, 'search_urls' => $el_search_urls, 'replace_urls' => $el_replace_urls);
32 return $items;
33 }
34
35 public function addEncode($value)
36 {
37 return urlencode($value);
38 }
39
40 protected function bakery_is_active()
41 {
42 $bool = false;
43
44 // did_action -> wpbakery , VCV_version -> detect Visual Composer
45 if (did_action('vc_plugins_loaded') || defined('VCV_VERSION'))
46 $bool = true;
47
48 return apply_filters('emr/externals/urlencode_is_active', $bool); // manual override
49 }
50 }
1 <?php
2 namespace EnableMediaReplace\Replacer\Modules;
3 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4
5 // Integration to reset indexes of Yoast (used for Og:image) when something is converted.
6 class YoastSeo
7 {
8
9 private $yoastTable;
10 private static $instance;
11
12 public static function getInstance()
13 {
14 if (is_null(self::$instance))
15 self::$instance = new YoastSeo();
16
17 return self::$instance;
18 }
19
20 public function __construct()
21 {
22 if (true === $this->yoast_is_active()) // elementor is active
23 {
24 global $wpdb;
25 $this->yoastTable = $wpdb->prefix . 'yoast_indexable';
26
27 add_action('shortpixel/replacer/replace_urls', array($this, 'removeIndexes'),10,2);
28 }
29 }
30
31 public function removeIndexes($search_urls, $replace_urls)
32 {
33 global $wpdb;
34
35 $sql = 'DELETE FROM ' . $this->yoastTable . ' WHERE ';
36 $prepare = array();
37
38 $base = isset($search_urls['base']) ? $search_urls['base'] : null;
39 $file = isset($search_urls['file']) ? $search_urls['file'] : null;
40
41 if (! is_null($base))
42 {
43 $querySQL = $sql . ' twitter_image like %s or open_graph_image like %s ';
44 $querySQL = $wpdb->prepare($querySQL, '%' . $base . '%', '%' . $base . '%');
45
46 $wpdb->query($querySQL);
47 }
48
49 if (! is_null($file))
50 {
51 $querySQL = $sql . ' twitter_image like %s or open_graph_image like %s ';
52 $querySQL = $wpdb->prepare($querySQL, '%' . $file . '%', '%' . $file . '%');
53
54 $wpdb->query($querySQL);
55 }
56
57 }
58
59 protected function yoast_is_active()
60 {
61 if (defined('WPSEO_VERSION'))
62 {
63 return true;
64 }
65 return false;
66 }
67
68
69
70
71 }
1 <?php
2 namespace EnableMediaReplace\Replacer;
3
4 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5 use EnableMediaReplace\Replacer\Libraries\Unserialize\Unserialize;
6
7 /** Module: Replacer.
8 *
9 * - Able to replace across database
10 * - Only replace thumbnails feature dependent on media library
11 * - Support for page builders / strange data
12 */
13
14 class Replacer
15 {
16
17 protected $source_url;
18 protected $target_url;
19 protected $source_metadata = array();
20 protected $target_metadata = array();
21
22 public function __construct()
23 {
24 //$this->source_url = $source_url;
25 ///$this->target_url = $target_url;
26 $this->loadFormats();
27 }
28
29 // Load classes that handle alternative formats that can occur in the metadata / post data.
30 protected function loadFormats()
31 {
32 Modules\Elementor::getInstance();
33 Modules\WpBakery::getInstance();
34 Modules\YoastSeo::getInstance();
35 }
36
37 public function setSource($url)
38 {
39 $this->source_url = $url;
40 }
41
42 public function getSource()
43 {
44 return $this->source_url;
45 }
46
47 public function setTarget($url)
48 {
49 $this->target_url = $url;
50 }
51
52 public function getTarget()
53 {
54 return $this->target_url;
55 }
56
57 public function setSourceMeta($meta)
58 {
59 $this->source_metadata = $meta;
60 }
61
62 public function setTargetMeta($meta)
63 {
64 $this->target_metadata = $meta;
65 }
66
67 public function replace($args = array())
68 {
69 if (is_null($this->source_url) || is_null($this->target_url))
70 {
71 Log::addWarn('Replacer called without source or target ');
72 return false;
73 }
74 $defaults = array(
75 'thumbnails_only' => false,
76 );
77
78 $errors = array();
79 $args = wp_parse_args($args, $defaults);
80
81 // Search-and-replace filename in post database
82 // @todo Check this with scaled images.
83 $base_url = parse_url($this->source_url, PHP_URL_PATH);// emr_get_match_url( $this->source_url);
84 $base_url = str_replace('.' . pathinfo($base_url, PATHINFO_EXTENSION), '', $base_url);
85
86 /** Fail-safe if base_url is a whole directory, don't go search/replace */
87 if (is_dir($base_url))
88 {
89 Log::addError('Search Replace tried to replace to directory - ' . $base_url);
90 $errors[] = __('Fail Safe :: Source Location seems to be a directory.', 'enable-media-replace');
91 return $errors;
92 }
93
94 if (strlen(trim($base_url)) == 0)
95 {
96 Log::addError('Current Base URL emtpy - ' . $base_url);
97 $errors[] = __('Fail Safe :: Source Location returned empty string. Not replacing content','enable-media-replace');
98 return $errors;
99 }
100
101 // get relurls of both source and target.
102 $urls = $this->getRelativeURLS();
103
104
105 if ($args['thumbnails_only'])
106 {
107 foreach($urls as $side => $data)
108 {
109 if (isset($data['base']))
110 {
111 unset($urls[$side]['base']);
112 }
113 if (isset($data['file']))
114 {
115 unset($urls[$side]['file']);
116 }
117 }
118 }
119
120 $search_urls = $urls['source'];
121 $replace_urls = $urls['target'];
122
123 /* If the replacement is much larger than the source, there can be more thumbnails. This leads to disbalance in the search/replace arrays.
124 Remove those from the equation. If the size doesn't exist in the source, it shouldn't be in use either */
125 foreach($replace_urls as $size => $url)
126 {
127 if (! isset($search_urls[$size]))
128 {
129 Log::addDebug('Dropping size ' . $size . ' - not found in source urls');
130 unset($replace_urls[$size]);
131 }
132 }
133
134 Log::addDebug('Source', $this->source_metadata);
135 Log::addDebug('Target', $this->target_metadata);
136 /* If on the other hand, some sizes are available in source, but not in target, try to replace them with something closeby. */
137 foreach($search_urls as $size => $url)
138 {
139 if (! isset($replace_urls[$size]))
140 {
141 $closest = $this->findNearestSize($size);
142 if ($closest)
143 {
144 $sourceUrl = $search_urls[$size];
145 $baseurl = trailingslashit(str_replace(wp_basename($sourceUrl), '', $sourceUrl));
146 Log::addDebug('Nearest size of source ' . $size . ' for target is ' . $closest);
147 $replace_urls[$size] = $baseurl . $closest;
148 }
149 else
150 {
151 Log::addDebug('Unset size ' . $size . ' - no closest found in source');
152 }
153 }
154 }
155
156 /* If source and target are the same, remove them from replace. This happens when replacing a file with same name, and +/- same dimensions generated.
157
158 After previous loops, for every search there should be a replace size.
159 */
160 foreach($search_urls as $size => $url)
161 {
162 $replace_url = isset($replace_urls[$size]) ? $replace_urls[$size] : false;
163 if ($url == $replace_url) // if source and target as the same, no need for replacing.
164 {
165 unset($search_urls[$size]);
166 unset($replace_urls[$size]);
167 }
168 }
169
170 // If the two sides are disbalanced, the str_replace part will cause everything that has an empty replace counterpart to replace it with empty. Unwanted.
171 if (count($search_urls) !== count($replace_urls))
172 {
173 Log::addError('Unbalanced Replace Arrays, aborting', array($search_urls, $replace_urls, count($search_urls), count($replace_urls) ));
174 $errors[] = __('There was an issue with updating your image URLS: Search and replace have different amount of values. Aborting updating thumbnails', 'enable-media-replace');
175 return $errors;
176 }
177
178 Log::addDebug('Doing meta search and replace -', array($search_urls, $replace_urls) );
179 Log::addDebug('Searching with BaseuRL ' . $base_url);
180
181 do_action('shortpixel/replacer/replace_urls', $search_urls, $replace_urls);
182 $updated = 0;
183
184 $updated += $this->doReplaceQuery($base_url, $search_urls, $replace_urls);
185
186 $replaceRuns = apply_filters('shortpixel/replacer/custom_replace_query', array(), $base_url, $search_urls, $replace_urls);
187 Log::addDebug("REPLACE RUNS", $replaceRuns);
188 foreach($replaceRuns as $component => $run)
189 {
190 Log::addDebug('Running additional replace for : '. $component, $run);
191 $updated += $this->doReplaceQuery($run['base_url'], $run['search_urls'], $run['replace_urls']);
192 }
193
194 Log::addDebug("Updated Records : " . $updated);
195 return $updated;
196 }
197
198 private function doReplaceQuery($base_url, $search_urls, $replace_urls)
199 {
200 global $wpdb;
201 /* Search and replace in WP_POSTS */
202 // Removed $wpdb->remove_placeholder_escape from here, not compatible with WP 4.8
203
204 $posts_sql = $wpdb->prepare(
205 "SELECT ID, post_content FROM $wpdb->posts WHERE post_status in ('publish', 'future', 'draft', 'pending', 'private')
206 AND post_content LIKE %s",
207 '%' . $base_url . '%');
208
209 $rs = $wpdb->get_results( $posts_sql, ARRAY_A );
210 $number_of_updates = 0;
211
212 if ( ! empty( $rs ) ) {
213 foreach ( $rs AS $rows ) {
214 $number_of_updates = $number_of_updates + 1;
215 // replace old URLs with new URLs.
216
217 $post_content = $rows["post_content"];
218 $post_id = $rows['ID'];
219 $replaced_content = $this->replaceContent($post_content, $search_urls, $replace_urls, false, true);
220
221 if ($replaced_content !== $post_content)
222 {
223
224 // $result = wp_update_post($post_ar);
225 $sql = 'UPDATE ' . $wpdb->posts . ' SET post_content = %s WHERE ID = %d';
226 $sql = $wpdb->prepare($sql, $replaced_content, $post_id);
227
228 $result = $wpdb->query($sql);
229
230 if ($result === false)
231 {
232 Notice::addError('Something went wrong while replacing' . $result->get_error_message() );
233 Log::addError('WP-Error during post update', $result);
234 }
235 }
236
237 }
238 }
239
240 $number_of_updates += $this->handleMetaData($base_url, $search_urls, $replace_urls);
241 return $number_of_updates;
242 }
243
244 private function handleMetaData($url, $search_urls, $replace_urls)
245 {
246 global $wpdb;
247
248 $meta_options = apply_filters('shortpixel/replacer/metadata_tables', array('post', 'comment', 'term', 'user'));
249 $number_of_updates = 0;
250
251 foreach($meta_options as $type)
252 {
253 switch($type)
254 {
255 case "post": // special case.
256 $sql = 'SELECT meta_id as id, meta_key, meta_value FROM ' . $wpdb->postmeta . '
257 WHERE post_id in (SELECT ID from '. $wpdb->posts . ' where post_status in ("publish", "future", "draft", "pending", "private") ) AND meta_value like %s';
258 $type = 'post';
259
260 $update_sql = ' UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d';
261 break;
262 default:
263 $table = $wpdb->{$type . 'meta'}; // termmeta, commentmeta etc
264
265 $meta_id = 'meta_id';
266 if ($type == 'user')
267 $meta_id = 'umeta_id';
268
269 $sql = 'SELECT ' . $meta_id . ' as id, meta_value FROM ' . $table . '
270 WHERE meta_value like %s';
271
272 $update_sql = " UPDATE $table set meta_value = %s WHERE $meta_id = %d ";
273 break;
274 }
275
276 $sql = $wpdb->prepare($sql, '%' . $url . '%');
277
278 // 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.
279 // $postmeta_sql = str_replace('[JSON_URL]', $json_url, $postmeta_sql);
280 $rsmeta = $wpdb->get_results($sql, ARRAY_A);
281
282 if (! empty($rsmeta))
283 {
284 foreach ($rsmeta as $row)
285 {
286 $number_of_updates++;
287 $content = $row['meta_value'];
288
289
290 $id = $row['id'];
291
292 $content = $this->replaceContent($content, $search_urls, $replace_urls); //str_replace($search_urls, $replace_urls, $content);
293
294 $prepared_sql = $wpdb->prepare($update_sql, $content, $id);
295
296 Log::addDebug('Update Meta SQl' . $prepared_sql);
297 $result = $wpdb->query($prepared_sql);
298
299 }
300 }
301 } // foreach
302
303 return $number_of_updates;
304 } // function
305
306
307
308 /**
309 * Replaces Content across several levels of possible data
310 * @param $content String The Content to replace
311 * @param $search String Search string
312 * @param $replace String Replacement String
313 * @param $in_deep Boolean. This is use to prevent serialization of sublevels. Only pass back serialized from top.
314 * @param $strict_check Boolean . If true, remove all classes from serialization check and fail. This should be done on post_content, not on metadata.
315 */
316 private function replaceContent($content, $search, $replace, $in_deep = false, $strict_check = false)
317 {
318 //$is_serial = false;
319 if ( true === is_serialized($content))
320 {
321 $serialized_content = $content; // use to return content back if incomplete classes are found, prevent destroying the original information
322
323 if (true === $strict_check)
324 {
325 $args = array('allowed_classes' => false);
326 }
327 else
328 {
329 $args = array('allowed_classes' => true);
330 }
331
332 $content = Unserialize::unserialize($content, $args);
333 // bail directly on incomplete classes. In < PHP 7.2 is_object is false on incomplete objects!
334 if (true === $this->checkIncomplete($content))
335 {
336 return $serialized_content;
337 }
338 }
339
340 $isJson = $this->isJSON($content);
341
342 if ($isJson)
343 {
344 $content = json_decode($content);
345 Log::addDebug('JSon Content', $content);
346 }
347
348 if (is_string($content)) // let's check the normal one first.
349 {
350 $content = apply_filters('shortpixel/replacer/content', $content, $search, $replace);
351
352 $content = str_replace($search, $replace, $content);
353 }
354 elseif (is_wp_error($content)) // seen this.
355 {
356 //return $content; // do nothing.
357 }
358 elseif (is_array($content) ) // array metadata and such.
359 {
360 foreach($content as $index => $value)
361 {
362 $content[$index] = $this->replaceContent($value, $search, $replace, true); //str_replace($value, $search, $replace);
363 if (is_string($index)) // If the key is the URL (sigh)
364 {
365 $index_replaced = $this->replaceContent($index, $search,$replace, true);
366 if ($index_replaced !== $index)
367 $content = $this->change_key($content, array($index => $index_replaced));
368 }
369 }
370 }
371 elseif(is_object($content)) // metadata objects, they exist.
372 {
373 // bail directly on incomplete classes.
374 if (true === $this->checkIncomplete($content))
375 {
376 // if it was serialized, return the original as not to corrupt data.
377 if (isset($serialized_content))
378 {
379 return $serialized_content;
380 }
381 else { // else just return the content.
382 return $content;
383 }
384 }
385 foreach($content as $key => $value)
386 {
387 $content->{$key} = $this->replaceContent($value, $search, $replace, true); //str_replace($value, $search, $replace);
388 }
389 }
390
391 if ($isJson && $in_deep === false) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically.
392 {
393 Log::addDebug('Value was found to be JSON, encoding');
394 // wp-slash -> WP does stripslashes_deep which destroys JSON
395 $content = json_encode($content, JSON_UNESCAPED_SLASHES);
396 Log::addDebug('Content returning', array($content));
397 }
398 elseif($in_deep === false && (is_array($content) || is_object($content)))
399 $content = maybe_serialize($content);
400
401 return $content;
402 }
403
404 private function change_key($arr, $set) {
405 if (is_array($arr) && is_array($set)) {
406 $newArr = array();
407 foreach ($arr as $k => $v) {
408 $key = array_key_exists( $k, $set) ? $set[$k] : $k;
409 $newArr[$key] = is_array($v) ? $this->change_key($v, $set) : $v;
410 }
411 return $newArr;
412 }
413 return $arr;
414 }
415
416 private function getRelativeURLS()
417 {
418 $dataArray = array(
419 'source' => array('url' => $this->source_url, 'metadata' => $this->getFilesFromMetadata($this->source_metadata) ),
420 'target' => array('url' => $this->target_url, 'metadata' => $this->getFilesFromMetadata($this->target_metadata) ),
421 );
422
423 // Log::addDebug('Source Metadata', $this->source_metadata);
424 // Log::addDebug('Target Metadata', $this->target_metadata);
425
426 $result = array();
427
428 foreach($dataArray as $index => $item)
429 {
430 $result[$index] = array();
431 $metadata = $item['metadata'];
432
433 $baseurl = parse_url($item['url'], PHP_URL_PATH);
434 $result[$index]['base'] = $baseurl; // this is the relpath of the mainfile.
435 $baseurl = trailingslashit(str_replace( wp_basename($item['url']), '', $baseurl)); // get the relpath of main file.
436
437 foreach($metadata as $name => $filename)
438 {
439 $result[$index][$name] = $baseurl . wp_basename($filename); // filename can have a path like 19/08 etc.
440 }
441
442 }
443 // Log::addDebug('Relative URLS', $result);
444 return $result;
445 }
446
447
448 private function getFilesFromMetadata($meta)
449 {
450 $fileArray = array();
451 if (isset($meta['file']))
452 $fileArray['file'] = $meta['file'];
453
454 if (isset($meta['sizes']))
455 {
456 foreach($meta['sizes'] as $name => $data)
457 {
458 if (isset($data['file']))
459 {
460 $fileArray[$name] = $data['file'];
461 }
462 }
463 }
464 return $fileArray;
465 }
466
467 /** FindNearestsize
468 * This works on the assumption that when the exact image size name is not available, find the nearest width with the smallest possible difference to impact the site the least.
469 */
470 private function findNearestSize($sizeName)
471 {
472
473 if (! isset($this->source_metadata['sizes'][$sizeName]) || ! isset($this->target_metadata['width'])) // This can happen with non-image files like PDF.
474 {
475 // Check if metadata-less item is a svg file. Just the main file to replace all thumbnails since SVG's don't need thumbnails.
476 if (strpos($this->target_url, '.svg') !== false)
477 {
478 $svg_file = wp_basename($this->target_url);
479 return $svg_file; // this is the relpath of the mainfile.
480 }
481
482 return false;
483 }
484 $old_width = $this->source_metadata['sizes'][$sizeName]['width']; // the width from size not in new image
485 $new_width = $this->target_metadata['width']; // default check - the width of the main image
486
487 $diff = abs($old_width - $new_width);
488 // $closest_file = str_replace($this->relPath, '', $this->newMeta['file']);
489 $closest_file = wp_basename($this->target_metadata['file']); // mainfile as default
490
491 foreach($this->target_metadata['sizes'] as $sizeName => $data)
492 {
493 $thisdiff = abs($old_width - $data['width']);
494
495 if ( $thisdiff < $diff )
496 {
497 $closest_file = $data['file'];
498 if(is_array($closest_file)) { $closest_file = $closest_file[0];} // HelpScout case 709692915
499 if(!empty($closest_file)) {
500 $diff = $thisdiff;
501 $found_metasize = true;
502 }
503 }
504 }
505
506 if(empty($closest_file)) return false;
507
508 return $closest_file;
509 }
510
511 /* Check if given content is JSON format. */
512 private function isJSON($content)
513 {
514 if (is_array($content) || is_object($content) || is_null($content))
515 return false; // can never be.
516
517 $json = json_decode($content);
518 return $json && $json != $content;
519 }
520
521 private function checkIncomplete($var)
522 {
523 return ($var instanceof \__PHP_Incomplete_Class);
524 }
525
526
527 } // class
1 <?php
2 namespace EnableMediaReplace\Controller;
3 use EnableMediaReplace\Notices\NoticeController as Notices;
4 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
6
7 if (! defined('ABSPATH')) {
8 exit; // Exit if accessed directly.
9 }
10
11 class RemoteNoticeController
12 {
13 protected static $instance;
14 private $remote_message_endpoint = 'https://api.shortpixel.com/v2/notices.php';
15
16
17 public function __construct()
18 {
19 $this->doRemoteNotices();
20 }
21
22 public static function getInstance()
23 {
24 if ( is_null(self::$instance))
25 {
26 self::$instance = new RemoteNoticeController();
27 }
28
29 return self::$instance;
30 }
31
32 protected function doRemoteNotices()
33 {
34 $notices = $this->get_remote_notices();
35
36 if (! is_array($notices))
37 return;
38
39 foreach($notices as $remoteNotice)
40 {
41 if (! isset($remoteNotice->id) && ! isset($remoteNotice->message))
42 return;
43
44 if (! isset($remoteNotice->type))
45 $remoteNotice->type = 'notice';
46
47 $message = esc_html($remoteNotice->message);
48 $id = sanitize_text_field($remoteNotice->id);
49
50 $noticeController = Notices::getInstance();
51 $noticeObj = $noticeController->getNoticeByID($id);
52
53 // not added to system yet
54 if ($noticeObj === false)
55 {
56 switch ($remoteNotice->type)
57 {
58 case 'warning':
59 $new_notice = Notices::addWarning($message);
60 break;
61 case 'error':
62 $new_notice = Notices::addError($message);
63 break;
64 case 'notice':
65 default:
66 $new_notice = Notices::addNormal($message);
67 break;
68 }
69
70 Notices::makePersistent($new_notice, $id, MONTH_IN_SECONDS);
71 }
72
73 }
74 }
75
76 private function get_remote_notices()
77 {
78 $transient_name = 'emr_remote_notice';
79 $transient_duration = DAY_IN_SECONDS;
80
81
82 // $keyControl = new apiKeyController();
83 //$keyControl->loadKey();
84
85 $notices = get_transient($transient_name);
86 $url = $this->remote_message_endpoint;
87 $url = add_query_arg(array( // has url
88 'version' => EMR_VERSION,
89 'plugin' => 'enable-media-replace',
90 'target' => 4,
91
92 ), $url);
93
94
95 if ( $notices === false || $notices == 'none' ) {
96 $notices_response = wp_safe_remote_request( $url );
97
98 $content = false;
99 if (! is_wp_error( $notices_response ) )
100 {
101 $notices = json_decode($notices_response['body']);
102
103 if (! is_array($notices))
104 $notices = 'none';
105
106 // Save transient anywhere to prevent over-asking when nothing good is there.
107 set_transient( $transient_name, 'true', $transient_duration );
108 }
109 else
110 {
111 set_transient( $transient_name, false, $transient_duration );
112 }
113 }
114
115 return $notices;
116 }
117
118 } // class
1 <?php
2 namespace EnableMediaReplace\Controller;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
9 use EnableMediaReplace\Replacer\Replacer as Replacer;
10 use EnableMediaReplace\emrCache as emrCache;
11
12 class ReplaceController
13 {
14 protected $post_id;
15 protected $sourceFile;
16 protected $sourceFileUntranslated;
17 protected $targetFile;
18
19 const MODE_REPLACE = 1;
20 const MODE_SEARCHREPLACE = 2;
21
22 const TIME_UPDATEALL = 1; // replace the date
23 const TIME_UPDATEMODIFIED = 2; // keep the date, update only modified
24 const TIME_CUSTOM = 3; // custom time entry
25
26 const ERROR_TARGET_EXISTS = 20;
27 const ERROR_DESTINATION_FAIL = 21;
28 const ERROR_COPY_FAILED = 22;
29 const ERROR_UPDATE_POST = 23;
30 const ERROR_DIRECTORY_SECURITY = 24;
31 const ERROR_DIRECTORY_NOTEXIST = 25;
32
33 protected $replaceType;
34 /** @var string */
35 protected $new_location;
36 protected $timeMode;
37 protected $newDate;
38
39 protected $new_filename;
40
41 protected $tmpUploadPath;
42
43 protected $lastError;
44 protected $lastErrorData; // optional extra data for last error.
45
46 public function __construct($post_id)
47 {
48 $this->post_id = $post_id;
49 $this->setupSource();
50 }
51
52 /* getSourceFile
53 *
54 * @param $untranslated boolean if file is offloaded, this indicates to return remote variant. Used for displaying preview
55 */
56 public function getSourceFile($untranslated = false)
57 {
58
59 if (true === $untranslated && ! is_null($this->sourceFileUntranslated))
60 {
61 return $this->sourceFileUntranslated;
62 }
63 return $this->sourceFile;
64 }
65
66 public function setupParams($params)
67 {
68 $this->replaceType = ($params['replace_type'] === 'replace_and_search') ? self::MODE_SEARCHREPLACE : self::MODE_REPLACE;
69
70 if ($this->replaceType == self::MODE_SEARCHREPLACE && true === $params['new_location'] && ! is_null($params['location_dir']))
71 {
72 $this->new_location = $params['location_dir'];
73 }
74
75 $this->timeMode = $params['timestamp_replace'];
76 $this->newDate = $params['new_date'];
77
78 $this->new_filename = $params['new_filename'];
79 $this->tmpUploadPath = $params['uploadFile'];
80
81 $targetFile = $this->setupTarget();
82 if (is_null($targetFile))
83 {
84 return false;
85 }
86 $this->targetFile = $this->fs()->getFile($targetFile);
87
88 return true;
89 }
90
91 public function returnLastError()
92 {
93 return $this->lastError;
94 }
95
96 public function returnLastErrorData()
97 {
98 if (! is_null($this->lastErrorData))
99 return $this->lastErrorData;
100 else {
101 return array();
102 }
103 }
104
105 public function run()
106 {
107 do_action('wp_handle_replace', array('post_id' => $this->post_id));
108
109 // Set Source / and Source Metadata
110 $Replacer = new Replacer();
111 $source_url = $this->getSourceUrl();
112 $Replacer->setSource($source_url);
113 $Replacer->setSourceMeta(wp_get_attachment_metadata( $this->post_id ));
114
115 $targetFileObj = $this->fs()->getFile($this->targetFile);
116
117 $directoryObj = $targetFileObj->getFileDir();
118
119 $result = $directoryObj->check();
120
121 if ($result === false)
122 {
123 Log::addError('Directory creation for targetFile failed');
124 }
125
126 $permissions = ($this->sourceFile->exists() ) ? $this->sourceFile->getPermissions() : -1;
127
128 $this->removeCurrent(); // tries to remove the current files.
129
130 $fileObj = $this->fs()->getFile($this->tmpUploadPath);
131 $copied = $fileObj->copy($targetFileObj);
132
133 if (false === $copied)
134 {
135 if ($targetFileObj->exists())
136 {
137 Log::addDebug('Copy declared failed, but target available');
138 }
139 else {
140 $this->lastError = self::ERROR_COPY_FAILED;
141 }
142 }
143
144 $deleted = $fileObj->delete();
145 if (false === $deleted)
146 {
147 Log::addWarn('Temp file could not be removed. Permission issues?');
148 }
149
150 $this->targetFile->resetStatus(); // reinit target file because it came into existence.
151
152 if ($permissions > 0)
153 chmod( $this->targetFile->getFullPath(), $permissions ); // restore permissions
154 else {
155 Log::addWarn('Setting permissions failed');
156 }
157
158 // Uspdate the file attached. This is required for wp_get_attachment_url to work.
159 // Using RawFullPath because FullPath does normalize path, which update_attached_file doesn't so in case of windows / strange Apspaths it fails.
160 $updated = update_attached_file($this->post_id, $this->targetFile->getRawFullPath() );
161 if (! $updated)
162 {
163 Log::addError('Update Attached File reports as not updated or same value');
164 }
165
166 // Run the filter, so other plugins can hook if needed.
167 $filtered = apply_filters( 'wp_handle_upload', array(
168 'file' => $this->targetFile->getFullPath(),
169 'url' => $this->getTargetURL(),
170 'type' => $this->targetFile->getMime(),
171 ), 'sideload');
172
173 // check if file changed during filter. Set changed to attached file meta properly.
174 if (isset($filtered['file']) && $filtered['file'] != $this->targetFile->getFullPath() )
175 {
176 update_attached_file($this->post_id, $filtered['file'] );
177 $this->targetFile = $this->fs()->getFile($filtered['file']); // handle as a new file
178 Log::addInfo('WP_Handle_upload filter returned different file', $filtered);
179 }
180
181 $target_url = $this->getTargetURL();
182 $Replacer->setTarget($target_url);
183
184 // Check and update post mimetype, otherwise badly coded plugins cry.
185 $post_mime = get_post_mime_type($this->post_id);
186 $target_mime = $this->targetFile->getMime();
187
188 // update DB post mime type, if somebody decided to mess it up, and the target one is not empty.
189 if ($target_mime !== $post_mime && strlen($target_mime) > 0)
190 {
191 \wp_update_post(array('post_mime_type' => $this->targetFile->getMime(), 'ID' => $this->post_id));
192 }
193
194 do_action('emr/converter/prevent-offload', $this->post_id);
195 $target_metadata = wp_generate_attachment_metadata( $this->post_id, $this->targetFile->getFullPath() );
196 do_action('emr/converter/prevent-offload-off', $this->post_id);
197 wp_update_attachment_metadata( $this->post_id, $target_metadata );
198
199
200 $Replacer->setTargetMeta($target_metadata);
201 //$this->target_metadata = $metadata;
202
203 /** If author is different from replacer, note this */
204 $post_author = get_post_field( 'post_author', $this->post_id );
205 $author_id = get_post_meta($this->post_id, '_emr_replace_author', true);
206
207 if ( intval($post_author) !== get_current_user_id())
208 {
209 update_post_meta($this->post_id, '_emr_replace_author', get_current_user_id());
210 }
211 elseif ($author_id)
212 {
213 delete_post_meta($this->post_id, '_emr_replace_author');
214 }
215
216
217 if ($this->replaceType == self::MODE_SEARCHREPLACE)
218 {
219 // Write new image title.
220 $title = $this->getNewTitle($target_metadata);
221 $excerpt = $this->getNewExcerpt($target_metadata);
222 $update_ar = array('ID' => $this->post_id);
223 $update_ar['post_title'] = $title;
224 $update_ar['post_name'] = sanitize_title($title);
225 if ($excerpt !== false)
226 {
227 $update_ar['post_excerpt'] = $excerpt;
228 }
229 $update_ar['guid'] = $target_url; //wp_get_attachment_url($this->post_id);
230
231 $post_id = \wp_update_post($update_ar, true);
232
233 global $wpdb;
234 // update post doesn't update GUID on updates.
235 $wpdb->update( $wpdb->posts, array( 'guid' => $target_url), array('ID' => $this->post_id) );
236 //enable-media-replace-upload-done
237
238 // @todo This error in general ever happens?
239 if (is_wp_error($post_id))
240 {
241 $this->lastError = self::ERROR_UPDATE_POST;
242 }
243
244 }
245
246 /// Here run the Replacer Module
247 $args = array(
248 'thumbnails_only' => ($this->replaceType == self::MODE_SEARCHREPLACE) ? false : true,
249 );
250
251 $Replacer->replace($args);
252
253 // Here Updatedata and a ffew others.
254 $this->updateDate();
255
256 // Give the caching a kick. Off pending specifics.
257 $cache_args = array(
258 'flush_mode' => 'post',
259 'post_id' => $this->post_id,
260 );
261
262 $cache = new emrCache();
263 $cache->flushCache($cache_args);
264
265 do_action("enable-media-replace-upload-done", $target_url, $source_url, $this->post_id);
266
267 return true;
268 } // run
269
270
271 protected function setupSource()
272 {
273 $source_file = false;
274
275 // The main image as registered in attached_file metadata. This can be regular or -scaled.
276 $source_file_main = trim(get_attached_file($this->post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
277
278 // If available it -needs- to use the main image when replacing since treating a -scaled images as main will create a resursion in the filename when not replacing that one . Ie image-scaled-scaled.jpg or image-scaled-100x100.jpg .
279 if (function_exists('wp_get_original_image_path')) // WP 5.3+
280 {
281 $source_file = wp_get_original_image_path($this->post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ));
282 // For offload et al to change path if wrong. Somehow this happens?
283 $source_file = apply_filters('emr/replace/original_image_path', $source_file, $this->post_id);
284
285 }
286
287 if (false === $source_file) // If not scaled, use the main one.
288 {
289 $source_file = $source_file_main;
290 }
291
292
293 $sourceFileObj = $this->fs()->getFile($source_file);
294 $isVirtual = false;
295 if ($sourceFileObj->is_virtual())
296 {
297 $isVirtual = true;
298
299 /***
300 *** Either here the table should check scaled - non-scaled ** or ** the original_path should be updated.
301 ***
302
303 */
304
305 $this->sourceFileUntranslated = $this->fs()->getFile($source_file);
306 $sourcePath = apply_filters('emr/file/virtual/translate', $sourceFileObj->getFullPath(), $sourceFileObj, $this->post_id);
307
308 if (false !== $sourcePath && $sourceFileObj->getFullPath() !== $sourcePath)
309 {
310 $sourceFileObj = $this->fs()->getFile($sourcePath);
311 $source_file = $sourcePath;
312 }
313
314 }
315
316
317 /* It happens that the SourceFile returns relative / incomplete when something messes up get_upload_dir with an error something.
318 This case shoudl be detected here and create a non-relative path anyhow..
319 */
320 if (
321 false === $isVirtual &&
322 false === file_exists($source_file) &&
323 $source_file && 0 !== strpos( $source_file, '/' )
324 && ! preg_match( '|^.:\\\|', $source_file ) )
325 {
326 $file = get_post_meta( $this->post_id, '_wp_attached_file', true );
327 $uploads = wp_get_upload_dir();
328 $source_file = $uploads['basedir'] . "/$source_file";
329 }
330
331 Log::addDebug('SetupSource SourceFile Path ' . $source_file);
332 $this->sourceFile = $this->fs()->getFile($source_file);
333 }
334
335 /** Returns a full target path to place to new file. Including the file name! **/
336 protected function setupTarget()
337 {
338 $targetPath = null;
339 if ($this->replaceType == self::MODE_REPLACE)
340 {
341 $targetFile = $this->getSourceFile()->getFullPath(); // overwrite source
342 }
343 elseif ($this->replaceType == self::MODE_SEARCHREPLACE)
344 {
345 $path = (string) $this->getSourceFile()->getFileDir();
346 $targetLocation = $this->getNewTargetLocation();
347 if (false === $targetLocation)
348 {
349 return null;
350 }
351
352 if (false === is_null($this->new_location)) // Replace to another path.
353 {
354 $otherTarget = $this->fs()->getFile($targetLocation . $this->new_filename);
355 // Halt if new target exists, but not if it's the same ( overwriting itself )
356
357 if ($otherTarget->exists() && $otherTarget->getFullPath() !== $this->getSourceFile()->getFullPath() )
358 {
359 $this->lastError = self::ERROR_TARGET_EXISTS;
360 return null;
361 }
362
363 $path = $targetLocation; // $this->target_location; // if all went well.
364 }
365 //if ($this->sourceFile->getFileName() == $this->targetName)
366 $targetpath = $path . $this->new_filename;
367
368 // If the source and target path AND filename are identical, user has wrong mode, just overwrite the sourceFile.
369 if ($targetpath == $this->sourceFile->getFullPath())
370 {
371 $unique = $this->sourceFile->getFileName();
372 $this->replaceType == self::MODE_REPLACE;
373 }
374 else
375 {
376 $unique = wp_unique_filename($path, $this->new_filename);
377 }
378 $new_filename = apply_filters( 'emr_unique_filename', $unique, $path, $this->post_id );
379 $targetFile = trailingslashit($path) . $new_filename;
380 }
381 if (is_dir($targetFile)) // this indicates an error with the source.
382 {
383 Log::addWarn('TargetFile is directory ' . $targetFile );
384 $upload_dir = wp_upload_dir();
385 if (isset($upload_dir['path']))
386 {
387 $targetFile = trailingslashit($upload_dir['path']) . wp_unique_filename($targetFile, $this->new_filename);
388 }
389 else {
390
391 $this->lastError = self::ERROR_DESTINATION_FAIL;
392 return null;
393 }
394 }
395 return $targetFile;
396 }
397
398 protected function getNewTitle($meta)
399 {
400 // get basename without extension
401 $title = basename($this->targetFile->getFileName(), '.' . $this->targetFile->getExtension());
402 // $meta = $this->target_metadata;
403
404 if (isset($meta['image_meta']))
405 {
406 if (isset($meta['image_meta']['title']))
407 {
408 if (strlen($meta['image_meta']['title']) > 0)
409 {
410 $title = $meta['image_meta']['title'];
411 }
412 }
413 }
414
415 // Thanks Jonas Lundman (http://wordpress.org/support/topic/add-filter-hook-suggestion-to)
416 $title = apply_filters( 'enable_media_replace_title', $title );
417
418 return $title;
419 }
420
421 protected function getNewExcerpt($meta)
422 {
423 // $meta = $this->target_metadata;
424 $excerpt = false;
425
426 if (isset($meta['image_meta']))
427 {
428 if (isset($meta['image_meta']['caption']))
429 {
430 if (strlen($meta['image_meta']['caption']) > 0)
431 {
432 $excerpt = $meta['image_meta']['caption'];
433 }
434 }
435 }
436
437 return $excerpt;
438 }
439
440 public function getSourceUrl()
441 {
442 if (function_exists('wp_get_original_image_url')) // WP 5.3+
443 {
444 $source_url = wp_get_original_image_url($this->post_id);
445 if ($source_url === false) // not an image, or borked, try the old way
446 $source_url = wp_get_attachment_url($this->post_id);
447
448 $source_url = $source_url;
449 }
450 else
451 $source_url = wp_get_attachment_url($this->post_id);
452
453 return $source_url;
454 }
455
456 /** Handle new dates for the replacement */
457 protected function updateDate()
458 {
459 global $wpdb;
460 $post_date = $this->newDate;
461 $post_date_gmt = get_gmt_from_date($post_date);
462
463 $update_ar = array('ID' => $this->post_id);
464 if ($this->timeMode == static::TIME_UPDATEALL || $this->timeMode == static::TIME_CUSTOM)
465 {
466 $update_ar['post_date'] = $post_date;
467 $update_ar['post_date_gmt'] = $post_date_gmt;
468 }
469 else {
470
471 }
472 $update_ar['post_modified'] = $post_date;
473 $update_ar['post_modified_gmt'] = $post_date_gmt;
474
475 $updated = $wpdb->update( $wpdb->posts, $update_ar , array('ID' => $this->post_id) );
476
477 wp_cache_delete($this->post_id, 'posts');
478
479 }
480
481 /** Tries to remove all of the old image, without touching the metadata in database
482 * This might fail on certain files, but this is not an indication of success ( remove might fail, but overwrite can still work)
483 */
484 protected function removeCurrent()
485 {
486 $meta = \wp_get_attachment_metadata( $this->post_id );
487 $backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
488
489 // 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.
490 $file = $this->sourceFile->getFullPath();
491 $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $file );
492
493 // If Attached file is not the same path as file, this indicates a -scaled images is in play.
494 // Also plugins like Polylang tend to block delete image while there is translation / duplicate item somewhere
495 // 10/06/22 : Added a hard delete if file still exists. Be gone, hard way.
496 $attached_file = get_attached_file($this->post_id);
497 if (file_exists($attached_file))
498 {
499 @unlink($attached_file);
500 }
501
502 do_action( 'emr_after_remove_current', $this->post_id, $meta, $backup_sizes, $this->sourceFile, $this->targetFile );
503 }
504
505 /** Since WP functions also can't be trusted here in certain cases, create the URL by ourselves */
506 protected function getTargetURL()
507 {
508 if (is_null($this->targetFile))
509 {
510 Log::addError('TargetFile NULL ', debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10));
511 return false;
512 }
513 //$uploads['baseurl']
514 $url = wp_get_attachment_url($this->post_id);
515 $url_basename = basename($url);
516
517 // Seems all worked as normal.
518 if (strpos($url, '://') >= 0 && $this->targetFile->getFileName() == $url_basename)
519 return $url;
520
521 // Relative path for some reason
522 if (strpos($url, '://') === false)
523 {
524 $uploads = wp_get_upload_dir();
525 $url = str_replace($uploads['basedir'], $uploads['baseurl'], $this->targetFile->getFullPath());
526 }
527 // This can happen when WordPress is not taking from attached file, but wrong /old GUID. Try to replace it to the new one.
528 elseif ($this->targetFile->getFileName() != $url_basename)
529 {
530 $url = str_replace($url_basename, $this->targetFile->getFileName(), $url);
531 }
532
533 return $url;
534
535 }
536
537 protected function getNewTargetLocation()
538 {
539 $uploadDir = wp_upload_dir();
540 $new_rel_location = $this->new_location;
541 $newPath = trailingslashit($uploadDir['basedir']) . $new_rel_location;
542
543 $realPath = realpath($newPath);
544 $basedir = realpath($uploadDir['basedir']); // both need to go to realpath, otherwise some servers will have issues with it.
545
546 // Detect traversal by making sure the canonical path starts with uploads' basedir.
547 if ( strpos($realPath, $basedir) !== 0)
548 {
549 $this->lastError = self::ERROR_DIRECTORY_SECURITY;
550 $this->lastErrorData = array('path' => $realPath, 'basedir' => $basedir);
551 return false;
552 }
553
554 if (! is_dir($newPath))
555 {
556 $this->lastError = self::ERROR_DIRECTORY_NOTEXIST;
557 return false;
558 }
559 return trailingslashit($newPath);
560 }
561
562
563 private function fs()
564 {
565 return emr()->filesystem();
566 }
567 }
1 <?php
2 namespace EnableMediaReplace;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 use EnableMediaReplace as emr;
9 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
10
11
12 abstract class ViewController
13 {
14
15 abstract function load();
16
17
18 const ERROR_UPLOAD_PERMISSION = 1;
19 const ERROR_IMAGE_PERMISSION = 2;
20 const ERROR_FORM = 3;
21 const ERROR_TIME = 4;
22 const ERROR_UPDATE_FAILED = 5;
23 const ERROR_SECURITY = 6;
24 const ERROR_UPLOAD_FAILED = 7;
25 const ERROR_NONCE = 8;
26 const ERROR_KEY = 9; // Missing key when replacing backgrounds.
27
28 // These synced with ReplaceController
29 const ERROR_TARGET_EXISTS = 20;
30 const ERROR_DESTINATION_FAIL = 21;
31 const ERROR_COPY_FAILED = 22;
32 const ERROR_UPDATE_POST = 23;
33 const ERROR_DIRECTORY_SECURITY = 24;
34 const ERROR_DIRECTORY_NOTEXIST = 25;
35
36 // Remove Background
37 const ERROR_DOWNLOAD_FAILED = 31;
38
39 protected static $viewsLoaded = array();
40
41 protected $view; // object to use in the view.
42 protected $url; // if controller is home to a page, sets the URL here. For redirects and what not.
43
44
45 public function __construct()
46 {
47 $this->view = new \stdClass;
48 }
49
50 protected function loadView($template = null, $unique = true)
51 {
52 if (is_null($template) )
53 {
54 return false;
55 }
56 elseif (strlen(trim($template)) == 0)
57 {
58 return false;
59 }
60
61 $view = $this->view;
62 $controller = $this;
63 $template_path = emr()->plugin_path('views/' . $template . '.php');
64
65 if (file_exists($template_path) === false)
66 {
67 Log::addError("View $template could not be found in " . $template_path,
68 array('class' => get_class($this)));
69 }
70 elseif ($unique === false || ! in_array($template, self::$viewsLoaded))
71 {
72 include($template_path);
73 self::$viewsLoaded[] = $template;
74 }
75 }
76
77 protected function viewError($errorCode, $errorData = array())
78 {
79 $message = $description = false;
80 switch($errorCode)
81 {
82 case self::ERROR_UPLOAD_PERMISSION:
83 $message = __('You don\'t have permission to upload images. Please refer to your administrator', 'enable-media-replace');
84 break;
85 case self::ERROR_IMAGE_PERMISSION:
86 $message = __('You don\'t have permission to edit this image', 'enable-media-replace');
87 break;
88 case self::ERROR_FORM:
89 $message = __('The form submitted is missing various fields', 'enable-media-replace');
90 break;
91 case self::ERROR_TIME:
92 $message = __('The custom time format submitted is invalid', 'enable-media-replace');
93 break;
94 case self::ERROR_UPDATE_FAILED:
95 $message = __('Updating the WordPress attachment failed', 'enable-media-replace');
96 break;
97 case self::ERROR_SECURITY:
98 $message = __('The file upload has been rejected for security reason. WordPress might not allow uploading this extension or filetype', 'enable-media-replace');
99 break;
100 case self::ERROR_UPLOAD_FAILED:
101 $message = __('The upload from your browser seem to have failed', 'enable-media-replace');
102 break;
103 case self::ERROR_TARGET_EXISTS:
104 $message = __('The target file already exists in this directory. Please try another name / directory', 'enable-media-replace');
105 $description = __('This error is shown because you try to move the image to another folder, which already has this file', 'enable-media-replace');
106 break;
107 case self::ERROR_DESTINATION_FAIL:
108 $message = __('Something went wrong while writing the file or directory', 'enable-media-replace');
109 break;
110 case self::ERROR_COPY_FAILED:
111 $message = __('Copying replacement file to destination failed', 'enable-media-replace');
112 break;
113 case self::ERROR_UPDATE_POST:
114 $message = __('Error updating WordPress post in the database', 'enable-media-replace');
115 break;
116 case self::ERROR_DIRECTORY_SECURITY:
117 $message = __('Specificed directory is outside the upload directory. This is not allowed for security reasons', 'enable-media-replace');
118 $path = isset($errorData['path']) ? $errorData['path'] : false;
119 $basedir = isset($errorData['basedir']) ? $errorData['basedir'] : false;
120
121 if ($path !== false && $basedir !== false)
122 {
123 $description = sprintf(__('Path: %s is not within basedir reported as: %s', 'shortpixel-image-optimiser'), $path, $basedir);
124 }
125 break;
126 case self::ERROR_DIRECTORY_NOTEXIST:
127 $message = __('Specificed new directory does not exist. Path must be a relative path from the upload directory and exist', 'enable-media-replace');
128 break;
129
130 case self::ERROR_NONCE:
131 $message = __('Fail to validate form nonce. Please try again', 'enable-media-replace');
132 $description = __('This can happen when the window is open for a long time and/or there has been a timeout. You can go back to previous screen and try again. If this happens each time when replacing, contact us', 'enable-media-replace');
133 break;
134
135 // Remove Background
136 case self::ERROR_DOWNLOAD_FAILED:
137 $message = __('Replacement Image could not be downloaded or does not exist', 'enable-media-replace');
138 break;
139
140 default:
141 $message = __('An unknown error has occured', 'enable-media-replace');
142 break;
143 }
144
145 if( false !== $message)
146 $this->view->errorMessage = $message;
147
148
149 if (false !== $description)
150 {
151 $this->view->errorDescription = $description;
152 }
153
154
155 $this->loadView('error');
156 exit();
157 }
158
159
160 protected function viewSuccess()
161 {
162 wp_enqueue_script('emr_success');
163 $this->loadView('success');
164 exit();
165 }
166
167 }
1 <?php
2
3 namespace EnableMediaReplace\ViewController;
4
5 use EnableMediaReplace\Replacer\Libraries\Unserialize\Unserialize;
6
7
8 if (! defined('ABSPATH')) {
9 exit; // Exit if accessed directly.
10 }
11
12 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
13 use EnableMediaReplace\Controller\ReplaceController as ReplaceController;
14 use EnableMediaReplace\Api as Api;
15
16 class RemoveBackGroundViewController extends \EnableMediaReplace\ViewController
17 {
18 static $instance;
19
20 public function __construct()
21 {
22 parent::__construct();
23 }
24
25 public static function getInstance()
26 {
27 if (is_null(self::$instance))
28 self::$instance = new RemoveBackgroundViewController();
29
30 return self::$instance;
31 }
32
33 public function load()
34 {
35 if (!current_user_can('upload_files')) {
36 $this->viewError(self::ERROR_UPLOAD_PERMISSION);
37 // wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace'));
38 }
39
40
41 $attachment_id = intval($_REQUEST['attachment_id']);
42 $attachment = get_post($attachment_id);
43
44 $uiHelper = \emr()->uiHelper();
45 $uiHelper->setPreviewSizes();
46 $uiHelper->setSourceSizes($attachment_id);
47
48 $replacer = new ReplaceController($attachment_id);
49 $file = $replacer->getSourceFile(true); // for display only
50
51 $defaults = array(
52 'bg_type' => 'transparent',
53 'bg_color' => '#ffffff',
54 'bg_transparency' => 100,
55 );
56 $settings = get_option('enable_media_replace', $defaults);
57 $settings = array_merge($defaults, $settings); // might miss some
58
59 $this->view->attachment = $attachment;
60 $this->view->settings = $settings;
61 $this->view->sourceFile = $file;
62
63 $this->loadView('prepare-remove-background');
64
65 }
66
67 // When the background has been posted - process.
68 public function loadPost()
69 {
70 if ( ! isset( $_POST['emr_nonce'] )
71 || ! wp_verify_nonce( $_POST['emr_nonce'], 'media_remove_background' ))
72 {
73 $this->viewError(self::ERROR_NONCE);
74 }
75
76 $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : null;
77 if (is_null($key) || strlen($key) == 0)
78 {
79 $this->viewError(self::ERROR_KEY);
80 //wp_die(esc_html__('Error while sending form (no key). Please try again.', 'enable-media-replace'));
81 }
82
83 $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id.
84 if (is_null($post_id)) {
85 $this->viewError(self::ERROR_FORM);
86 // wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));
87 }
88
89 $this->setView($post_id);
90 $result = $this->replaceBackground($post_id, $key);
91
92 if (false === $result->success)
93 {
94 $this->view->errorMessage = $result->message;
95 $this->viewError();
96 }
97 elseif (! file_exists($result->image))
98 {
99 $this->viewError(self::ERROR_DOWNLOAD_FAILED);
100 }
101
102 // $result = $replacer->replaceWith($result->image, $source->getFileName() , true);
103 //$params = array();
104 $replaceController = new ReplaceController($post_id);
105 $sourceFile = $replaceController->getSourceFile();
106
107 $datetime = current_time('mysql');
108
109 $params = array(
110 'post_id' => $post_id,
111 'replace_type' => ReplaceController::MODE_REPLACE,
112 'timestamp_replace' => ReplaceController::TIME_UPDATEMODIFIED,
113 'new_date' => $datetime,
114 'is_custom_date' => false,
115 'remove_background' => true,
116 'uploadFile' => $result->image,
117 'new_filename' => $sourceFile->getFileName(),
118 );
119
120
121 $check = $replaceController->setupParams($params);
122 $this->setView($post_id, $params);
123
124 if (false === $check)
125 {
126 $error = $replaceController->returnLastError();
127 $this->viewError($error);
128 }
129
130 $result = $replaceController->run();
131 if (true == $result)
132 {
133 $this->viewSuccess();
134 }
135
136 }
137
138 // Low init might only be w/ post_id ( error handling et al ), most advanced / nicer with params.
139 protected function setView($post_id, $params = array())
140 {
141 $uiHelper = \emr()->uiHelper();
142 $this->view->post_id = $post_id;
143 $this->view->postUrl = $uiHelper->getSuccesRedirect($post_id);
144 $this->view->emrUrl = $uiHelper->getFailedRedirect($post_id);
145
146 }
147
148
149 protected function replaceBackground($post_id, $key)
150 {
151 $api = new Api();
152 $result = $api->handleDownload($key);
153
154 return $result;
155 }
156
157
158
159 } // class
1 <?php
2 namespace EnableMediaReplace\ViewController;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 use EnableMediaReplace as emr;
9 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
10 use EnableMediaReplace\Controller\ReplaceController as ReplaceController;
11
12 class ReplaceViewController extends \EnableMediaReplace\ViewController
13 {
14 static $instance;
15
16 public function __construct()
17 {
18 parent::__construct();
19 }
20
21 public static function getInstance()
22 {
23 if (is_null(self::$instance))
24 self::$instance = new ReplaceViewController();
25
26 return self::$instance;
27 }
28
29 public function load()
30 {
31
32 $attachment_id = intval($_GET['attachment_id']);
33 $attachment = get_post($attachment_id);
34
35 if (! \emr()->checkImagePermission($attachment))
36 {
37 $this->viewError(self::ERROR_IMAGE_PERMISSION);
38 wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') );
39 }
40
41 $replacer = new ReplaceController($attachment_id);
42
43 $file = $replacer->getSourceFile(true);
44 /* $filepath = $file->getFullPath();
45 $filename = $file->getFileName();
46 $filetype = $file->getExtension(); */
47 $source_mime = get_post_mime_type($attachment_id);
48
49 $uiHelper = \emr()->uiHelper();
50 $uiHelper->setPreviewSizes();
51 $uiHelper->setSourceSizes($attachment_id);
52
53 $defaults = array(
54 'replace_type' => 'replace',
55 'timestamp_replace' => ReplaceController::TIME_UPDATEMODIFIED,
56 'custom_date' => date("Y-m-d H:i:s"),
57 'new_location' => false,
58 'new_location_dir' => false,
59 );
60 $settings = get_option('enable_media_replace', $defaults);
61
62 $this->view->attachment = $attachment;
63 $this->view->sourceFile = $file;
64 $this->view->sourceMime = $source_mime;
65 $this->view->settings = array_merge($defaults, $settings); // might miss some
66
67 // Indicates if file can be moved to other location. Can't be done when offloaded.
68 $this->view->is_movable = apply_filters('emr/replace/file_is_movable', true, $attachment_id);
69
70 $uploadDir = wp_upload_dir();
71 $basedir = trailingslashit($uploadDir['basedir']);
72
73 $this->view->custom_basedir = $basedir;
74
75
76 $this->loadView('screen');
77
78 }
79
80 }
1 <?php
2 namespace EnableMediaReplace\ViewController;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 use EnableMediaReplace as emr;
9 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
10 use EnableMediaReplace\Controller\UploadController as UploadController;
11 use EnableMediaReplace\Controller\ReplaceController as ReplaceController;
12
13
14 class UploadViewController extends \EnableMediaReplace\ViewController
15 {
16 static $instance;
17
18
19 public function __construct()
20 {
21 parent::__construct();
22 }
23
24 public static function getInstance()
25 {
26 if (is_null(self::$instance))
27 self::$instance = new UploadViewController();
28
29 return self::$instance;
30 }
31
32 public function load()
33 {
34
35 // No form submit?
36 if (count($_POST) == 0)
37 {
38 $post_id = isset($_REQUEST['attachment_id']) ? intval($_REQUEST['attachment_id']) : null;
39 $this->setView($post_id);
40
41 if (isset($_GET['emr_success']))
42 {
43 $this->viewSuccess();
44 }
45
46 }
47
48 if ( ! isset( $_POST['emr_nonce'] )
49 || ! wp_verify_nonce( $_POST['emr_nonce'], 'media_replace_upload' ))
50 {
51 $this->viewError(self::ERROR_NONCE);
52 }
53
54 if (!current_user_can('upload_files')) {
55 $this->viewError(self::ERROR_UPLOAD_PERMISSION);
56 // wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace'));
57 }
58
59 $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id.
60 if (is_null($post_id)) {
61 $this->viewError(self::ERROR_FORM);
62 // wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));
63 }
64 $attachment = get_post($post_id);
65
66 if (! emr()->checkImagePermission($attachment)) {
67 $this->viewError(self::ERROR_IMAGE_PERMISSION);
68 // wp_die(esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace'));
69 }
70
71 $params = $this->getPost();
72
73 // UploadController here / replacerController here with save Settings as well? s
74 $this->updateSettings($params);
75 $this->setView($post_id, $params); // set variables needed for view.
76
77 $replaceController = new ReplaceController($post_id);
78 $check = $replaceController->setupParams($params);
79
80 if (false === $check)
81 {
82 $error = $replaceController->returnLastError();
83 $data = $replaceController->returnLastErrorData();
84 $this->viewError($error, $data);
85 }
86
87 $result = $replaceController->run();
88
89 if (true == $result)
90 {
91 $this->viewSuccess();
92 }
93 }
94
95
96 protected function getPost()
97 {
98 $ID = intval($_POST["ID"]); // legacy
99 $replace_type = isset($_POST["replace_type"]) ? sanitize_text_field($_POST["replace_type"]) : false;
100 $timestamp_replace = isset($_POST['timestamp_replace']) ? intval($_POST['timestamp_replace']) : ReplaceController::TIME_UPDATEMODIFIED;
101
102 $remove_background = ( isset( $_POST['remove_after_progress'] ) ) ? true : false;
103
104 $do_new_location = isset($_POST['new_location']) ? true : false;
105 $do_new_location = apply_filters('emr/replace/file_is_movable', $do_new_location, $ID);
106 $new_location_dir = isset($_POST['location_dir']) ? sanitize_text_field($_POST['location_dir']) : null;
107
108 $is_custom_date = false;
109
110 switch ($timestamp_replace) {
111 case ReplaceController::TIME_UPDATEALL:
112 case ReplaceController::TIME_UPDATEMODIFIED:
113 $datetime = current_time('mysql');
114 break;
115 case ReplaceController::TIME_CUSTOM:
116 $custom_date = $_POST['custom_date_formatted'];
117 $custom_hour = str_pad($_POST['custom_hour'], 2, 0, STR_PAD_LEFT);
118 $custom_minute = str_pad($_POST['custom_minute'], 2, 0, STR_PAD_LEFT);
119
120 // create a mysql time representation from what we have.
121 Log::addDebug('Custom Date - ' . $custom_date . ' ' . $custom_hour . ':' . $custom_minute);
122 $custom_date = \DateTime::createFromFormat('Y-m-d G:i', $custom_date . ' ' . $custom_hour . ':' . $custom_minute);
123 if ($custom_date === false) {
124 $this->viewError(self::ERROR_TIME);
125 }
126 $datetime = $custom_date->format("Y-m-d H:i:s");
127 $is_custom_date = true;
128 break;
129 }
130
131 list($uploadFile, $new_filename) = $this->getUpload();
132
133 return array(
134 'post_id' => $ID,
135 'replace_type' => $replace_type,
136 'timestamp_replace' => $timestamp_replace,
137 'new_date' => $datetime,
138 'new_location' => $do_new_location,
139 'location_dir' => $new_location_dir,
140 'is_custom_date' => $is_custom_date,
141 'remove_background' => $remove_background,
142 'uploadFile' => $uploadFile,
143 'new_filename' => $new_filename,
144 );
145
146 }
147
148 // Low init might only be w/ post_id ( error handling et al ), most advanced / nicer with params.
149 protected function setView($post_id, $params = array())
150 {
151 $uiHelper = \emr()->uiHelper();
152 $this->view->post_id = $post_id;
153 $this->view->postUrl = $uiHelper->getSuccesRedirect($post_id);
154 $this->view->emrUrl = $uiHelper->getFailedRedirect($post_id);
155
156 if (isset($params['remove_background']) && true === $params['remove_background'])
157 {
158 $this->view->postUrl = $uiHelper->getBackgroundRemoveRedirect($post_id);
159 }
160 }
161
162
163 protected function updateSettings($params)
164 {
165 $settings = get_option('enable_media_replace', array()); // save settings and show last loaded.
166 $settings['replace_type'] = $params['replace_type'];
167 $settings['timestamp_replace'] = $params['timestamp_replace'];
168 $settings['new_location'] = $params['new_location'];
169 $settings['new_location_dir'] = $params['location_dir'];
170
171 if (true === $params['is_custom_date'])
172 {
173 $settings['custom_date'] = $params['new_date'];
174 }
175 update_option('enable_media_replace', $settings, false);
176
177 }
178
179 protected function getUpload()
180 {
181 if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) {
182 Log::addDebug('Uploaded Files', $_FILES['userfile']);
183
184 // New method for validating that the uploaded file is allowed, using WP:s internal wp_check_filetype_and_ext() function.
185 $filedata = wp_check_filetype_and_ext($_FILES["userfile"]["tmp_name"], $_FILES["userfile"]["name"]);
186
187 Log::addDebug('Data after check', $filedata);
188 if (isset($_FILES['userfile']['error']) && $_FILES['userfile']['error'] > 0) {
189 //$e = new RunTimeException('File Uploaded Failed');
190 //Notices::addError($e->getMessage());
191 // wp_safe_redirect($redirect_error);
192 $this->viewError(self::ERROR_UPDATE_FAILED);
193 // exit();
194 }
195
196 if ($filedata["ext"] == false && ! current_user_can('unfiltered_upload')) {
197 $this->viewError(self::ERROR_SECURITY);
198 }
199
200 // Here we have the uploaded file
201 $new_filename = $_FILES["userfile"]["name"];
202 $new_filetype = $filedata["type"] ? $filedata["type"] : $_FILES['userfile']['type'];
203
204 return array($_FILES["userfile"]["tmp_name"], $new_filename);
205 // Execute hook actions - thanks rubious for the suggestion!
206 }
207 $this->viewError(self::ERROR_UPLOAD_FAILED);
208 }
209
210
211
212
213 } // class
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();
1 <?php
2 /**
3 * This page contains api class.
4 */
5 namespace EnableMediaReplace;
6
7 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
8 use EnableMediaReplace\Controller\ReplaceController as ReplaceController;
9
10
11 use Exception;
12 use stdClass;
13 /**
14 * This class contains api methods
15 */
16 class Api {
17
18 /**
19 * Request Counter
20 *
21 * @var int $counter
22 */
23 private $counter = 0;
24
25 /**
26 * ShortPixel api url
27 *
28 * @var string $url
29 */
30 private $url = 'http://api.shortpixel.com/v2/free-reducer.php';
31
32 /**
33 * ShortPixel api request headers
34 *
35 * @var array $headers
36 */
37 private $headers = array(
38 'Content-Type: application/json',
39 'Accept: application/json',
40 );
41
42 private $refresh = true; // only first request should be fresh
43
44
45
46 public function __construct()
47 {
48
49 }
50 /**
51 * Create ShortPixel api request
52 *
53 * @param array $data
54 * @return stdClass $result
55 */
56 public function request( array $posted_data ) {
57 $bg_remove = '1';
58 $compression_level = 0; // intval($posted_data['compression_level']); // off for now.
59
60 $attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : null;
61 $attachment = get_post($attachment_id);
62
63
64 if (is_null($attachment_id))
65 {
66 $result = $this->getResponseObject();
67 $result->success = false;
68 $result->message = __('No attachment ID given', 'enable-media-replace');
69 return $result;
70 }
71
72 if (! emr()->checkImagePermission($attachment)) {
73 $result = $this->getResponseObject();
74 $result->success = false;
75 $result->message = __('No permission for user', 'enable-media-replace');
76 return $result;
77 }
78
79 $replaceController = new ReplaceController($attachment_id);
80 $url = $replaceController->getSourceUrl();
81
82 $settings = get_option('enable_media_replace', array()); // save settings and show last loaded.
83 $settings['bg_type'] = isset($_POST['background']['type']) ? sanitize_text_field($_POST['background']['type']) : false;
84 $settings['bg_color'] = isset($_POST['background']['color']) ? sanitize_text_field($_POST['background']['color']) : '#ffffff'; // default to white.
85 $settings['bg_transparency'] = isset($_POST['background']['transparency']) ? sanitize_text_field($_POST['background']['transparency']) : false;
86
87 update_option('enable_media_replace', $settings, false);
88
89
90 if ( 'solid' === $posted_data['background']['type'] ) {
91 $bg_remove = $posted_data['background']['color'];
92
93 $transparency = isset($posted_data['background']['transparency']) ? intval($posted_data['background']['transparency']) : -1;
94 // if transparancy without acceptable boundaries, add it to color ( as rgba I presume )
95 if ($transparency >= 0 && $transparency < 100)
96 {
97 if ($transparency == 100)
98 $transparency = 'FF';
99
100 // Strpad for lower than 10 should add 09, 08 etc.
101 $bg_remove .= str_pad($transparency, 2, '0', STR_PAD_LEFT);
102 }
103 }
104
105
106
107 $data = array(
108 'plugin_version' => EMR_VERSION,
109 'bg_remove' => $bg_remove,
110 'urllist' => array( urlencode( esc_url($url) ) ),
111 'lossy' => $compression_level,
112 'refresh' => $this->refresh,
113 );
114
115 $request = array(
116 'method' => 'POST',
117 'timeout' => 60,
118 'headers' => $this->headers,
119 'body' => json_encode( $data ),
120
121 );
122
123 $settingsData = '';
124 //unset($settingsData['url']);
125
126 foreach($data as $key => $val)
127 {
128 if ($key == 'urllist' || $key == 'refresh')
129 {
130 continue;
131 }
132 $settingsData .= " $key:$val ";
133 }
134
135
136 //we need to wait a bit until we try to check if the image is ready
137 if ($this->counter > 0)
138 sleep( $this->counter + 3 );
139
140 $this->counter++;
141
142 $result = $this->getResponseObject();
143
144 if ( $this->counter < 10 ) {
145 try {
146
147 Log::addDebug('Sending request', $request);
148 $response = wp_remote_post( $this->url, $request );
149
150 $this->refresh = false;
151
152 if ( is_wp_error( $response ) ) {
153 $result->message = $response->get_error_message();
154 } else {
155
156 $json = json_decode( $response['body'] );
157
158 Log::addDebug('Response Json', $json);
159 if ( is_array( $json ) && '2' === $json[0]->Status->Code ) {
160 $result->success = true;
161
162 if ( '1' === $compression_level || '2' === $compression_level ) {
163 $result->image = $json[0]->LossyURL;
164 } else {
165 $result->image = $json[0]->LosslessURL;
166 }
167
168 $key = $this->handleSuccess($result);
169 $result->key = $key;
170 $result->url = $url;
171 $result->image = add_query_arg('ts', time(), $result->image);
172
173 $result->settings = $settingsData;
174
175 // $this->handleSuccess($result);
176 } elseif ( is_array( $json ) && '1' === $json[0]->Status->Code ) {
177 return $this->request( $posted_data );
178 } else {
179 if (is_array($json))
180 {
181 $result->message = $json[0]->Status->Message;
182 }
183 elseif (is_object($json) && property_exists($json, 'Status'))
184 {
185 $result->message = $json->Status->Message;
186 }
187 }
188 }
189 } catch ( Exception $e ) {
190 $result->message = $e->getMessage();
191 }
192 } else {
193 $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' );
194 }
195
196 return $result;
197 }
198
199 public function handleSuccess($result)
200 {
201 // $fs = emr()->filesystem();
202 // $result = $fs->downloadFile($result->image, wp_tempnam($result->image));
203 $nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : wp_create_nonce();
204 $key = wp_hash($nonce . $result->image, 'logged_in');
205
206 set_transient('emr_' . $key, $result->image, 30 * MINUTE_IN_SECONDS);
207 return $key;
208 }
209
210 public function handleDownload($key)
211 {
212 $url = get_transient('emr_' . $key);
213 $result = $this->getResponseObject();
214
215 if ($url === false)
216 {
217 $result->message = __('This file seems not available anymore. Please try again', 'enable-media-replace');
218 return $result;
219 }
220
221 $fs = emr()->filesystem();
222 $target = wp_tempnam($url);
223
224 $bool = $fs->downloadFile($url, $target);
225
226 if ($bool === false)
227 {
228 $result->message = __('Download failed', 'enable-media-replace');
229 }
230 else {
231 $result->success = true;
232 $result->image = $target;
233 }
234 return $result;
235 }
236
237 protected function getResponseObject()
238 {
239 $result = new stdClass;
240 $result->success = false;
241 $result->image = null;
242 $result->message = null;
243
244 return $result;
245 }
246
247 }
1 <?php
2 namespace EnableMediaReplace;
3
4 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
6 class emrCache
7 {
8 protected $has_supercache = false; // supercache seems to replace quite fine, without our help. @todo Test if this is needed
9 protected $has_w3tc = false;
10 protected $has_wpengine = false;
11 protected $has_fastestcache = false;
12 protected $has_siteground = false;
13 protected $has_litespeed = false;
14
15 public function __construct()
16 {
17
18 }
19
20 /** Checks which cache plugins are active on the moment a flush is needed */
21 public function checkCaches()
22 {
23 if ( function_exists( 'w3tc_pgcache_flush' ) )
24 $this->has_w3tc = true;
25
26 if ( function_exists('wp_cache_clean_cache') )
27 $this->has_supercache = true;
28
29 if ( class_exists( 'WpeCommon' ) )
30 $this->has_wpengine = true;
31
32 global $wp_fastest_cache;
33 if ( method_exists( 'WpFastestCache', 'deleteCache' ) && !empty( $wp_fastest_cache ) )
34 $this->has_fastestcache = true;
35
36 // SG SuperCacher
37 if (function_exists('sg_cachepress_purge_cache')) {
38 $this->has_siteground = true;
39 }
40
41 if (defined( 'LSCWP_DIR' ))
42 {
43 $this->has_litespeed = true;
44 }
45
46 // @todo WpRocket?
47 // @todo BlueHost Caching?
48 }
49
50 /* Tries to flush cache there were we have issues
51 *
52 * @param Array $args Argument Array to provide data.
53 */
54 public function flushCache($args)
55 {
56 $defaults = array(
57 'flush_mode' => 'post',
58 'post_id' => 0,
59 );
60
61 $args = wp_parse_args($args, $defaults);
62 $post_id = $args['post_id']; // can be zero!
63
64 // important - first check the available cache plugins
65 $this->checkCaches();
66
67 // general WP
68 if ($args['flush_mode'] === 'post' && $post_id > 0)
69 clean_post_cache($post_id);
70 else
71 wp_cache_flush();
72
73 /* Verified working without.
74 if ($this->has_supercache)
75 $this->removeSuperCache();
76 */
77 if ($this->has_w3tc)
78 $this->removeW3tcCache();
79
80 if ($this->has_wpengine)
81 $this->removeWpeCache();
82
83 if ($this->has_siteground)
84 $this->removeSiteGround();
85
86 if ($this->has_fastestcache)
87 $this->removeFastestCache();
88
89 if ($this->has_litespeed)
90 $this->litespeedReset($post_id);
91
92 do_action('emr/cache/flush', $post_id);
93 }
94
95 protected function removeSuperCache()
96 {
97 global $file_prefix, $supercachedir;
98 if ( empty( $supercachedir ) && function_exists( 'get_supercache_dir' ) ) {
99 $supercachedir = get_supercache_dir();
100 }
101 wp_cache_clean_cache( $file_prefix );
102 }
103
104 protected function removeW3tcCache()
105 {
106 w3tc_pgcache_flush();
107 }
108
109 protected function removeWpeCache()
110 {
111 if ( method_exists( 'WpeCommon', 'purge_memcached' ) ) {
112 \WpeCommon::purge_memcached();
113 }
114 if ( method_exists( 'WpeCommon', 'clear_maxcdn_cache' ) ) {
115 \WpeCommon::clear_maxcdn_cache();
116 }
117 if ( method_exists( 'WpeCommon', 'purge_varnish_cache' ) ) {
118 \WpeCommon::purge_varnish_cache();
119 }
120 }
121
122 protected function removeFastestCache()
123 {
124 global $wp_fastest_cache;
125 $wp_fastest_cache->deleteCache();
126 }
127
128 protected function removeSiteGround()
129 {
130 sg_cachepress_purge_cache();
131 }
132
133 protected function litespeedReset($post_id)
134 {
135 do_action('litespeed_media_reset', $post_id);
136 }
137
138 }
1 <?php
2
3 // Compatibility functions for old version of WordPress / PHP / Other
4
5
6 /*
7 * Introduced in WP 4.9.7 - https://developer.wordpress.org/reference/functions/wp_delete_attachment_files/
8 * Compat for previous versions.
9 */
10 if (! function_exists('wp_delete_attachment_files'))
11 {
12 function wp_delete_attachment_files($post_id, $meta, $backup_sizes, $file )
13 {
14 global $wpdb;
15 $uploadpath = wp_get_upload_dir();
16 $deleted = true;
17
18 if ( ! empty( $meta['thumb'] ) ) {
19 // Don't delete the thumb if another attachment uses it.
20 if ( ! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id ) ) ) {
21 $thumbfile = str_replace( wp_basename( $file ), $meta['thumb'], $file );
22 if ( ! empty( $thumbfile ) ) {
23 $thumbfile = path_join( $uploadpath['basedir'], $thumbfile );
24 $thumbdir = path_join( $uploadpath['basedir'], dirname( $file ) );
25
26 if ( ! wp_delete_file_from_directory( $thumbfile, $thumbdir ) ) {
27 $deleted = false;
28 }
29 }
30 }
31 }
32
33 // Remove intermediate and backup images if there are any.
34 if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
35 $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) );
36 foreach ( $meta['sizes'] as $size => $sizeinfo ) {
37 $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file );
38 if ( ! empty( $intermediate_file ) ) {
39 $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file );
40
41 if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) {
42 $deleted = false;
43 }
44 }
45 }
46 }
47
48 if ( is_array( $backup_sizes ) ) {
49 $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) );
50 foreach ( $backup_sizes as $size ) {
51 $del_file = path_join( dirname( $meta['file'] ), $size['file'] );
52 if ( ! empty( $del_file ) ) {
53 $del_file = path_join( $uploadpath['basedir'], $del_file );
54
55 if ( ! wp_delete_file_from_directory( $del_file, $del_dir ) ) {
56 $deleted = false;
57 }
58 }
59 }
60 }
61
62 if ( ! wp_delete_file_from_directory( $file, $uploadpath['basedir'] ) ) {
63 $deleted = false;
64 }
65
66 return $deleted;
67
68 }
69 } // end function
70
71
72 /*
73 * Introduced in WP 4.9.7 - https://developer.wordpress.org/reference/functions/wp_delete_attachment_files/
74 * Compat for previous versions.
75 */
76 if (! function_exists('wp_delete_file_from_directory'))
77 {
78 function wp_delete_file_from_directory( $file, $directory ) {
79 if ( wp_is_stream( $file ) ) {
80 $real_file = wp_normalize_path( $file );
81 $real_directory = wp_normalize_path( $directory );
82 } else {
83 $real_file = realpath( wp_normalize_path( $file ) );
84 $real_directory = realpath( wp_normalize_path( $directory ) );
85 }
86
87 if ( false === $real_file || false === $real_directory || strpos( $real_file, trailingslashit( $real_directory ) ) !== 0 ) {
88 return false;
89 }
90
91 wp_delete_file( $file );
92
93 return true;
94 }
95
96 } // end function
97
98
99 /*
100 * Introduced in WP 4.5.0 - needed for compat function of wp_delete_attachment_files
101 */
102 if (! function_exists('wp_get_upload_dir'))
103 {
104 function wp_get_upload_dir() {
105 return wp_upload_dir( null, false );
106 }
107 }
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\Controller\RemoteNoticeController as RemoteNoticeController;
8 use EnableMediaReplace\Ajax;
9
10 // Does what a plugin does.
11 class EnableMediaReplacePlugin
12 {
13
14 protected $plugin_path;
15 private static $instance;
16
17 private $user_cap = false;
18 private $general_cap = false;
19
20 private $features = array();
21
22 public function __construct()
23 {
24 add_action('plugins_loaded', array($this, 'runtime')); //lowInit, before theme setup!
25 add_action('admin_init', array($this, 'adminInit')); // adminInit, after functions.php
26 }
27
28 public function runtime()
29 {
30 $this->nopriv_plugin_actions();
31
32 if (EMR_CAPABILITY !== false) {
33 if (is_array(EMR_CAPABILITY)) {
34 $this->general_cap = EMR_CAPABILITY[0];
35 $this->user_cap = EMR_CAPABILITY[1];
36
37 if (! current_user_can($this->general_cap) && ! current_user_can($this->user_cap)) {
38 return;
39 }
40 } else {
41 $this->general_cap = EMR_CAPABILITY;
42 if (! current_user_can($this->general_cap)) {
43 return;
44 }
45 }
46 } elseif (! current_user_can('upload_files')) {
47 return;
48 }
49
50 new Externals();
51
52 $this->plugin_actions(); // init
53 }
54
55 public function adminInit()
56 {
57 $this->features['replace'] = true; // does nothing just for completeness
58 $this->features['background'] = apply_filters('emr/feature/background', true);
59
60 load_plugin_textdomain('enable-media-replace', false, basename(dirname(EMR_ROOT_FILE)) . '/languages');
61
62 // Load Submodules
63 new Ajax();
64 }
65
66 public function filesystem()
67 {
68 return new FileSystem();
69 }
70
71 public function uiHelper()
72 {
73 return Uihelper::getInstance();
74 }
75
76 public function useFeature($name)
77 {
78 // If for some obscure reason, it's called earlier or out of admin, still load the features.
79 if (count($this->features) === 0)
80 {
81 $this->adminInit();
82 }
83
84 switch($name)
85 {
86 case 'background':
87 $bool = $this->features['background'];
88 break;
89 default:
90 $bool = false;
91 break;
92 }
93 return $bool;
94 }
95
96 public static function get()
97 {
98 if (is_null(self::$instance)) {
99 self::$instance = new EnableMediaReplacePlugin();
100 }
101
102 $log = Log::getInstance();
103 if (Log::debugIsActive()) {
104 $uploaddir = wp_upload_dir(null, false, false);
105 if (isset($uploaddir['basedir'])) {
106 $log->setLogPath( trailingslashit($uploaddir['basedir']) . "emr_log");
107 }
108 }
109 return self::$instance;
110 }
111
112 // Actions for EMR that always need to hook
113 protected function nopriv_plugin_actions()
114 {
115 // shortcode
116 add_shortcode('file_modified', array($this, 'get_modified_date'));
117 }
118
119
120 public function plugin_actions()
121 {
122 $this->plugin_path = plugin_dir_path(EMR_ROOT_FILE);
123 //$this->plugin_url = plugin_dir_url(EMR_ROOT_FILE);
124
125 // loads the dismiss hook.
126 $notices = Notices::getInstance();
127
128 // init plugin
129 add_action('admin_menu', array($this,'menu'));
130 add_action('submenu_file', array($this, 'hide_sub_menu'));
131
132 add_action( 'current_screen', array($this, 'setScreen') ); // annoying workaround for notices in edit-attachment screen
133 add_action('admin_enqueue_scripts', array($this,'admin_scripts'));
134
135
136 // content filters
137 add_filter('media_row_actions', array($this,'add_media_action'), 10, 2);
138 add_action('attachment_submitbox_misc_actions', array($this,'admin_date_replaced_media_on_edit_media_screen'), 91);
139 //add_filter('upload_mimes', array($this,'add_mime_types'), 1, 1);
140
141 // notices
142
143 // editors
144 add_action('add_meta_boxes_attachment', array($this, 'add_meta_boxes'), 10, 2);
145 add_filter('attachment_fields_to_edit', array($this, 'attachment_editor'), 10, 2);
146
147 /** Just after an image is replaced, try to browser decache the images */
148 if (isset($_GET['emr_replaced']) && intval($_GET['emr_replaced'] == 1)) {
149 add_filter('wp_get_attachment_image_src', array($this, 'attempt_uncache_image'), 10, 4);
150
151 // adds a metabox to list thumbnails. This is a cache reset hidden as feature.
152 //add_action( 'add_meta_boxes', function () { );
153 add_filter('postbox_classes_attachment_emr-showthumbs-box', function ($classes) {
154 $classes[] = 'closed';
155 return $classes;
156 });
157 }
158 }
159
160 /**
161 * Register this file in WordPress so we can call it with a ?page= GET var.
162 * To suppress it in the menu we give it an empty menu title.
163 */
164 public function menu()
165 {
166 $title = esc_html__("Replace media", "enable-media-replace");
167 $title = (isset($_REQUEST['action']) && ($_REQUEST['action'] === 'emr_prepare_remove')) ? esc_html__("Remove background", "enable-media-replace") : $title;
168 add_submenu_page('upload.php',$title, $title, 'upload_files', 'enable-media-replace/enable-media-replace.php', array($this, 'route'));
169
170 }
171
172 public function hide_sub_menu($submenu_file)
173 {
174 global $plugin_page;
175 // Select another submenu item to highlight (optional).
176 if ( $plugin_page && $plugin_page == 'enable-media-replace/enable-media-replace.php' ) {
177 $submenu_file = 'upload.php';
178 }
179
180 // Hide the submenu.
181
182 remove_submenu_page( 'upload.php', 'enable-media-replace/enable-media-replace.php' );
183
184 return $submenu_file;
185 }
186
187
188 public function setScreen()
189 {
190 $screen = get_current_screen();
191
192 $notice_pages = array('attachment', 'media_page_enable-media-replace/enable-media-replace', 'upload' );
193 if ( in_array($screen->id, $notice_pages) && true === emr()->useFeature('background'))
194 {
195
196 RemoteNoticeController::getInstance(); // check for remote stuff
197 $notices = Notices::getInstance();
198 $notices->loadIcons(array(
199 'normal' => '<img class="emr-notice-icon" src="' . plugins_url('img/notices/slider.png', EMR_ROOT_FILE) . '">',
200 'success' => '<img class="emr-notice-icon" src="' . plugins_url('img/notices/robo-cool.png', EMR_ROOT_FILE) . '">',
201 'warning' => '<img class="emr-notice-icon" src="' . plugins_url('img/notices/robo-scared.png', EMR_ROOT_FILE) . '">',
202 'error' => '<img class="emr-notice-icon" src="' . plugins_url('img/notices/robo-scared.png', EMR_ROOT_FILE) . '">',
203 ));
204
205 add_action('admin_notices', array($notices, 'admin_notices')); // previous page / init time
206 }
207 }
208
209 /** Load EMR views based on request */
210 public function route()
211 {
212 global $plugin_page;
213 switch ($plugin_page) {
214 case 'enable-media-replace/enable-media-replace.php':
215 $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : '';
216 wp_enqueue_style('emr_style');
217 wp_enqueue_script('jquery-ui-datepicker');
218 wp_enqueue_style('jquery-ui-datepicker');
219 wp_enqueue_script('emr_admin');
220
221 $this->uiHelper()->featureNotice();
222
223 if ($action == 'media_replace') {
224 if (array_key_exists("attachment_id", $_GET) && intval($_GET["attachment_id"]) > 0) {
225 wp_enqueue_script('emr_upsell');
226
227 $controller = \EnableMediaReplace\ViewController\ReplaceViewController::getInstance();
228 $controller->load();
229 // require_once($this->plugin_path . "views/popup.php"); // warning variables like $action be overwritten here.
230 }
231 }
232 elseif ($action == 'media_replace_upload') {
233
234 $controller = \EnableMediaReplace\ViewController\UploadViewController::getInstance();
235 $controller->load();
236 // require_once($this->plugin_path . 'views/upload.php');
237 }
238 elseif ('emr_prepare_remove' === $action && $this->useFeature('background')) {
239 // $attachment_id = intval($_GET['attachment_id']);
240 // $attachment = get_post($attachment_id);
241 //We're adding a timestamp to the image URL for cache busting
242
243 wp_enqueue_script('emr_remove_bg');
244
245 wp_enqueue_style('emr_style');
246 wp_enqueue_style('emr-remove-background');
247 wp_enqueue_script('emr_upsell');
248
249 $controller = \EnableMediaReplace\ViewController\RemoveBackgroundViewController::getInstance();
250 $controller->load();
251
252 // require_once($this->plugin_path . "views/prepare-remove-background.php");
253
254 } elseif ('do_background_replace' === $action &&
255 $this->useFeature('background')
256 ) {
257 $controller = \EnableMediaReplace\ViewController\RemoveBackgroundViewController::getInstance();
258 $controller->loadPost();
259
260 // require_once($this->plugin_path . 'views/do-replace-background.php');
261 }
262 else {
263
264 exit('Something went wrong loading page, please try again');
265 }
266 break;
267 } // Route
268 }
269
270 public function getPluginURL($path = '')
271 {
272 return plugins_url($path, EMR_ROOT_FILE);
273 }
274
275 public function plugin_path($path = '')
276 {
277 $plugin_path = trailingslashit(plugin_dir_path(EMR_ROOT_FILE));
278 if ( strlen( $path ) > 0 ) {
279 $plugin_path .= $path;
280 }
281 return $plugin_path;
282 }
283
284 /** register styles and scripts
285 *
286 * Nothing should ever by -enqueued- here, just registered.
287 */
288 public function admin_scripts()
289 {
290 if (is_rtl()) {
291 wp_register_style('emr_style', plugins_url('css/admin.rtl.css', EMR_ROOT_FILE));
292 } else {
293 wp_register_style('emr_style', plugins_url('css/admin.css', EMR_ROOT_FILE));
294 }
295
296 wp_register_style('emr_edit-attachment', plugins_url('css/edit_attachment.css', EMR_ROOT_FILE));
297
298 wp_register_style('emr-remove-background', plugins_url('css/remove_background.css', EMR_ROOT_FILE));
299
300 $mimes = array_values(get_allowed_mime_types());
301
302 wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true);
303 $emr_options = array(
304 'dateFormat' => $this->convertdate(get_option('date_format')),
305 'maxfilesize' => wp_max_upload_size(),
306 'allowed_mime' => $mimes,
307 );
308
309 wp_register_script('emr_upsell', plugins_url('js/upsell.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true);
310
311 wp_localize_script('emr_upsell', 'emr_upsell', array(
312 'ajax' => admin_url('admin-ajax.php'),
313 'installing' => __('Installing ...', 'enable-media-replace'),
314
315 ));
316
317 $ts = time();
318 $ajax_url = admin_url('admin-ajax.php');
319
320
321 wp_register_script('emr_remove_bg', plugins_url('js/remove_bg.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true);
322 wp_localize_script('emr_remove_bg', 'emrObject', array(
323 'ajax_url' => $ajax_url,
324 'nonce' => wp_create_nonce('emr_remove_background')
325 ));
326
327
328 if (Log::debugIsActive()) {
329 $emr_options['is_debug'] = true;
330 }
331
332 wp_localize_script('emr_admin', 'emr_options', $emr_options);
333
334 wp_register_script('emr_success', plugins_url('js/emr_success.js', EMR_ROOT_FILE), array(), EMR_VERSION, true);
335
336 wp_localize_script('emr_success', 'emr_success_options', array(
337 'timeout' => apply_filters('emr/success/timeout', 5),
338 ));
339 }
340
341 /** Utility function for the Jquery UI Datepicker */
342 public function convertdate($sFormat)
343 {
344 switch ($sFormat) {
345 //Predefined WP date formats
346 case 'F j, Y':
347 return( 'MM dd, yy' );
348 break;
349 case 'Y/m/d':
350 return( 'yy/mm/dd' );
351 break;
352 case 'm/d/Y':
353 return( 'mm/dd/yy' );
354 break;
355 case 'd/m/Y':
356 default:
357 return( 'dd/mm/yy' );
358 break;
359 }
360 }
361
362 public function checkImagePermission($post)
363 {
364 if (! is_object($post))
365 {
366 return false;
367 }
368 $post_id = $post->ID;
369 $post_type = $post->post_type;
370 $author_id = $post->post_author;
371
372 if ($post_type !== 'attachment')
373 return false;
374
375 if (is_null($post_id) || intval($post_id) >! 0)
376 {
377 return false;
378 }
379
380 if ($this->general_cap === false && $this->user_cap === false) {
381 if (current_user_can('edit_post', $post_id) === true) {
382 return true;
383 }
384 } elseif (current_user_can($this->general_cap)) {
385 return true;
386 } elseif (current_user_can($this->user_cap) && $author_id == get_current_user_id()) {
387 return true;
388 }
389
390 return false;
391 }
392
393 /** Get the URL to the media replace page
394 * @param $attach_id The attachment ID to replace
395 * @return Admin URL to the page.
396 */
397 protected function getMediaReplaceURL($attach_id)
398 {
399 $url = admin_url("upload.php");
400 $url = add_query_arg(array(
401 'page' => 'enable-media-replace/enable-media-replace.php',
402 'action' => 'media_replace',
403 'attachment_id' => $attach_id,
404 ), $url);
405
406 return $url;
407 }
408
409 protected function getRemoveBgURL($attach_id)
410 {
411 $url = admin_url("upload.php");
412 $url = add_query_arg(array(
413 'page' => 'enable-media-replace/enable-media-replace.php',
414 'action' => 'emr_prepare_remove',
415 'attachment_id' => $attach_id,
416 ), $url);
417
418 return $url;
419 }
420
421 public function add_meta_boxes($post)
422 {
423 // Because some plugins don't like to play by the rules.
424 if (is_null($post) || ! is_object($post) ) {
425 return false;
426 }
427
428 if (! $this->checkImagePermission($post)) {
429 return;
430 }
431
432 add_meta_box('emr-replace-box', __('Replace Media', 'enable-media-replace'), array($this, 'replace_meta_box'), 'attachment', 'side', 'low');
433
434 if (isset($_GET['emr_replaced']) && intval($_GET['emr_replaced'] == 1)) {
435 add_meta_box('emr-showthumbs-box', __('Replaced Thumbnails Preview', 'enable-media-replace'), array($this, 'show_thumbs_box'), 'attachment', 'side', 'low');
436 }
437 }
438
439 public function replace_meta_box($post)
440 {
441
442 //Replace media button
443 $replace_url = $this->getMediaReplaceURL($post->ID);
444
445 $replace_action = "media_replace";
446 $replace_editurl = wp_nonce_url($replace_url, $replace_action);
447
448 $replace_link = "href=\"$replace_editurl\"";
449
450 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>";
451 //Remove background button
452 $removeBg_url = $this->getRemoveBgURL($post->ID);
453
454 $removeBg_action = "emr_prepare_remove";
455 $removeBg_editurl = wp_nonce_url($removeBg_url, $removeBg_action);
456
457 $removeBg_link = "href=\"$removeBg_editurl\"";
458
459 if ($this->uiHelper()->isBackgroundRemovable($post))
460 {
461 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>";
462 }
463 }
464
465 public function show_thumbs_box($post)
466 {
467 if (! $this->checkImagePermission($post)) {
468 return;
469 }
470
471 wp_enqueue_style('emr_edit-attachment');
472
473 $meta = wp_get_attachment_metadata($post->ID);
474
475 if (! isset($meta['sizes'])) {
476 echo __('Thumbnails were not generated', 'enable-media-replace');
477 return false;
478 }
479
480 if (function_exists('wp_get_original_image_url')) { // indicating WP 5.3+
481 $source_url = wp_get_original_image_url($post->ID);
482 // oldway will give -scaled in case of scaling.
483 $source_url_oldway = wp_get_attachment_url($post->ID);
484
485 if ($source_url !== $source_url_oldway) {
486 echo "<div class='original previewwrapper'><img src='" . $source_url_oldway . "'><span class='label'>" . __('Original') . "</span></div>";
487 }
488 }
489
490
491 foreach ($meta['sizes'] as $size => $data) {
492 $display_size = ucfirst(str_replace("_", " ", $size));
493 $img = wp_get_attachment_image_src($post->ID, $size);
494 echo "<div class='$size previewwrapper'><img src='" . $img[0] . "'><span class='label'>$display_size</span></div>";
495 }
496 }
497
498 public function attachment_editor($form_fields, $post)
499 {
500 $screen = null;
501
502 if (! $this->checkImagePermission($post)) {
503 return $form_fields;
504 }
505
506 if (function_exists('get_current_screen')) {
507 $screen = get_current_screen();
508 if (! is_null($screen) && $screen->id == 'attachment') { // hide on edit attachment screen.
509 return $form_fields;
510 }
511 }
512
513 $url = $this->getMediaReplaceURL($post->ID);
514 $action = "media_replace";
515 $editurl = wp_nonce_url($url, $action);
516
517 $link = "href=\"$editurl\"";
518 $form_fields["enable-media-replace"] = array(
519 "label" => esc_html__("Replace media", "enable-media-replace"),
520 "input" => "html",
521 "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")
522 );
523
524 if ($this->uiHelper()->isBackgroundRemovable($post))
525 {
526 $link = $this->getRemoveBgURL($post->ID);
527 $link = "href='" . wp_nonce_url($link, 'emr_prepare_remove') . "'";
528 $form_fields["emr-remove-background"] = array(
529 "label" => esc_html__("Remove background", "enable-media-replace"),
530 "input" => "html",
531 "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")
532 );
533 }
534 return $form_fields;
535 }
536
537 /**
538 * @param array $mime_types
539 * @return array
540 */
541 /* Off, no clue why this is here.
542 public function add_mime_types($mime_types)
543 {
544 $mime_types['dat'] = 'text/plain'; // Adding .dat extension
545 return $mime_types;
546 }
547 */
548 /**
549 * Function called by filter 'media_row_actions'
550 * Enables linking to EMR straight from the media library
551 */
552 public function add_media_action($actions, $post)
553 {
554
555 if (! $this->checkImagePermission($post)) {
556 return $actions;
557 }
558
559 $media_replace_editurl = $this->getMediaReplaceURL($post->ID);
560 $media_replace_action = "media_replace";
561 // $media_replace_editurl = wp_nonce_url($url, $media_replace_action);
562 $url = $this->getRemoveBgURL($post->ID);
563 $background_remove_action = "emr_prepare_remove";
564 $background_remove_editurl = wp_nonce_url($url, $background_remove_action);
565
566 /* See above, not needed.
567 if (FORCE_SSL_ADMIN) {
568 $editurl = str_replace("http:", "https:", $editurl);
569 } */
570 $media_replace_link = "href=\"$media_replace_editurl\"";
571 $background_remove_link = "href=\"$background_remove_editurl\"";
572
573 $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>';
574
575 if ($this->uiHelper()->isBackgroundRemovable($post))
576 {
577 $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>';
578
579 }
580 return array_merge($actions, $newaction);
581 }
582
583
584
585 /** Outputs the replaced date of the media on the edit_attachment screen
586 *
587 * @param $post Obj Post Object
588 */
589 function admin_date_replaced_media_on_edit_media_screen($post)
590 {
591
592 // Fallback for before version 4.9, doens't pass post.
593 if (! is_object($post)) {
594 global $post;
595 }
596
597 if (! is_object($post)) { // try to global, if it doesn't work - return.
598 return false;
599 }
600
601 $post_id = $post->ID;
602 if ($post->post_modified !== $post->post_date) {
603 $modified = date_i18n(__('M j, Y @ H:i'), strtotime($post->post_modified));
604 ?>
605 <div class="misc-pub-section curtime">
606 <span id="timestamp"><?php echo esc_html__('Revised', 'enable-media-replace'); ?>: <b><?php echo $modified; ?></b></span>
607 </div>
608
609 <?php
610 }
611 $author_id = get_post_meta($post_id, '_emr_replace_author', true);
612
613 if ($author_id) {
614 $display_name = get_the_author_meta('display_name', $author_id);
615 ?>
616 <div class="misc-pub-section replace_author">
617 <span><?php echo esc_html__('Replaced By', 'enable-media-replace'); ?>: <b><?php echo $display_name; ?></b></span>
618 </div>
619 <?php
620 }
621 }
622
623 /** When an image is just replaced, it can stuck in the browser cache making a look like it was not replaced. Try
624 * undo that effect by adding a timestamp to the query string */
625 public function attempt_uncache_image($image, $attachment_id, $size, $icon)
626 {
627 if ($image === false) {
628 return $image;
629 }
630
631 // array with image src on 0
632 $image[0] = add_query_arg('time', time(), $image[0]);
633 return $image;
634 }
635
636 /**
637 * Shorttag function to show the media file modification date/time.
638 * @param array shorttag attributes
639 * @return string content / replacement shorttag
640 * @todo Note this returns the wrong date, ie. server date not corrected for timezone. Function could be removed altogether, not sure about purpose.
641 */
642 public function get_modified_date($atts)
643 {
644 $id=0;
645 $format= '';
646
647 extract(shortcode_atts(array(
648 'id' => '',
649 'format' => get_option('date_format') . " " . get_option('time_format'),
650 ), $atts));
651
652 if ($id == '') {
653 return false;
654 }
655
656 // Get path to file
657 $current_file = get_attached_file($id);
658
659 if (! file_exists($current_file)) {
660 return false;
661 }
662
663 // Get file modification time
664 $filetime = filemtime($current_file);
665
666 if (false !== $filetime) {
667 // do date conversion
668 return date($format, $filetime);
669 }
670
671 return false;
672 }
673 } // class
1 <?php
2 namespace EnableMediaReplace\Externals;
3
4 class Elementor
5 {
6 private static $instance;
7
8 protected $queryKey = 'elementor';
9
10 public static function getInstance()
11 {
12 if (is_null(self::$instance))
13 self::$instance = new Elementor();
14
15 return self::$instance;
16 }
17
18 public function __construct()
19 {
20 if ($this->elementor_is_active()) // elementor is active
21 {
22 add_filter('emr/replacer/custom_replace_query', array($this, 'addElementor'), 10, 4); // custom query for elementor \ // problem
23 add_action('enable-media-replace-upload-done', array($this, 'removeCache') );
24 }
25 }
26
27 public function addElementor($items, $base_url, $search_urls, $replace_urls)
28 {
29 $base_url = $this->addSlash($base_url);
30 $el_search_urls = $search_urls; //array_map(array($this, 'addslash'), $search_urls);
31 $el_replace_urls = $replace_urls; //array_map(array($this, 'addslash'), $replace_urls);
32 $items[$this->queryKey] = array('base_url' => $base_url, 'search_urls' => $el_search_urls, 'replace_urls' => $el_replace_urls);
33 return $items;
34 }
35
36 public function addSlash($value)
37 {
38 global $wpdb;
39 $value= ltrim($value, '/'); // for some reason the left / isn't picked up by Mysql.
40 $value= str_replace('/', '\/', $value);
41 $value = $wpdb->esc_like(($value)); //(wp_slash) / str_replace('/', '\/', $value);
42
43 return $value;
44 }
45
46 protected function elementor_is_active()
47 {
48 $bool = false;
49
50 if (defined('ELEMENTOR_VERSION'))
51 $bool = true;
52
53 return apply_filters('emr/externals/elementor_is_active', $bool); // manual override
54 }
55
56 public function removeCache()
57 {
58 \Elementor\Plugin::$instance->files_manager->clear_cache();
59 }
60 }
1 <?php
2 namespace EnableMediaReplace\Externals;
3
4
5 class SiteOrigin
6 {
7 protected static $instance;
8
9 public function __construct()
10 {
11 if (defined('SITEORIGIN_PANELS_VERSION'))
12 {
13 add_filter('emr/replacer/option_fields', array($this, 'addOption'));
14 }
15 }
16
17 public static function getInstance()
18 {
19 if (is_null(self::$instance))
20 {
21 self::$instance = new SiteOrigin();
22 }
23
24 return self::$instance;
25 }
26
27 public function addOption($options)
28 {
29 $options[] = 'widget_siteorigin-panels-builder';
30 return $options;
31 }
32 } // class
1 <?php
2
3
4 /**
5 * Skin class.
6 *
7 * @since 1.0.0
8 *
9 * @package Envira_Gallery
10 * @author Envira Team
11 */
12 class EMR_Envira_Gallery_Skin extends WP_Upgrader_Skin {
13
14 /**
15 * Primary class constructor.
16 *
17 * @since 1.0.0
18 *
19 * @param array $args Empty array of args (we will use defaults).
20 */
21 public function __construct( $args = array() ) {
22
23 parent::__construct();
24
25 }
26
27 /**
28 * Set the upgrader object and store it as a property in the parent class.
29 *
30 * @since 1.0.0
31 *
32 * @param object $upgrader The upgrader object (passed by reference).
33 */
34 public function set_upgrader( &$upgrader ) {
35
36 if ( is_object( $upgrader ) ) {
37 $this->upgrader =& $upgrader;
38 }
39
40 }
41
42 /**
43 * Set the upgrader result and store it as a property in the parent class.
44 *
45 * @since 1.0.0
46 *
47 * @param object $result The result of the install process.
48 */
49 public function set_result( $result ) {
50
51 $this->result = $result;
52
53 }
54
55 /**
56 * Empty out the header of its HTML content and only check to see if it has
57 * been performed or not.
58 *
59 * @since 1.0.0
60 */
61 public function header() {}
62
63 /**
64 * Empty out the footer of its HTML contents.
65 *
66 * @since 1.0.0
67 */
68 public function footer() {}
69
70 /**
71 * Instead of outputting HTML for errors, json_encode the errors and send them
72 * back to the Ajax script for processing.
73 *
74 * @since 1.0.0
75 *
76 * @param array $errors Array of errors with the install process.
77 */
78 public function error( $errors ) {
79
80 if ( ! empty( $errors ) ) {
81 echo wp_json_encode( array( 'error' => __( 'There was an error installing the addon. Please try again.', 'envira-gallery' ) ) );
82 /* log this for API issues */
83
84 error_log( print_r( $errors, true ) );
85
86 die;
87 }
88
89 }
90
91 /**
92 * Empty out the feedback method to prevent outputting HTML strings as the install
93 * is progressing.
94 *
95 * @since 1.0.0
96 *
97 * @param string $string The feedback string.
98 * @param array ...$args The args.
99 */
100 public function feedback( $string, ...$args ) {}
101
102 }
1 <?php
2
3 add_action( 'wp_ajax_emr_plugin_install', 'emr_plugin_install' );
4
5 function emr_plugin_install() {
6
7 // Run a security check first.
8 check_admin_referer( 'emr-plugin-install', 'nonce' );
9
10 $plugin = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : null;
11
12 if ( ! current_user_can('install_plugins'))
13 {
14 // Send back a response.
15 wp_send_json(array('result'=> false));
16 die;
17 }
18
19 switch($plugin)
20 {
21 case "envira":
22 $download_url = 'https://downloads.wordpress.org/plugin/envira-gallery-lite.zip';
23 break;
24 case 'spio':
25 $download_url = 'https://downloads.wordpress.org/plugin/shortpixel-image-optimiser.zip';
26 break;
27 case 'spai':
28 $download_url = 'https://downloads.wordpress.org/plugin/shortpixel-adaptive-images.zip';
29 break;
30 }
31
32 // Install the addon.
33 if ( ! is_null($download_url ) ) {
34
35 //$download_url = esc_url_raw( wp_unslash( $_POST['plugin'] ) );
36 global $hook_suffix;
37
38 // Set the current screen to avoid undefined notices.
39 set_current_screen();
40
41 // Prepare variables.
42 $method = '';
43 $url = add_query_arg(
44 array(
45 // 'page' => 'envira-gallery-settings',
46 ),
47 admin_url( 'admin.php' )
48 );
49 $url = esc_url( $url );
50
51 // Start output bufferring to catch the filesystem form if credentials are needed.
52 ob_start();
53 $creds = request_filesystem_credentials( $url, $method, false, false, null );
54 if ( false === $creds ) {
55 $form = ob_get_clean();
56 echo wp_json_encode( array( 'form' => $form ) );
57 die;
58 }
59
60 // If we are not authenticated, make it happen now.
61 if ( ! WP_Filesystem( $creds ) ) {
62 ob_start();
63 request_filesystem_credentials( $url, $method, true, false, null );
64 $form = ob_get_clean();
65 echo wp_json_encode( array( 'form' => $form ) );
66 die;
67 }
68
69 // We do not need any extra credentials if we have gotten this far, so let's install the plugin.
70 require_once (ABSPATH . 'wp-admin/includes/class-wp-upgrader.php');
71 require_once (plugin_dir_path( EMR_ROOT_FILE ) . 'classes/external/upgrader_skin.php');
72
73 // Create the plugin upgrader with our custom skin.
74 $skin = new EMR_Envira_Gallery_Skin();
75 $installer = new Plugin_Upgrader( $skin );
76 $installer->install( $download_url );
77
78 // Flush the cache and return the newly installed plugin basename.
79 wp_cache_flush();
80
81 if ( $installer->plugin_info() ) {
82 $plugin_basename = $installer->plugin_info();
83
84 ob_clean();
85
86
87 wp_send_json_success( array( 'plugin' => $plugin_basename ) );
88
89 die();
90 }
91 }
92
93 // Send back a response.
94 wp_send_json(array('result'=> false));
95 die;
96
97 }
98
99 add_action( 'wp_ajax_emr_plugin_activate', 'emr_activate' );
100
101 /**
102 * Activates an Envira addon.
103 *
104 * @since 1.0.0
105 */
106 function emr_activate() {
107
108 // Run a security check first.
109 check_admin_referer( 'emr-plugin-activate', 'nonce' );
110
111 $plugin = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : null;
112
113 if ( ! current_user_can('activate_plugins'))
114 {
115 // Send back a response.
116 wp_send_json(array('result'=> false));
117 die;
118 }
119
120
121 switch($plugin)
122 {
123 case "envira":
124 $plugin = 'envira-gallery-lite/envira-gallery-lite.php';
125 break;
126 case 'spio':
127 $plugin = 'shortpixel-image-optimiser/wp-shortpixel.php';
128 break;
129 case 'spai':
130 $plugin = 'shortpixel-adaptive-images/short-pixel-ai.php';
131 break;
132 }
133
134 // Activate the addon.
135 if ( ! is_null($plugin) ) {
136 $activate = activate_plugin( $plugin );
137 if ( is_wp_error( $activate ) ) {
138 echo json_encode( array( 'error' => $activate->get_error_message() ) );
139 die;
140 }
141 }
142
143 echo json_encode( true );
144 die;
145
146 }
1 <?php
2 namespace EnableMediaReplace\Externals;
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
11
12 class WPOffload
13 {
14 private static $instance;
15
16 private $as3cf;
17 private $sources; // cache for url > source_id lookup, to prevent duplicate queries.
18
19 private static $offloadPrevented = array();
20
21 private $post_id; // source_id. The plugin has this, so why so tricky checks.
22
23
24 public function __construct()
25 {
26 add_action('as3cf_init', array($this, 'init'));
27
28 add_action('emr/converter/prevent-offload', array($this, 'preventOffload'), 10);
29 add_action('emr/converter/prevent-offload-off', array($this, 'preventOffloadOff'), 10);
30 add_filter('as3cf_pre_update_attachment_metadata', array($this, 'preventUpdateMetaData'), 10,4);
31
32
33 }
34
35 public static function getInstance()
36 {
37 if (is_null(self::$instance))
38 {
39 self::$instance = new WPOffload();
40 }
41
42 return self::$instance;
43 }
44
45 public function init($as3cf)
46 {
47 if (! class_exists('\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item'))
48 {
49 Notices::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'enable-media-replace'), true);
50 return false;
51 }
52
53 $this->as3cf = $as3cf;
54
55 if (method_exists($as3cf, 'get_item_handler'))
56 {
57 }
58 else {
59 Notices::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'enable-media-replace'), true);
60 return false;
61 }
62
63 // @todo This all is begging for the creating of an enviroment model / controller.
64 if( !function_exists('is_plugin_active') ) {
65
66 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
67
68 }
69 $spio_active = \is_plugin_active('shortpixel-image-optimiser/wp-shortpixel.php');
70
71 // Let spio handle this.
72 if (false === $spio_active)
73 {
74 add_filter('shortpixel/image/urltopath', array($this, 'checkIfOffloaded'), 10,2);
75 }
76
77 add_action('emr_after_remove_current', array($this, 'removeRemote'), 10, 5);
78 add_filter('emr/file/virtual/translate', array($this, 'getLocalPath'), 10, 3);
79 add_filter('emr/replace/file_is_movable', array($this, 'isFileMovable'), 10, 2);
80
81 add_filter('emr/replace/original_image_path', array($this, 'checkScaledUrl'), 10,2);
82
83 add_action('enable-media-replace-upload-done', array($this, 'updateOriginalPath'), 10, 3);
84 }
85
86 /*
87 * @param $post_id int The post ID
88 * @param $meta array Old Metadata before remove
89 * @param $backup_sizes array WP Backup sizes
90 * @param $sourceFile Object Source File
91 * @param $targetFile Object Target File
92 */
93 public function removeRemote($post_id, $meta, $backup_sizes, $sourceFile, $targetFile )
94 {
95 // Always remove because also thumbnails can be different.
96 $a3cfItem = $this->getItemById($post_id); // MediaItem is AS3CF Object
97 if ($a3cfItem === false)
98 {
99 Log::addDebug('S3-Offload MediaItem not remote - ' . $post_id);
100 return false;
101 }
102
103 $remove = \DeliciousBrains\WP_Offload_Media\Items\Remove_Provider_Handler::get_item_handler_key_name();
104 $itemHandler = $this->as3cf->get_item_handler($remove);
105
106 $result = $itemHandler->handle($a3cfItem, array( 'verify_exists_on_local' => false)); //handle it then.
107
108 }
109
110 // @param s3 based URL that which is needed for finding local path
111 // @return String Filepath. Translated file path
112 public function getLocalPath($url, $sourceFileObj, $source_id)
113 {
114 $item = $this->getItemById($source_id);
115
116 if ($item === false)
117 {
118 $source_id = $this->getSourceIDByURL($url);
119 if (false !== $source_id)
120 $item = $this->getItemById($source_id);
121 }
122
123 if ($source_id == false)
124 {
125 Log::addError('Get Local Path: No source id for URL (Offload) ' . $url);
126 return false;
127 }
128
129 $original_path = $item->original_source_path(); // $values['original_source_path'];
130
131 if (wp_basename($url) !== wp_basename($original_path)) // thumbnails translate to main file.
132 {
133 $original_path = str_replace(wp_basename($original_path), wp_basename($url), $original_path);
134 }
135
136 $fs = emr()->filesystem();
137 $base = $fs->getWPUploadBase();
138
139 $file = $base . $original_path;
140 return $file;
141 }
142
143
144
145 public function isFileMovable($bool, $attach_id)
146 {
147 $item = $this->getItemById($attach_id);
148 if ($item === false)
149 {
150 return $bool;
151 }
152
153 // Can't move offloaded items.
154 if (is_object($item))
155 {
156 return false;
157 }
158
159 }
160
161 public function checkIfOffloaded($bool, $url)
162 {
163
164 $source_id = $this->sourceCache($url);
165
166 if (false === $source_id)
167 {
168 $extension = substr($url, strrpos($url, '.') + 1);
169 // If these filetypes are not in the cache, they cannot be found via geSourceyIDByUrl method ( not in path DB ), so it's pointless to try. If they are offloaded, at some point the extra-info might load.
170 if ($extension == 'webp' || $extension == 'avif')
171 {
172 return false;
173 }
174
175 $source_id = $this->getSourceIDByURL($url);
176 }
177
178 if ($source_id !== false)
179 {
180 return true;
181 }
182 else
183 {
184 return false;
185 }
186 }
187
188 // This is used in the converted. Might be deployed elsewhere for better control.
189 public function preventOffload($attach_id)
190 {
191 self::$offloadPrevented[$attach_id] = true;
192 }
193
194 public function preventOffloadOff($attach_id)
195 {
196 unset(self::$offloadPrevented[$attach_id]);
197 }
198
199 public function updateOriginalPath($source_url, $target_url, $post_id)
200 {
201 $item = $this->getItemById($post_id);
202
203 // If no item comes back, probably it's not offloaded
204 if (false === $item)
205 {
206 return;
207 }
208
209
210 $original_path = $item->original_path(); // Original path (non-scaled-)
211 $original_source_path = $item->original_source_path();
212 $path = $item->path();
213 $source_path = $item->source_path();
214
215 $wp_original = wp_get_original_image_path($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ));
216 $wp_original = apply_filters('emr/replace/original_image_path', $wp_original, $post_id);
217 $wp_source = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
218
219 $updated = false;
220
221 // If image is replaced with another name, the original soruce path will not match. This could also happen when an image is with -scaled as main is replaced by an image that doesn't have it. In all cases update the table to reflect proper changes.
222 if (wp_basename($wp_original) !== wp_basename($original_path))
223 {
224
225 $newpath = str_replace( wp_basename( $original_path ), wp_basename($wp_original), $original_path );
226
227 $item->set_original_path($newpath);
228
229 $newpath = str_replace( wp_basename( $original_source_path ), wp_basename($wp_original), $original_source_path );
230 $updated = true;
231
232 $item->set_original_source_path($newpath);
233
234 $item->save();
235 }
236 }
237
238 // When Offload is not offloaded but is created during the process of generate metadata in WP, wp_create_image_subsizes fires an update metadata after just moving the upload, before making any thumbnails. If this is the case and the file has an -scaled / original image setup, the original_source_path becomes the same as the source_path which creates issue later on when dealing with optimizing it, if the file is deleted on local server. Prevent this, and lean on later update metadata.
239 public function preventUpdateMetaData($bool, $data, $post_id, $old_provider_object)
240 {
241 if (isset(self::$offloadPrevented[$post_id]))
242 {
243 return true ; // return true to cancel.
244 }
245
246 return $bool;
247
248 }
249
250
251
252 // WP Offload -for some reason - returns the same result of get_attached_file and wp_get_original_image_path , which are different files (one scaled) which then causes a wrong copy action after optimizing the image ( wrong destination download of the remote file ). This happens if offload with delete is on. Attempt to fix the URL to reflect the differences between -scaled and not.
253 public function checkScaledUrl($filepath, $id)
254 {
255 // Original filepath can never have a scaled in there.
256 // @todo This should probably check -scaled.<extension> as string end preventing issues.
257 if (strpos($filepath, '-scaled') !== false)
258 {
259 $filepath = str_replace('-scaled', '', $filepath);
260 }
261 return $filepath;
262 }
263
264 /** @return Returns S3Ofload MediaItem, or false when this does not exist */
265 protected function getItemById($id, $create = false)
266 {
267 $class = $this->getMediaClass();
268 $mediaItem = $class::get_by_source_id($id);
269
270 if (true === $create && $mediaItem === false)
271 {
272 $mediaItem = $class::create_from_source_id($id);
273 }
274
275 return $mediaItem;
276 }
277
278 protected function getSourceIDByURL($url)
279 {
280 $source_id = $this->sourceCache($url); // check cache first.
281
282 if (false === $source_id) // check on the raw url.
283 {
284 $class = $this->getMediaClass();
285
286 $parsedUrl = parse_url($url);
287
288 if (! isset($parsedUrl['scheme']) || ! in_array($parsedUrl['scheme'], array('http','https')))
289 {
290 $url = 'http://' . $url; //str_replace($parsedUrl['scheme'], 'https', $url);
291 }
292
293 $source = $class::get_item_source_by_remote_url($url);
294
295 $source_id = isset($source['id']) ? intval($source['id']) : false;
296 }
297
298 if (false === $source_id) // check now via the thumbnail hocus.
299 {
300 $pattern = '/(.*)-\d+[xX]\d+(\.\w+)/m';
301 $url = preg_replace($pattern, '$1$2', $url);
302
303 $source_id = $this->sourceCache($url); // check cache first.
304
305 if (false === $source_id)
306 {
307 $source = $class::get_item_source_by_remote_url($url);
308 $source_id = isset($source['id']) ? intval($source['id']) : false;
309 }
310
311 }
312
313 // Check issue with double extensions. If say double webp/avif is on, the double extension causes the URL not to be found (ie .jpg)
314 if (false === $source_id)
315 {
316 if (substr_count($parsedUrl['path'], '.') > 1)
317 {
318 // Get extension
319 $ext = substr(strrchr($url, '.'), 1);
320
321 // Remove all extensions from the URL
322 $checkurl = substr($url, 0, strpos($url,'.')) ;
323
324 // Add back the last one.
325 $checkurl .= '.' . $ext;
326
327 // Retry
328 $source_id = $this->sourceCache($checkurl); // check cache first.
329
330 if (false === $source_id)
331 {
332 $source = $class::get_item_source_by_remote_url($url);
333 $source_id = isset($source['id']) ? intval($source['id']) : false;
334 }
335
336
337 }
338 }
339
340 if ($source_id !== false)
341 {
342
343 $this->sourceCache($url, $source_id); // cache it.
344
345 // get item
346 $item = $this->getItemById($source_id);
347 if (is_object($item) && method_exists($item, 'extra_info'))
348 {
349 $baseUrl = str_replace(basename($url),'', $url);
350 $extra_info = $item->extra_info();
351
352 if (isset($extra_info['objects']))
353 {
354 foreach($extra_info['objects'] as $extraItem)
355 {
356 if (is_array($extraItem) && isset($extraItem['source_file']))
357 {
358 // Add source stuff into cache.
359 $this->sourceCache($baseUrl . $extraItem['source_file'], $source_id);
360 }
361 }
362 }
363 }
364
365 return $source_id;
366 }
367
368 return false;
369 }
370
371 private function sourceCache($url, $source_id = null)
372 {
373 if ($source_id === null && isset($this->sources[$url]))
374 {
375 $source_id = $this->sources[$url];
376 return $source_id;
377 }
378 elseif ($source_id !== null)
379 {
380 if (! isset($this->sources[$url]))
381 {
382 $this->sources[$url] = $source_id;
383 }
384 return $source_id;
385 }
386
387 return false;
388 }
389
390
391 private function getMediaClass()
392 {
393 $class = $this->as3cf->get_source_type_class('media-library');
394 return $class;
395 }
396
397 }
1 <?php
2 namespace EnableMediaReplace\Externals;
3
4
5 // Note! This class doubles as integration for both Visual Composer *and* WP Bakery. They both need URLENCODE.
6 class WpBakery
7 {
8 private static $instance;
9
10 protected $queryKey = 'wpbakery';
11
12 public static function getInstance()
13 {
14 if (is_null(self::$instance))
15 self::$instance = new WpBakery();
16
17 return self::$instance;
18 }
19
20 public function __construct()
21 {
22 if ($this->bakery_is_active()) // elementor is active
23 {
24 add_filter('emr/replacer/custom_replace_query', array($this, 'addURLEncoded'), 10, 4); // custom query for elementor \ // problem
25 }
26 }
27
28 public function addUrlEncoded($items, $base_url, $search_urls, $replace_urls)
29 {
30 $base_url = $this->addEncode($base_url);
31 $el_search_urls = array_map(array($this, 'addEncode'), $search_urls);
32 $el_replace_urls = array_map(array($this, 'addEncode'), $replace_urls);
33 $items[$this->queryKey] = array('base_url' => $base_url, 'search_urls' => $el_search_urls, 'replace_urls' => $el_replace_urls);
34 return $items;
35 }
36
37 public function addEncode($value)
38 {
39 return urlencode($value);
40 }
41
42 protected function bakery_is_active()
43 {
44 $bool = false;
45
46 // did_action -> wpbakery , VCV_version -> detect Visual Composer
47 if (did_action('vc_plugins_loaded') || defined('VCV_VERSION'))
48 $bool = true;
49
50 return apply_filters('emr/externals/urlencode_is_active', $bool); // manual override
51 }
52 }
1 <?php
2 namespace EnableMediaReplace;
3 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4 use EnableMediaReplace\Notices\NoticeController as Notices;
5
6 use EnableMediaReplace\Externals\Elementor as Elementor;
7 use EnableMediaReplace\Externals\WpBakery as WpBakery;
8 use EnableMediaReplace\Externals\SiteOrigin as SiteOrigin;
9 use EnableMediaReplace\Externals\WPOffload as WPOffload;
10
11
12 class Externals
13 {
14 protected $replaceType = null;
15 protected $replaceSearchType = null;
16
17 protected $messages = array();
18
19 public function __construct()
20 {
21 // These hooks prevent loading of options when plugin conflicts arise.
22 add_filter('emr_display_replace_type_options', array($this, 'get_replace_type'));
23 add_filter('emr_enable_replace_and_search', array($this, 'get_replacesearch_type'));
24 add_action('emr_after_replace_type_options', array($this, 'get_messages'));
25
26 $this->check();
27
28 // integrations
29 $this->loadElementor();
30 $this->loadBakery(); // in case of urlencoded issues, this class should be used probably.
31 $this->loadSiteOrigins();
32 $this->loadWpOffload();
33 }
34
35 protected function check() // check if any of the options should be disabled due to conflicts
36 {
37 /*if (class_exists('FLBuilder'))
38 {
39 $this->replaceSearchType = false;
40 $this->messages[] return true;
41 = __('Replace and Search feature is not compatible with Beaver Builder.', 'enable-media-replace');
42 } */
43 }
44
45 public function get_replace_type($bool)
46 {
47 if ($this->replaceType === null)
48 return $bool;
49
50 return $this->replaceType;
51 }
52
53 public function get_replacesearch_type($bool)
54 {
55 if ($this->replaceSearchType === null)
56 return $bool;
57
58 return $this->replaceSearchType;
59 }
60
61 public function get_messages()
62 {
63 foreach($this->messages as $message)
64 {
65 echo '<span class="nofeature-notice"><p>'. $message . '</p></span>';
66 }
67 }
68
69 public function loadElementor()
70 {
71 Elementor::getInstance();
72 }
73
74 public function loadBakery()
75 {
76 WpBakery::getInstance();
77 }
78
79 public function loadSiteOrigins()
80 {
81 SiteOrigin::getInstance();
82 }
83
84 public function loadWPOffload()
85 {
86 WPOffload::getInstance();
87 }
88
89 } // class
1 <?php
2 namespace EnableMediaReplace;
3
4 // Legacy functions.
5
6 /**
7 * Maybe remove query string from URL.
8 *
9 * @param string $url
10 *
11 * @return string
12 */
13
14 function emr_maybe_remove_query_string( $url ) {
15 $parts = explode( '?', $url );
16
17 return reset( $parts );
18 }
19
20 /**
21 * Remove scheme from URL.
22 *
23 * @param string $url
24 *
25 * @return string
26 */
27 function emr_remove_scheme( $url ) {
28 return preg_replace( '/^(?:http|https):/', '', $url );
29 }
30
31 /**
32 * Remove size from filename (image[-100x100].jpeg).
33 *
34 * @param string $url
35 * @param bool $remove_extension
36 *
37 * @return string
38 */
39 function emr_remove_size_from_filename( $url, $remove_extension = false ) {
40 $url = preg_replace( '/^(\S+)-[0-9]{1,4}x[0-9]{1,4}(\.[a-zA-Z0-9\.]{2,})?/', '$1$2', $url );
41
42 if ( $remove_extension ) {
43 $ext = pathinfo( $url, PATHINFO_EXTENSION );
44 $url = str_replace( ".$ext", '', $url );
45 }
46
47 return $url;
48 }
49
50 /**
51 * Strip an image URL down to bare minimum for matching.
52 *
53 * @param string $url
54 *
55 * @return string
56 */
57 function emr_get_match_url($url) {
58 $url = emr_remove_scheme($url);
59 $url = emr_maybe_remove_query_string($url);
60 $url = emr_remove_size_from_filename($url, true); // and extension is removed.
61 $url = emr_remove_domain_from_filename($url);
62 return $url;
63 }
64
65
66 function emr_remove_domain_from_filename($url) {
67 // Holding place for possible future function
68 $url = str_replace(emr_remove_scheme(get_bloginfo('url')), '', $url);
69 return $url;
70 }
71
72 /**
73 * Build an array of search or replace URLs for given attachment GUID and its metadata.
74 *
75 * @param string $guid
76 * @param array $metadata
77 *
78 * @return array
79 */
80 function emr_get_file_urls( $guid, $metadata ) {
81 $urls = array();
82
83 $guid = emr_remove_scheme( $guid );
84 $guid= emr_remove_domain_from_filename($guid);
85
86 $urls['guid'] = $guid;
87
88 if ( empty( $metadata ) ) {
89 return $urls;
90 }
91
92 $base_url = dirname( $guid );
93
94 if ( ! empty( $metadata['file'] ) ) {
95 $urls['file'] = trailingslashit( $base_url ) . wp_basename( $metadata['file'] );
96 }
97
98 if ( ! empty( $metadata['sizes'] ) ) {
99 foreach ( $metadata['sizes'] as $key => $value ) {
100 $urls[ $key ] = trailingslashit( $base_url ) . wp_basename( $value['file'] );
101 }
102 }
103
104 return $urls;
105 }
106
107 /**
108 * Ensure new search URLs cover known sizes for old attachment.
109 * Falls back to full URL if size not covered (srcset or width/height attributes should compensate).
110 *
111 * @param array $old
112 * @param array $new
113 *
114 * @return array
115 */
116 function emr_normalize_file_urls( $old, $new ) {
117 $result = array();
118
119 if ( empty( $new['guid'] ) ) {
120 return $result;
121 }
122
123 $guid = $new['guid'];
124
125 foreach ( $old as $key => $value ) {
126 $result[ $key ] = empty( $new[ $key ] ) ? $guid : $new[ $key ];
127 }
128
129 return $result;
130 }
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 }
1 <?php
2 namespace EnableMediaReplace;
3 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4 use EnableMediaReplace\Notices\NoticeController as Notices;
5
6 /* Collection of functions helping the interface being cleaner. */
7 class UIHelper
8 {
9 protected $preview_size = '';
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
51 if (isset($_REQUEST['SHORTPIXEL_DEBUG']))
52 {
53 $spdebug = $_REQUEST['SHORTPIXEL_DEBUG'];
54 if (is_numeric($spdebug))
55 $spdebug = intval($spdebug);
56 else {
57 $spdebug = sanitize_text_field($spdebug);
58 }
59
60 $url = add_query_arg('SHORTPIXEL_DEBUG', $spdebug, $url);
61 }
62
63 return $url;
64
65 }
66
67 public function getSuccesRedirect($attach_id)
68 {
69 $url = admin_url('post.php');
70 $url = add_query_arg(array('action' => 'edit', 'post' => $attach_id, 'emr_replaced' => '1'), $url);
71
72 if (isset($_REQUEST['SHORTPIXEL_DEBUG']))
73 {
74 $spdebug = $_REQUEST['SHORTPIXEL_DEBUG'];
75 if (is_numeric($spdebug))
76 $spdebug = intval($spdebug);
77 else {
78 $spdebug = sanitize_text_field($spdebug);
79 }
80
81 $url = add_query_arg('SHORTPIXEL_DEBUG', $spdebug, $url);
82 }
83
84 $url = apply_filters('emr_returnurl', $url);
85
86 return $url;
87
88 }
89
90 public function getBackgroundRemoveRedirect($attach_id)
91 {
92 //if ( isset( $_POST['remove_after_progress'] ) ) {
93 $url = admin_url("upload.php");
94 $url = add_query_arg(array(
95 'page' => 'enable-media-replace/enable-media-replace.php',
96 'action' => 'emr_prepare_remove',
97 'attachment_id' => $attach_id,
98 ), $url);
99
100 // $redirect_success = $url;
101 return $url;
102 // }
103 }
104
105 public function getFailedRedirect($attach_id)
106 {
107 $url = admin_url('upload.php');
108 $url = add_query_arg(array(
109 'page' => 'enable-media-replace/enable-media-replace.php',
110 'action' => 'media_replace',
111 'attachment_id' => $attach_id,
112 '_wpnonce' => wp_create_nonce('media_replace'),
113 ), $url
114 );
115
116 $url = apply_filters('emr_returnurl_failed', $url);
117 return $url;
118 }
119
120
121
122 public function setPreviewSizes()
123 {
124
125 list($this->preview_size, $this->preview_width, $this->preview_height) = $this->findImageSizeByMax($this->preview_max_width);
126 }
127
128 public function setSourceSizes($attach_id)
129 {
130 $data = $this->getImageSizes($attach_id, 'full'); // wp_get_attachment_image_src($attach_id, 'full');
131 // $file = get_attached_file($attach_id);
132
133 if (is_array($data))
134 {
135
136 $this->full_width = $data[1];
137 $this->full_height = $data[2];
138 }
139
140 }
141
142 protected function getImageSizes($attach_id, $size = 'thumbnail')
143 {
144 // 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
145 // $data = wp_get_attachment_image_src($attach_id, $size);
146 $meta = wp_get_attachment_metadata($attach_id);
147
148 $data = false;
149
150 if (isset($meta['sizes']))
151 {
152 foreach($meta['sizes'] as $sizeName => $metaData)
153 {
154 if ($sizeName == $size)
155 {
156 $width = isset($metaData['width']) ? $metaData['width'] : 0;
157 $height = isset($metaData['height']) ? $metaData['height'] : 0;
158 $imgData = image_downsize($attach_id, $size); // return whole array w/ possible wrong dimensions.
159 $data = array($imgData[0], $width, $height);
160 }
161 }
162 }
163
164 if ($data === false)
165 {
166 $data = wp_get_attachment_image_src($attach_id, $size);
167 $width = isset($data[1]) ? $data[1] : 0;
168 }
169
170 $file = get_attached_file($attach_id, true);
171 if (! file_exists($file))
172 return $data;
173
174 $mime_type = wp_get_image_mime($file);
175
176 if (strpos($mime_type, 'svg') !== false && $width <= 5)
177 {
178 $file = get_attached_file($attach_id);
179 $data = $this->fixSVGSize($data, $file);
180 }
181
182
183 return $data;
184 }
185
186 protected function fixSVGSize($data, $file)
187 {
188 if (! function_exists('simplexml_load_file'))
189 return $data;
190
191 $xml = simplexml_load_file($file);
192 //Log::addDebug('XML LOAD FILE', $xml);
193 if ($xml)
194 { // stolen from SVG Upload plugin
195 $attr = $xml->attributes();
196 $viewbox = explode(' ', $attr->viewBox);
197 $data[1] = isset($attr->width) && preg_match('/\d+/', $attr->width, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[2] : null);
198 $data[2] = isset($attr->height) && preg_match('/\d+/', $attr->height, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[3] : null);
199 }
200
201 return $data;
202 }
203
204 // Returns Preview Image HTML Output.
205 public function getPreviewImage($attach_id,$file, $args = array())
206 {
207 $data = false;
208
209 if ($attach_id > 0)
210 {
211 $data = $this->getImageSizes($attach_id, $this->preview_size); //wp_get_attachment_image_src($attach_id, $this->preview_size);
212 }
213
214 $mime_type = get_post_mime_type($attach_id);
215
216 if (! is_array($data) || (! $file->exists() && ! $file->is_virtual()) )
217 {
218 // if attachid higher than zero ( exists ) but not the image, fail, that's an error state.
219 $icon = ($attach_id < 0) ? '' : 'dashicons-no';
220 $is_document = false;
221
222 $defaults = array(
223 'width' => $this->preview_width,
224 'height' => $this->preview_height,
225 'is_image' => false,
226 'is_document' => $is_document,
227 'is_upload' => false,
228 'icon' => $icon,
229 'mime_type' => null,
230 );
231 $args = wp_parse_args($args, $defaults);
232
233 // failed, it might be this server doens't support PDF thumbnails. Fallback to File preview.
234 if ($mime_type == 'application/pdf')
235 {
236 return $this->getPreviewFile($attach_id, $file);
237 }
238
239 return $this->getPlaceHolder($args);
240 }
241
242 $url = $data[0];
243 $width = $data[1];
244 $height = $data[2];
245
246 // width
247 $width_ratio = $height_ratio = 0;
248 if ($width > $this->preview_max_width)
249 {
250 $width_ratio = $width / $this->preview_max_width;
251 }
252 if ($height > $this->preview_max_height) // height
253 {
254 $height_ratio = $height / $this->preview_max_height;
255 }
256
257 $ratio = ($width_ratio > $height_ratio) ? $width_ratio : $height_ratio;
258
259 if ($ratio > 0)
260 {
261
262 $width = floor($width / $ratio);
263 $height = floor($height / $ratio);
264 }
265
266 // SVG's without any helpers return around 0 for width / height. Fix preview.
267
268
269 // preview width, if source if found, should be set to source.
270 $this->preview_width = $width;
271 $this->preview_height = $height;
272
273 $image = "<img src='$url' width='$width' height='$height' class='image' style='max-width:100%; max-height: 100%;' />";
274 // $image = "<span class='the-image' style='background: url(\"" . $url . "\")';>&nbsp;</span>";
275
276 $defaults = array(
277 'width' => $width,
278 'height' => $height,
279 'image' => $image,
280 'mime_type' => $mime_type,
281 'is_upload' => false,
282 'file_size' => $file->getFileSize(),
283 );
284
285 $args = wp_parse_args($args, $defaults);
286
287 $output = $this->getPlaceHolder($args);
288 return $output;
289 }
290
291 public function getPreviewError($attach_id)
292 {
293 $args = array(
294 'width' => $this->preview_width,
295 'height' => $this->preview_height,
296 'icon' => 'dashicons-no',
297 'is_image' => false,
298 );
299 $output = $this->getPlaceHolder($args);
300 return $output;
301 }
302
303 public function getPreviewFile($attach_id, $file, $args = array())
304 {
305 if ($attach_id > 0)
306 {
307 //$filepath = get_attached_file($attach_id);
308 $filename = $file->getFileName();
309 }
310 else {
311 $filename = false;
312 }
313
314 $mime_type = $file->getMime();
315 if (false === $mime_type) // If server is not supporting this, go w/ the post mime type one.
316 {
317 $mime_type = get_post_mime_type($attach_id);
318 }
319
320 $defaults = array(
321 'width' => 300,
322 'height' => 300,
323 'is_image' => false,
324 'is_document' => true,
325 'is_upload' => false,
326 'layer' => $filename,
327 'mime_type' => $mime_type,
328 'file_size' => $file->getFileSize(),
329 );
330 $args = wp_parse_args($args, $defaults);
331
332
333 $output = $this->getPlaceHolder($args);
334 return $output;
335 }
336
337 public function findImageSizeByMax($maxwidth)
338 {
339 $image_sizes = $this->wp_get_image_sizes();
340
341 $match_width = 0;
342 $match_height = 0;
343 $match = '';
344
345 foreach($image_sizes as $sizeName => $sizeItem)
346 {
347
348 $width = $sizeItem['width'];
349 if ($width > $match_width && $width <= $maxwidth)
350 {
351 $match = $sizeName;
352 $match_width = $width;
353 $match_height = $sizeItem['height'];
354 }
355 }
356 return array($match, $match_width, $match_height);
357 }
358
359 public function getPlaceHolder($args)
360 {
361 $defaults = array(
362 'width' => 150,
363 'height' => 150,
364 'image' => '',
365 'icon' => 'dashicons-media-document',
366 'layer' => $this->full_width . ' x ' . $this->full_height,
367 'is_image' => true,
368 'is_document' => false,
369 'is_upload' => false, // indicating right-side upload interface for image.
370 'mime_type' => false,
371 'file_size' => false,
372 'remove_bg_ui' => false, // In process icons et al when removing background, for preview pane.
373
374 );
375
376 $args = wp_parse_args($args, $defaults);
377
378 $w = $args['width'];
379 $h = $args['height'];
380
381 if ($w < 150) // minimum
382 $w = 150;
383 if ($h < 150)
384 $h = 150;
385
386 $icon = $args['icon'];
387
388 if ($args['is_image'])
389 {
390 $placeholder_class = 'is_image';
391 }
392 else {
393 $placeholder_class = 'not_image';
394 }
395
396 if ($args['is_document'])
397 {
398 $placeholder_class .= ' is_document';
399 }
400
401 if (true == $args['is_upload'])
402 {
403 $placeholder_class .= ' upload-file-action';
404 }
405
406 $filetype = '';
407 if ($args['mime_type'])
408 {
409 $filetype = 'data-filetype="' . $args['mime_type'] . '"';
410 }
411
412 $filesize = ($args['file_size']) ? $args['file_size'] : '';
413
414 $is_backgroundremove_ui = (isset($args['remove_bg_ui']) && $args['remove_bg_ui'] == true);
415 $background_remove_ui = ($is_backgroundremove_ui) ? $this->getBgremoveUI() : '';
416
417 $output = "<div class='image_placeholder $placeholder_class' $filetype style='width:" . $w . "px; height:". $h ."px'> ";
418 if (true === $args['is_upload'])
419 {
420 $output .= "<span class='upload-title'>" . __('New', 'enable-media-replace') . "</span>";
421 $output .= '<input type="file" name="userfile" id="upload-file" />';
422 $output .= '<div class="drag-and-drop-title">
423 <span>' . __('Click here to upload or drop file in area', 'enable-media-replace') . '</span>
424 </div>';
425 }
426 else
427 {
428 $title = (true === $is_backgroundremove_ui ) ? __('Preview', 'enable-media-replace') : __('Current', 'enable-media-replace');
429 $output .= "<span class='upload-title'>" . $title . "</span>";
430 $output .= $args['image'];
431 }
432 $output .= "<div class='dashicons $icon'>&nbsp;</div>";
433 $output .= "<span class='textlayer'>" . $args['layer'] . "</span>";
434 $output .= $background_remove_ui;
435 $output .= "<div class='image_size'>" . $this->convertFileSize($filesize). "</div>";
436 $output .= "</div>";
437
438 return $output;
439 }
440
441 public function isBackgroundRemovable($post)
442 {
443 if (false === wp_attachment_is_image($post))
444 return false;
445
446 if (false === emr()->useFeature('background'))
447 return false;
448
449 $extensions = array('jpg', 'png','jpeg');
450
451 $mime = get_post_mime_type($post);
452 foreach($extensions as $extension)
453 {
454 if (strpos($mime, $extension) !== false )
455 return true;
456 }
457
458 return false;
459
460 }
461
462 private function getBgremoveUI()
463 {
464 $output = '<div class="overlay" id="overlay">
465 <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>';
466
467 $output .= '<h3>' . esc_html__('Removing background...', 'enable-media-replace') . '</h3>';
468 $output .= '</div>';
469
470 return $output;
471 }
472
473 private function convertFileSize($filesize)
474 {
475 return size_format($filesize);
476 }
477
478 /**
479 * Get size information for all currently-registered image sizes.
480 * Directly stolen from - https://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
481 * @global $_wp_additional_image_sizes
482 * @uses get_intermediate_image_sizes()
483 * @return array $sizes Data for all currently-registered image sizes.
484 */
485 private function wp_get_image_sizes() {
486 global $_wp_additional_image_sizes;
487
488 $sizes = array();
489
490 foreach ( get_intermediate_image_sizes() as $_size ) {
491 if ( in_array( $_size, array('thumbnail', 'medium', 'medium_large', 'large') ) ) {
492 $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" );
493 $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" );
494 } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
495 $sizes[ $_size ] = array(
496 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
497 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
498 );
499 }
500 }
501
502 return $sizes;
503 }
504
505 /** For Location Dir replacement. Get the Subdir that is in use now. */
506 public function getRelPathNow()
507 {
508 $uploadDir = wp_upload_dir();
509 if (isset($uploadDir['subdir']))
510 return ltrim($uploadDir['subdir'], '/');
511 else
512 return false;
513 }
514
515 public function featureNotice()
516 {
517 // @todo Remove in 2023.
518 $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>',
519 '<p>', '</p>');
520
521 $notice = Notices::addNormal($message, true);
522 Notices::makePersistent($notice, self::NOTICE_NEW_FEATURE, 2 * YEAR_IN_SECONDS);
523 }
524
525 } // class
1 .ui-widget-content {
2 border: 1px solid #dddddd;
3 background: #ffffff;
4 color: #333333;
5 }
6
7 .ui-widget-overlay {
8 position: fixed;
9 top: 0;
10 left: 0;
11 width: 100%;
12 height: 100%;
13 }
14
15 .ui-datepicker {
16 width: 17em;
17 padding: 0.2em 0.2em 0;
18 display: none;
19 }
20
21 .ui-datepicker .ui-datepicker-header {
22 position: relative;
23 padding: 0.2em 0;
24 }
25
26 .ui-datepicker .ui-datepicker-prev,
27 .ui-datepicker .ui-datepicker-next {
28 position: absolute;
29 top: 2px;
30 width: 1.8em;
31 height: 1.8em;
32 }
33
34 .ui-datepicker .ui-datepicker-prev-hover,
35 .ui-datepicker .ui-datepicker-next-hover {
36 top: 1px;
37 }
38
39 .ui-datepicker .ui-datepicker-prev {
40 left: 2px;
41 }
42
43 .ui-datepicker .ui-datepicker-next {
44 right: 2px;
45 }
46
47 .ui-datepicker .ui-datepicker-prev-hover {
48 left: 1px;
49 }
50
51 .ui-datepicker .ui-datepicker-next-hover {
52 right: 1px;
53 }
54
55 .ui-datepicker .ui-datepicker-prev span,
56 .ui-datepicker .ui-datepicker-next span {
57 display: block;
58 position: absolute;
59 left: 50%;
60 margin-left: -8px;
61 top: 50%;
62 margin-top: -8px;
63 }
64
65 .ui-datepicker .ui-datepicker-title {
66 margin: 0 2.3em;
67 line-height: 1.8em;
68 text-align: center;
69 }
70
71 .ui-datepicker .ui-datepicker-title select {
72 font-size: 1em;
73 margin: 1px 0;
74 }
75
76 .ui-datepicker select.ui-datepicker-month,
77 .ui-datepicker select.ui-datepicker-year {
78 width: 45%;
79 }
80
81 .ui-datepicker table {
82 width: 100%;
83 font-size: 0.9em;
84 border-collapse: collapse;
85 margin: 0 0 0.4em;
86 }
87
88 .ui-datepicker th {
89 padding: 0.7em 0.3em;
90 text-align: center;
91 font-weight: bold;
92 border: 0;
93 }
94
95 .ui-datepicker td {
96 border: 0;
97 padding: 1px;
98 }
99
100 .ui-datepicker td span,
101 .ui-datepicker td a {
102 display: block;
103 padding: 0.2em;
104 text-align: center;
105 text-decoration: none;
106 }
107
108 .ui-datepicker .ui-datepicker-buttonpane {
109 background-image: none;
110 margin: 0.7em 0 0 0;
111 padding: 0 0.2em;
112 border-left: 0;
113 border-right: 0;
114 border-bottom: 0;
115 }
116
117 .ui-datepicker .ui-datepicker-buttonpane button {
118 float: right;
119 margin: 0.5em 0.2em 0.4em;
120 cursor: pointer;
121 padding: 0.2em 0.6em 0.3em 0.6em;
122 width: auto;
123 overflow: visible;
124 }
125
126 .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
127 float: left;
128 }
129
130 /* with multiple calendars */
131 .ui-datepicker.ui-datepicker-multi {
132 width: auto;
133 }
134
135 .ui-datepicker-multi .ui-datepicker-group {
136 float: left;
137 }
138
139 .ui-datepicker-multi .ui-datepicker-group table {
140 width: 95%;
141 margin: 0 auto 0.4em;
142 }
143
144 .ui-datepicker-multi-2 .ui-datepicker-group {
145 width: 50%;
146 }
147
148 .ui-datepicker-multi-3 .ui-datepicker-group {
149 width: 33.3%;
150 }
151
152 .ui-datepicker-multi-4 .ui-datepicker-group {
153 width: 25%;
154 }
155
156 .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
157 .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
158 border-left-width: 0;
159 }
160
161 .ui-datepicker-multi .ui-datepicker-buttonpane {
162 clear: left;
163 }
164
165 .ui-datepicker-row-break {
166 clear: both;
167 width: 100%;
168 font-size: 0;
169 }
170
171 /* RTL support */
172 .ui-datepicker-rtl {
173 direction: rtl;
174 }
175
176 .ui-datepicker-rtl .ui-datepicker-prev {
177 right: 2px;
178 left: auto;
179 }
180
181 .ui-datepicker-rtl .ui-datepicker-next {
182 left: 2px;
183 right: auto;
184 }
185
186 .ui-datepicker-rtl .ui-datepicker-prev:hover {
187 right: 1px;
188 left: auto;
189 }
190
191 .ui-datepicker-rtl .ui-datepicker-next:hover {
192 left: 1px;
193 right: auto;
194 }
195
196 .ui-datepicker-rtl .ui-datepicker-buttonpane {
197 clear: right;
198 }
199
200 .ui-datepicker-rtl .ui-datepicker-buttonpane button {
201 float: left;
202 }
203
204 .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
205 .ui-datepicker-rtl .ui-datepicker-group {
206 float: right;
207 }
208
209 .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
210 .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
211 border-right-width: 0;
212 border-left-width: 1px;
213 }
214
215 /* Icons */
216 .ui-datepicker .ui-icon {
217 display: block;
218 text-indent: -99999px;
219 overflow: hidden;
220 background-repeat: no-repeat;
221 left: 0.5em;
222 top: 0.3em;
223 }
224
225 .ui-icon {
226 width: 16px;
227 height: 16px;
228 }
229
230 .ui-icon,
231 .ui-widget-content .ui-icon {
232 background-image: url("../img/ui-icons_444444_256x240.png");
233 }
234
235 .ui-widget-header .ui-icon {
236 background-image: url("../img/ui-icons_444444_256x240.png");
237 }
238
239 .ui-state-hover .ui-icon,
240 .ui-state-focus .ui-icon,
241 .ui-button:hover .ui-icon,
242 .ui-button:focus .ui-icon {
243 background-image: url("../img/ui-icons_555555_256x240.png");
244 }
245
246 .ui-state-active .ui-icon,
247 .ui-button:active .ui-icon {
248 background-image: url("../img/ui-icons_ffffff_256x240.png");
249 }
250
251 .ui-state-highlight .ui-icon,
252 .ui-button .ui-state-highlight.ui-icon {
253 background-image: url("../img/ui-icons_777620_256x240.png");
254 }
255
256 /* positioning */
257 .ui-icon-blank {
258 background-position: 16px 16px;
259 }
260
261 .ui-icon-caret-1-n {
262 background-position: 0 0;
263 }
264
265 .ui-icon-caret-1-ne {
266 background-position: -16px 0;
267 }
268
269 .ui-icon-caret-1-e {
270 background-position: -32px 0;
271 }
272
273 .ui-icon-caret-1-se {
274 background-position: -48px 0;
275 }
276
277 .ui-icon-caret-1-s {
278 background-position: -65px 0;
279 }
280
281 .ui-icon-caret-1-sw {
282 background-position: -80px 0;
283 }
284
285 .ui-icon-caret-1-w {
286 background-position: -96px 0;
287 }
288
289 .ui-icon-caret-1-nw {
290 background-position: -112px 0;
291 }
292
293 .ui-icon-caret-2-n-s {
294 background-position: -128px 0;
295 }
296
297 .ui-icon-caret-2-e-w {
298 background-position: -144px 0;
299 }
300
301 .ui-icon-triangle-1-n {
302 background-position: 0 -16px;
303 }
304
305 .ui-icon-triangle-1-ne {
306 background-position: -16px -16px;
307 }
308
309 .ui-icon-triangle-1-e {
310 background-position: -32px -16px;
311 }
312
313 .ui-icon-triangle-1-se {
314 background-position: -48px -16px;
315 }
316
317 .ui-icon-triangle-1-s {
318 background-position: -65px -16px;
319 }
320
321 .ui-icon-triangle-1-sw {
322 background-position: -80px -16px;
323 }
324
325 .ui-icon-triangle-1-w {
326 background-position: -96px -16px;
327 }
328
329 .ui-icon-triangle-1-nw {
330 background-position: -112px -16px;
331 }
332
333 .ui-icon-triangle-2-n-s {
334 background-position: -128px -16px;
335 }
336
337 .ui-icon-triangle-2-e-w {
338 background-position: -144px -16px;
339 }
340
341 .ui-icon-arrow-1-n {
342 background-position: 0 -32px;
343 }
344
345 .ui-icon-arrow-1-ne {
346 background-position: -16px -32px;
347 }
348
349 .ui-icon-arrow-1-e {
350 background-position: -32px -32px;
351 }
352
353 .ui-icon-arrow-1-se {
354 background-position: -48px -32px;
355 }
356
357 .ui-icon-arrow-1-s {
358 background-position: -65px -32px;
359 }
360
361 .ui-icon-arrow-1-sw {
362 background-position: -80px -32px;
363 }
364
365 .ui-icon-arrow-1-w {
366 background-position: -96px -32px;
367 }
368
369 .ui-icon-arrow-1-nw {
370 background-position: -112px -32px;
371 }
372
373 .ui-icon-arrow-2-n-s {
374 background-position: -128px -32px;
375 }
376
377 .ui-icon-arrow-2-ne-sw {
378 background-position: -144px -32px;
379 }
380
381 .ui-icon-arrow-2-e-w {
382 background-position: -160px -32px;
383 }
384
385 .ui-icon-arrow-2-se-nw {
386 background-position: -176px -32px;
387 }
388
389 .ui-icon-arrowstop-1-n {
390 background-position: -192px -32px;
391 }
392
393 .ui-icon-arrowstop-1-e {
394 background-position: -208px -32px;
395 }
396
397 .ui-icon-arrowstop-1-s {
398 background-position: -224px -32px;
399 }
400
401 .ui-icon-arrowstop-1-w {
402 background-position: -240px -32px;
403 }
404
405 .ui-icon-arrowthick-1-n {
406 background-position: 1px -48px;
407 }
408
409 .ui-icon-arrowthick-1-ne {
410 background-position: -16px -48px;
411 }
412
413 .ui-icon-arrowthick-1-e {
414 background-position: -32px -48px;
415 }
416
417 .ui-icon-arrowthick-1-se {
418 background-position: -48px -48px;
419 }
420
421 .ui-icon-arrowthick-1-s {
422 background-position: -64px -48px;
423 }
424
425 .ui-icon-arrowthick-1-sw {
426 background-position: -80px -48px;
427 }
428
429 .ui-icon-arrowthick-1-w {
430 background-position: -96px -48px;
431 }
432
433 .ui-icon-arrowthick-1-nw {
434 background-position: -112px -48px;
435 }
436
437 .ui-icon-arrowthick-2-n-s {
438 background-position: -128px -48px;
439 }
440
441 .ui-icon-arrowthick-2-ne-sw {
442 background-position: -144px -48px;
443 }
444
445 .ui-icon-arrowthick-2-e-w {
446 background-position: -160px -48px;
447 }
448
449 .ui-icon-arrowthick-2-se-nw {
450 background-position: -176px -48px;
451 }
452
453 .ui-icon-arrowthickstop-1-n {
454 background-position: -192px -48px;
455 }
456
457 .ui-icon-arrowthickstop-1-e {
458 background-position: -208px -48px;
459 }
460
461 .ui-icon-arrowthickstop-1-s {
462 background-position: -224px -48px;
463 }
464
465 .ui-icon-arrowthickstop-1-w {
466 background-position: -240px -48px;
467 }
468
469 .ui-icon-arrowreturnthick-1-w {
470 background-position: 0 -64px;
471 }
472
473 .ui-icon-arrowreturnthick-1-n {
474 background-position: -16px -64px;
475 }
476
477 .ui-icon-arrowreturnthick-1-e {
478 background-position: -32px -64px;
479 }
480
481 .ui-icon-arrowreturnthick-1-s {
482 background-position: -48px -64px;
483 }
484
485 .ui-icon-arrowreturn-1-w {
486 background-position: -64px -64px;
487 }
488
489 .ui-icon-arrowreturn-1-n {
490 background-position: -80px -64px;
491 }
492
493 .ui-icon-arrowreturn-1-e {
494 background-position: -96px -64px;
495 }
496
497 .ui-icon-arrowreturn-1-s {
498 background-position: -112px -64px;
499 }
500
501 .ui-icon-arrowrefresh-1-w {
502 background-position: -128px -64px;
503 }
504
505 .ui-icon-arrowrefresh-1-n {
506 background-position: -144px -64px;
507 }
508
509 .ui-icon-arrowrefresh-1-e {
510 background-position: -160px -64px;
511 }
512
513 .ui-icon-arrowrefresh-1-s {
514 background-position: -176px -64px;
515 }
516
517 .ui-icon-arrow-4 {
518 background-position: 0 -80px;
519 }
520
521 .ui-icon-arrow-4-diag {
522 background-position: -16px -80px;
523 }
524
525 .ui-icon-extlink {
526 background-position: -32px -80px;
527 }
528
529 .ui-icon-newwin {
530 background-position: -48px -80px;
531 }
532
533 .ui-icon-refresh {
534 background-position: -64px -80px;
535 }
536
537 .ui-icon-shuffle {
538 background-position: -80px -80px;
539 }
540
541 .ui-icon-transfer-e-w {
542 background-position: -96px -80px;
543 }
544
545 .ui-icon-transferthick-e-w {
546 background-position: -112px -80px;
547 }
548
549 .ui-icon-folder-collapsed {
550 background-position: 0 -96px;
551 }
552
553 .ui-icon-folder-open {
554 background-position: -16px -96px;
555 }
556
557 .ui-icon-document {
558 background-position: -32px -96px;
559 }
560
561 .ui-icon-document-b {
562 background-position: -48px -96px;
563 }
564
565 .ui-icon-note {
566 background-position: -64px -96px;
567 }
568
569 .ui-icon-mail-closed {
570 background-position: -80px -96px;
571 }
572
573 .ui-icon-mail-open {
574 background-position: -96px -96px;
575 }
576
577 .ui-icon-suitcase {
578 background-position: -112px -96px;
579 }
580
581 .ui-icon-comment {
582 background-position: -128px -96px;
583 }
584
585 .ui-icon-person {
586 background-position: -144px -96px;
587 }
588
589 .ui-icon-print {
590 background-position: -160px -96px;
591 }
592
593 .ui-icon-trash {
594 background-position: -176px -96px;
595 }
596
597 .ui-icon-locked {
598 background-position: -192px -96px;
599 }
600
601 .ui-icon-unlocked {
602 background-position: -208px -96px;
603 }
604
605 .ui-icon-bookmark {
606 background-position: -224px -96px;
607 }
608
609 .ui-icon-tag {
610 background-position: -240px -96px;
611 }
612
613 .ui-icon-home {
614 background-position: 0 -112px;
615 }
616
617 .ui-icon-flag {
618 background-position: -16px -112px;
619 }
620
621 .ui-icon-calendar {
622 background-position: -32px -112px;
623 }
624
625 .ui-icon-cart {
626 background-position: -48px -112px;
627 }
628
629 .ui-icon-pencil {
630 background-position: -64px -112px;
631 }
632
633 .ui-icon-clock {
634 background-position: -80px -112px;
635 }
636
637 .ui-icon-disk {
638 background-position: -96px -112px;
639 }
640
641 .ui-icon-calculator {
642 background-position: -112px -112px;
643 }
644
645 .ui-icon-zoomin {
646 background-position: -128px -112px;
647 }
648
649 .ui-icon-zoomout {
650 background-position: -144px -112px;
651 }
652
653 .ui-icon-search {
654 background-position: -160px -112px;
655 }
656
657 .ui-icon-wrench {
658 background-position: -176px -112px;
659 }
660
661 .ui-icon-gear {
662 background-position: -192px -112px;
663 }
664
665 .ui-icon-heart {
666 background-position: -208px -112px;
667 }
668
669 .ui-icon-star {
670 background-position: -224px -112px;
671 }
672
673 .ui-icon-link {
674 background-position: -240px -112px;
675 }
676
677 .ui-icon-cancel {
678 background-position: 0 -128px;
679 }
680
681 .ui-icon-plus {
682 background-position: -16px -128px;
683 }
684
685 .ui-icon-plusthick {
686 background-position: -32px -128px;
687 }
688
689 .ui-icon-minus {
690 background-position: -48px -128px;
691 }
692
693 .ui-icon-minusthick {
694 background-position: -64px -128px;
695 }
696
697 .ui-icon-close {
698 background-position: -80px -128px;
699 }
700
701 .ui-icon-closethick {
702 background-position: -96px -128px;
703 }
704
705 .ui-icon-key {
706 background-position: -112px -128px;
707 }
708
709 .ui-icon-lightbulb {
710 background-position: -128px -128px;
711 }
712
713 .ui-icon-scissors {
714 background-position: -144px -128px;
715 }
716
717 .ui-icon-clipboard {
718 background-position: -160px -128px;
719 }
720
721 .ui-icon-copy {
722 background-position: -176px -128px;
723 }
724
725 .ui-icon-contact {
726 background-position: -192px -128px;
727 }
728
729 .ui-icon-image {
730 background-position: -208px -128px;
731 }
732
733 .ui-icon-video {
734 background-position: -224px -128px;
735 }
736
737 .ui-icon-script {
738 background-position: -240px -128px;
739 }
740
741 .ui-icon-alert {
742 background-position: 0 -144px;
743 }
744
745 .ui-icon-info {
746 background-position: -16px -144px;
747 }
748
749 .ui-icon-notice {
750 background-position: -32px -144px;
751 }
752
753 .ui-icon-help {
754 background-position: -48px -144px;
755 }
756
757 .ui-icon-check {
758 background-position: -64px -144px;
759 }
760
761 .ui-icon-bullet {
762 background-position: -80px -144px;
763 }
764
765 .ui-icon-radio-on {
766 background-position: -96px -144px;
767 }
768
769 .ui-icon-radio-off {
770 background-position: -112px -144px;
771 }
772
773 .ui-icon-pin-w {
774 background-position: -128px -144px;
775 }
776
777 .ui-icon-pin-s {
778 background-position: -144px -144px;
779 }
780
781 .ui-icon-play {
782 background-position: 0 -160px;
783 }
784
785 .ui-icon-pause {
786 background-position: -16px -160px;
787 }
788
789 .ui-icon-seek-next {
790 background-position: -32px -160px;
791 }
792
793 .ui-icon-seek-prev {
794 background-position: -48px -160px;
795 }
796
797 .ui-icon-seek-end {
798 background-position: -64px -160px;
799 }
800
801 .ui-icon-seek-start {
802 background-position: -80px -160px;
803 }
804
805 /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
806 .ui-icon-seek-first {
807 background-position: -80px -160px;
808 }
809
810 .ui-icon-stop {
811 background-position: -96px -160px;
812 }
813
814 .ui-icon-eject {
815 background-position: -112px -160px;
816 }
817
818 .ui-icon-volume-off {
819 background-position: -128px -160px;
820 }
821
822 .ui-icon-volume-on {
823 background-position: -144px -160px;
824 }
825
826 .ui-icon-power {
827 background-position: 0 -176px;
828 }
829
830 .ui-icon-signal-diag {
831 background-position: -16px -176px;
832 }
833
834 .ui-icon-signal {
835 background-position: -32px -176px;
836 }
837
838 .ui-icon-battery-0 {
839 background-position: -48px -176px;
840 }
841
842 .ui-icon-battery-1 {
843 background-position: -64px -176px;
844 }
845
846 .ui-icon-battery-2 {
847 background-position: -80px -176px;
848 }
849
850 .ui-icon-battery-3 {
851 background-position: -96px -176px;
852 }
853
854 .ui-icon-circle-plus {
855 background-position: 0 -192px;
856 }
857
858 .ui-icon-circle-minus {
859 background-position: -16px -192px;
860 }
861
862 .ui-icon-circle-close {
863 background-position: -32px -192px;
864 }
865
866 .ui-icon-circle-triangle-e {
867 background-position: -48px -192px;
868 }
869
870 .ui-icon-circle-triangle-s {
871 background-position: -64px -192px;
872 }
873
874 .ui-icon-circle-triangle-w {
875 background-position: -80px -192px;
876 }
877
878 .ui-icon-circle-triangle-n {
879 background-position: -96px -192px;
880 }
881
882 .ui-icon-circle-arrow-e {
883 background-position: -112px -192px;
884 }
885
886 .ui-icon-circle-arrow-s {
887 background-position: -128px -192px;
888 }
889
890 .ui-icon-circle-arrow-w {
891 background-position: -144px -192px;
892 }
893
894 .ui-icon-circle-arrow-n {
895 background-position: -160px -192px;
896 }
897
898 .ui-icon-circle-zoomin {
899 background-position: -176px -192px;
900 }
901
902 .ui-icon-circle-zoomout {
903 background-position: -192px -192px;
904 }
905
906 .ui-icon-circle-check {
907 background-position: -208px -192px;
908 }
909
910 .ui-icon-circlesmall-plus {
911 background-position: 0 -208px;
912 }
913
914 .ui-icon-circlesmall-minus {
915 background-position: -16px -208px;
916 }
917
918 .ui-icon-circlesmall-close {
919 background-position: -32px -208px;
920 }
921
922 .ui-icon-squaresmall-plus {
923 background-position: -48px -208px;
924 }
925
926 .ui-icon-squaresmall-minus {
927 background-position: -64px -208px;
928 }
929
930 .ui-icon-squaresmall-close {
931 background-position: -80px -208px;
932 }
933
934 .ui-icon-grip-dotted-vertical {
935 background-position: 0 -224px;
936 }
937
938 .ui-icon-grip-dotted-horizontal {
939 background-position: -16px -224px;
940 }
941
942 .ui-icon-grip-solid-vertical {
943 background-position: -32px -224px;
944 }
945
946 .ui-icon-grip-solid-horizontal {
947 background-position: -48px -224px;
948 }
949
950 .ui-icon-gripsmall-diagonal-se {
951 background-position: -64px -224px;
952 }
953
954 .ui-icon-grip-diagonal-se {
955 background-position: -80px -224px;
956 }
957
958 /* Corner radius */
959 .ui-corner-all,
960 .ui-corner-top,
961 .ui-corner-left,
962 .ui-corner-tl {
963 border-top-left-radius: 3px;
964 }
965
966 .ui-corner-all,
967 .ui-corner-top,
968 .ui-corner-right,
969 .ui-corner-tr {
970 border-top-right-radius: 3px;
971 }
972
973 .ui-corner-all,
974 .ui-corner-bottom,
975 .ui-corner-left,
976 .ui-corner-bl {
977 border-bottom-left-radius: 3px;
978 }
979
980 .ui-corner-all,
981 .ui-corner-bottom,
982 .ui-corner-right,
983 .ui-corner-br {
984 border-bottom-right-radius: 3px;
985 }
986
987 .enable-media-replace.emr-screen {
988 width: 90%;
989 background: #fff;
990 border: 1px solid #fff;
991 position: relative;
992 margin: 30px 0;
993 }
994 .enable-media-replace.emr-screen h3 {
995 font-size: 24px;
996 position: absolute;
997 top: -15px;
998 left: 30px;
999 padding: 16px 8px;
1000 border: 1px solid #000;
1001 background: #fff;
1002 margin: 0;
1003 }
1004 .enable-media-replace.emr-screen .content {
1005 padding: 32px;
1006 }
1007 .enable-media-replace.emr-screen h1 {
1008 font-weight: 700;
1009 }
1010 .enable-media-replace.emr-screen p {
1011 font-size: 14px;
1012 }
1013 .enable-media-replace.emr-screen .hide {
1014 display: none;
1015 }
1016 .enable-media-replace.emr-screen.error-screen h1 {
1017 color: #ff0000;
1018 }
1019
1020 #emr-drop-area-active {
1021 position: fixed;
1022 left: 0;
1023 right: 0;
1024 top: 0;
1025 bottom: 0;
1026 z-index: 9999;
1027 display: block;
1028 background: rgba(0, 0, 0, 0.5);
1029 border: 6px dashed #AAA;
1030 }
1031 #emr-drop-area-active h3 {
1032 font-size: 36px;
1033 color: #ddd;
1034 width: 200px;
1035 left: 50%;
1036 top: 50%;
1037 position: absolute;
1038 }
1039 #emr-drop-area-active .drop-wrapper {
1040 margin: 0 auto;
1041 }
1042
1043 .emr_upload_form form {
1044 display: flex;
1045 }
1046 .emr_upload_form form .upsell-wrapper {
1047 margin-left: 10px;
1048 }
1049 .emr_upload_form .editor-wrapper {
1050 width: 100%;
1051 }
1052 .emr_upload_form .emr_drop_area {
1053 display: none;
1054 }
1055 .emr_upload_form .wrapper {
1056 padding: 18px;
1057 border: 1px solid #ccc;
1058 }
1059 .emr_upload_form .wrapper .section-header {
1060 font-size: 18px;
1061 border-bottom: 1px solid #ccc;
1062 padding: 6px 0;
1063 margin: 0 0 15px 0;
1064 }
1065 .emr_upload_form .explainer {
1066 font-size: 14px;
1067 color: #555;
1068 max-width: 800px;
1069 margin: 20px 0;
1070 }
1071 .emr_upload_form .explainer .underline {
1072 text-decoration-style: dotted;
1073 text-decoration-line: underline;
1074 }
1075 .emr_upload_form .image_chooser.wrapper {
1076 min-height: 350px;
1077 }
1078 .emr_upload_form .image_chooser.wrapper .image_previews {
1079 margin: 15px 0;
1080 position: relative;
1081 }
1082 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder {
1083 position: relative;
1084 display: inline-block;
1085 margin-right: 25px;
1086 margin-bottom: 10px;
1087 border: 1px solid #ddd;
1088 vertical-align: top;
1089 box-sizing: border-box;
1090 background: #fff;
1091 max-width: calc(50% - 25px);
1092 }
1093 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder img {
1094 object-fit: contain;
1095 }
1096 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .upload-title {
1097 text-align: center;
1098 position: absolute;
1099 padding-top: 8px;
1100 line-height: 20px;
1101 font-weight: 700;
1102 width: 100%;
1103 left: 0;
1104 top: 0;
1105 }
1106 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder #upload-file {
1107 display: none;
1108 }
1109 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .textlayer {
1110 font-size: 25px;
1111 line-height: 25px;
1112 opacity: 0.7;
1113 position: absolute;
1114 color: #ccc;
1115 left: 48%;
1116 top: 50%;
1117 transform: translate(-50%, -50%);
1118 border: 1px dashed #eee;
1119 background-color: #333;
1120 padding: 8px;
1121 z-index: 5;
1122 }
1123 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .dashicons {
1124 font-size: 60px;
1125 position: absolute;
1126 top: 50%;
1127 margin-top: -30px;
1128 left: 50%;
1129 margin-left: -30px;
1130 opacity: 0.5;
1131 }
1132 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .image_size {
1133 text-align: center;
1134 position: absolute;
1135 bottom: -25px;
1136 width: 100%;
1137 }
1138 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .drag-and-drop-title {
1139 text-align: center;
1140 font-weight: 400;
1141 font-size: 14px;
1142 width: 100%;
1143 height: 100%;
1144 position: absolute;
1145 cursor: pointer;
1146 top: 0;
1147 left: 0;
1148 z-index: 1;
1149 }
1150 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .drag-and-drop-title span {
1151 position: absolute;
1152 top: 50%;
1153 transform: translate(-50%, -50%);
1154 padding: 5px 10px;
1155 width: 100%;
1156 }
1157 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.is_image .dashicons::before, .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.is_image .dashicons {
1158 display: none;
1159 }
1160 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.not_image img {
1161 display: none;
1162 }
1163 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.not_image .textlayer {
1164 display: none;
1165 }
1166 .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.not_image.is_document .textlayer {
1167 font-size: 18px;
1168 line-height: 20px;
1169 display: block;
1170 }
1171 .emr_upload_form .form-error, .emr_upload_form .form-warning {
1172 background: #fff;
1173 padding: 8px;
1174 border-left: 4px solid #ff0000;
1175 margin: 10px 0;
1176 display: none;
1177 }
1178 .emr_upload_form .form-error p, .emr_upload_form .form-warning p {
1179 margin: 0;
1180 font-size: 12px;
1181 font-weight: 700;
1182 }
1183 .emr_upload_form .form-warning {
1184 border-left: 4px solid #ffb900;
1185 }
1186 .emr_upload_form .option-flex-wrapper {
1187 display: flex;
1188 }
1189 .emr_upload_form .replace_type.wrapper {
1190 flex: 1;
1191 border: 1px solid #ccc;
1192 margin: 15px 0;
1193 }
1194 .emr_upload_form .replace_type.wrapper .option {
1195 position: relative;
1196 z-index: 1;
1197 }
1198 .emr_upload_form .replace_type.wrapper .option label {
1199 font-size: 1.2em;
1200 }
1201 .emr_upload_form .replace_type.wrapper .option .nofeature-notice {
1202 border: 1px solid #ccc;
1203 padding: 8px;
1204 margin: 0;
1205 position: absolute;
1206 left: 0;
1207 right: 0;
1208 top: 0;
1209 bottom: 0;
1210 opacity: 0.8;
1211 z-index: 9;
1212 background: #444;
1213 }
1214 .emr_upload_form .replace_type.wrapper .option .nofeature-notice p {
1215 text-align: center;
1216 color: #fff;
1217 margin: 15px 0;
1218 }
1219 .emr_upload_form .options.wrapper {
1220 flex: 1;
1221 border: 1px solid #ccc;
1222 padding: 15px;
1223 margin: 15px 0 15px 35px;
1224 }
1225 .emr_upload_form .options.wrapper .custom_date .emr_datepicker {
1226 width: 150px;
1227 }
1228 .emr_upload_form .options.wrapper .custom_date .emr_hour, .emr_upload_form .options.wrapper .custom_date .emr_minute {
1229 width: 45px;
1230 }
1231 .emr_upload_form .options.wrapper .replace_custom_date {
1232 margin-left: 5px;
1233 }
1234 .emr_upload_form .options.wrapper .replace_custom_date:hover {
1235 text-decoration: underline;
1236 cursor: pointer;
1237 }
1238 .emr_upload_form .options.wrapper ul li input {
1239 margin-right: 8px;
1240 }
1241 .emr_upload_form .options.wrapper .option label {
1242 vertical-align: top;
1243 }
1244 .emr_upload_form .options.wrapper .small {
1245 font-size: 10px;
1246 vertical-align: top;
1247 margin-left: 8px;
1248 }
1249 .emr_upload_form .options.wrapper .custom_date {
1250 margin: 8px 0 0 25px;
1251 visibility: hidden;
1252 opacity: 0;
1253 }
1254 .emr_upload_form .options.wrapper .custom_date span.field-title {
1255 display: inline-block;
1256 margin-bottom: 4px;
1257 color: #444;
1258 font-size: 12px;
1259 width: 100%;
1260 text-align: left;
1261 vertical-align: middle;
1262 line-height: 26px;
1263 }
1264 .emr_upload_form .options.wrapper .custom_date span.field-title::before {
1265 font-size: 20px;
1266 vertical-align: top;
1267 margin-right: 4px;
1268 }
1269 .emr_upload_form .options.wrapper .location_option {
1270 display: none;
1271 margin-top: 12px;
1272 }
1273 .emr_upload_form .options.wrapper .location_option label {
1274 vertical-align: baseline;
1275 margin-right: 8px;
1276 }
1277 .emr_upload_form .form_controls.wrapper {
1278 clear: both;
1279 margin: 8px 0 15px 0;
1280 border: 0;
1281 padding: 0;
1282 }
1283 .emr_upload_form .form_controls.wrapper .button {
1284 padding-left: 20px;
1285 padding-right: 20px;
1286 }
1287 .emr_upload_form .shortpixel.notice {
1288 padding: 12px;
1289 }
1290 .emr_upload_form .shortpixel-offer {
1291 background: #fff;
1292 width: 250px;
1293 min-height: 270px;
1294 border: 1px solid #ccc;
1295 padding: 10px;
1296 margin-bottom: 10px;
1297 float: right;
1298 clear: both;
1299 background-color: #dcfdff;
1300 }
1301 .emr_upload_form .shortpixel-offer h3 {
1302 color: #00d0e5;
1303 font-size: 18px;
1304 text-align: center;
1305 margin: 0;
1306 line-height: 1.3em;
1307 }
1308 .emr_upload_form .shortpixel-offer h4 {
1309 font-size: 16px;
1310 text-align: center;
1311 }
1312 .emr_upload_form .shortpixel-offer .red {
1313 color: #ff0000;
1314 }
1315 .emr_upload_form .shortpixel-offer .cyan {
1316 color: #00d0e5;
1317 }
1318 .emr_upload_form .shortpixel-offer .grey {
1319 color: grey;
1320 }
1321 .emr_upload_form .shortpixel-offer .ucase {
1322 text-transform: uppercase;
1323 }
1324 .emr_upload_form .shortpixel-offer a {
1325 text-decoration: none;
1326 }
1327 .emr_upload_form .shortpixel-offer .button-wrapper {
1328 text-align: center;
1329 margin-top: 35px;
1330 }
1331 .emr_upload_form .shortpixel-offer .button-wrapper a {
1332 background-color: #ff0000;
1333 color: #fff;
1334 display: inline-block;
1335 padding: 8px;
1336 text-decoration: none;
1337 font-weight: 700;
1338 font-size: 20px;
1339 text-transform: uppercase;
1340 }
1341 .emr_upload_form .shortpixel-offer .hidden {
1342 display: none !important;
1343 }
1344 .emr_upload_form .shortpixel-offer .img-wrapper {
1345 text-align: center;
1346 margin: 0 0 25px 0;
1347 }
1348 .emr_upload_form .shortpixel-offer .img-wrapper img {
1349 max-width: 140px;
1350 max-height: 140px;
1351 margin: 0;
1352 }
1353 .emr_upload_form .shortpixel-offer.envira-shortpixel-install {
1354 background: #fff;
1355 }
1356 .emr_upload_form .shortpixel-offer.theme-offer {
1357 background: #fff;
1358 }
1359 .emr_upload_form .shortpixel-offer.theme-offer img {
1360 max-width: 100%;
1361 }
1362 @media (max-width: 1200px) {
1363 .emr_upload_form .image_previews {
1364 text-align: center;
1365 }
1366 .emr_upload_form .option-flex-wrapper {
1367 flex-direction: column;
1368 }
1369 .emr_upload_form .option-flex-wrapper .options.wrapper {
1370 margin-left: 0;
1371 }
1372 }
1373 @media (max-width: 960px) {
1374 .emr_upload_form .upsell-wrapper {
1375 display: none;
1376 }
1377 }
1378 @media (max-width: 450px) {
1379 .emr_upload_form .replace_custom_date_wrapper {
1380 display: block;
1381 margin-top: 15px;
1382 font-size: 16px;
1383 }
1384 .emr_upload_form .location_option label {
1385 margin: 25px 0;
1386 display: inline-block;
1387 }
1388 }
1389
1390 /*# sourceMappingURL=admin.css.map */
1 {"version":3,"sourceRoot":"","sources":["../scss/_datepicker.scss","../scss/_screens.scss","../scss/admin.scss"],"names":[],"mappings":"AAEA;EACC;EACA;EACA;;;AAID;EACC;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;EACA;EACA;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;;;AAED;AAAA;EAEC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAGD;AACA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAGD;AACA;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;AAAA;EAEC;;;AAED;AAAA;EAEC;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAED;AAAA;EAEC;;;AAED;EACC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;EAEC;;;AAED;AAAA;EAEC;;;AAGD;AACA;EAAiB;;;AACjB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAAwB;;;AACxB;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAsB;;;AACtB;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA0B;;;AAC1B;EAA2B;;;AAC3B;EAA4B;;;AAC5B;EAA8B;;;AAC9B;EAA4B;;;AAC5B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA2B;;;AAC3B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAA4B;;;AAC5B;EAAmB;;;AACnB;EAAwB;;;AACxB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAwB;;;AACxB;EAA6B;;;AAC7B;EAA4B;;;AAC5B;EAAuB;;;AACvB;EAAoB;;;AACpB;EAAsB;;;AACtB;EAAgB;;;AAChB;EAAuB;;;AACvB;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAoB;;;AACpB;EAAoB;;;AACpB;EAAe;;;AACf;EAAgB;;;AAChB;EAAgB;;;AAChB;EAAoB;;;AACpB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAsB;;;AACtB;EAAkB;;;AAClB;EAAmB;;;AACnB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAe;;;AACf;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAqB;;;AACrB;EAAgB;;;AAChB;EAAmB;;;AACnB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAkB;;;AAClB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAkB;;;AAClB;EAAoB;;;AACpB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAoB;;;AACpB;EAAsB;;;AACtB;AACA;EAAsB;;;AACtB;EAAgB;;;AAChB;EAAiB;;;AACjB;EAAsB;;;AACtB;EAAqB;;;AACrB;EAAiB;;;AACjB;EAAuB;;;AACvB;EAAkB;;;AAClB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAuB;;;AACvB;EAAwB;;;AACxB;EAAwB;;;AACxB;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAA0B;;;AAC1B;EAAyB;;;AACzB;EAA0B;;;AAC1B;EAAwB;;;AACxB;EAA4B;;;AAC5B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAA4B;;;AAC5B;EAA6B;;;AAC7B;EAA6B;;;AAC7B;EAAgC;;;AAChC;EAAkC;;;AAClC;EAA+B;;;AAC/B;EAAiC;;;AACjC;EAAiC;;;AACjC;EAA4B;;;AAE5B;AACA;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AAED;AAAA;AAAA;AAAA;EAIC;;;AClaD;EAGC;EACA;EACA;EACA;EACA;;AAEA;EACE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;;AAGD;EACE;;AAGF;EAAI;;AAGJ;EAAQ;;AAGN;EAAK;;;AChCR;EAIC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;;AAGD;EAEC;;;AAOA;EAEE;;AACA;EAEE;;AAIL;EAEC;;AAED;EACC;;AAGD;EAGG;EACA;;AAEA;EAEE;EAEA;EACA;EACA;;AAIL;EACC;EACA;EACA;EACA;;AACA;EACC;EACA;;AAMD;EAEE;;AAGA;EAEE;EACH;;AAEG;EAEE;EACA;EACA;EACA;EACA;EACA;EACJ;EACA;EACA;;AAEA;EAAM;;AAEN;EAEC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EACC;;AAEG;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACL;;AAGG;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEG;EACA;EACA;EACA;;AAEP;EAEC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EAEA;EACA;EACA;EACA;;AAMG;EAAiC;;AAMjC;EAAM;;AACN;EAAa;;AAEX;EACE;EACA;EACA;;AASZ;EAEE;EACA;EACA;EAEA;EACA;;AACA;EACI;EACA;EACA;;AAIN;EAEE;;AAGF;EAEE;;AAGF;EAEE;EACA;EACA;;AACA;EAEE;EACA;;AAKA;EAEE;;AAEF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAOR;EAEE;EACA;EACA;EACA;;AAGE;EACE;;AAEF;EAEE;;AAGN;EAEE;;AACA;EACC;EACA;;AAOG;EAEE;;AAMJ;EAAQ;;AAEV;EAEE;EACA;EACA;;AAEF;EAEE;EACA;EACA;;AACA;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;;AAIN;EAEE;EACA;;AACA;EAEE;EACA;;AAKN;EAEE;EACA;EACA;EACA;;AACA;EAEE;EACA;;AAIJ;EAEG;;AAIH;EAEE;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACF;;AACE;EACE;EACA;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;AAEF;EAAO;;AACP;EAAQ;;AACR;EAAQ;;AACR;EAAS;;AACR;EACE;;AAED;EAEE;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACJ;;AAGF;EAAU;;AAER;EAEE;EACA;;AACA;EAAM;EAAkB;EAAmB;;AAU/C;EAEE;;AAEF;EAEE;;AACA;EACC;;AAKH;EAEI;IAEG;;EAEH;IACE;;EACA;IACE;;;AAGR;EAEE;IAAkB;;;AAErB;EAEE;IACE;IACA;IACA;;EAIA;IACE;IACD","file":"admin.css"}
...\ No newline at end of file ...\ No newline at end of file
1 .ui-widget-content {
2 border: 1px solid #dddddd;
3 background: #ffffff;
4 color: #333333; }
5
6 .ui-widget-overlay {
7 position: fixed;
8 top: 0;
9 right: 0;
10 width: 100%;
11 height: 100%; }
12
13 .ui-datepicker {
14 width: 17em;
15 padding: .2em .2em 0;
16 display: none; }
17
18 .ui-datepicker .ui-datepicker-header {
19 position: relative;
20 padding: .2em 0; }
21
22 .ui-datepicker .ui-datepicker-prev,
23 .ui-datepicker .ui-datepicker-next {
24 position: absolute;
25 top: 2px;
26 width: 1.8em;
27 height: 1.8em; }
28
29 .ui-datepicker .ui-datepicker-prev-hover,
30 .ui-datepicker .ui-datepicker-next-hover {
31 top: 1px; }
32
33 .ui-datepicker .ui-datepicker-prev {
34 right: 2px; }
35
36 .ui-datepicker .ui-datepicker-next {
37 left: 2px; }
38
39 .ui-datepicker .ui-datepicker-prev-hover {
40 right: 1px; }
41
42 .ui-datepicker .ui-datepicker-next-hover {
43 left: 1px; }
44
45 .ui-datepicker .ui-datepicker-prev span,
46 .ui-datepicker .ui-datepicker-next span {
47 display: block;
48 position: absolute;
49 right: 50%;
50 margin-right: -8px;
51 top: 50%;
52 margin-top: -8px; }
53
54 .ui-datepicker .ui-datepicker-title {
55 margin: 0 2.3em;
56 line-height: 1.8em;
57 text-align: center; }
58
59 .ui-datepicker .ui-datepicker-title select {
60 font-size: 1em;
61 margin: 1px 0; }
62
63 .ui-datepicker select.ui-datepicker-month,
64 .ui-datepicker select.ui-datepicker-year {
65 width: 45%; }
66
67 .ui-datepicker table {
68 width: 100%;
69 font-size: .9em;
70 border-collapse: collapse;
71 margin: 0 0 .4em; }
72
73 .ui-datepicker th {
74 padding: .7em .3em;
75 text-align: center;
76 font-weight: bold;
77 border: 0; }
78
79 .ui-datepicker td {
80 border: 0;
81 padding: 1px; }
82
83 .ui-datepicker td span,
84 .ui-datepicker td a {
85 display: block;
86 padding: .2em;
87 text-align: center;
88 text-decoration: none; }
89
90 .ui-datepicker .ui-datepicker-buttonpane {
91 background-image: none;
92 margin: .7em 0 0 0;
93 padding: 0 .2em;
94 border-right: 0;
95 border-left: 0;
96 border-bottom: 0; }
97
98 .ui-datepicker .ui-datepicker-buttonpane button {
99 float: left;
100 margin: .5em .2em .4em;
101 cursor: pointer;
102 padding: .2em .6em .3em .6em;
103 width: auto;
104 overflow: visible; }
105
106 .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
107 float: right; }
108
109 /* with multiple calendars */
110 .ui-datepicker.ui-datepicker-multi {
111 width: auto; }
112
113 .ui-datepicker-multi .ui-datepicker-group {
114 float: right; }
115
116 .ui-datepicker-multi .ui-datepicker-group table {
117 width: 95%;
118 margin: 0 auto .4em; }
119
120 .ui-datepicker-multi-2 .ui-datepicker-group {
121 width: 50%; }
122
123 .ui-datepicker-multi-3 .ui-datepicker-group {
124 width: 33.3%; }
125
126 .ui-datepicker-multi-4 .ui-datepicker-group {
127 width: 25%; }
128
129 .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
130 .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
131 border-right-width: 0; }
132
133 .ui-datepicker-multi .ui-datepicker-buttonpane {
134 clear: right; }
135
136 .ui-datepicker-row-break {
137 clear: both;
138 width: 100%;
139 font-size: 0; }
140
141 /* RTL support */
142 .ui-datepicker-rtl {
143 direction: ltr; }
144
145 .ui-datepicker-rtl .ui-datepicker-prev {
146 left: 2px;
147 right: auto; }
148
149 .ui-datepicker-rtl .ui-datepicker-next {
150 right: 2px;
151 left: auto; }
152
153 .ui-datepicker-rtl .ui-datepicker-prev:hover {
154 left: 1px;
155 right: auto; }
156
157 .ui-datepicker-rtl .ui-datepicker-next:hover {
158 right: 1px;
159 left: auto; }
160
161 .ui-datepicker-rtl .ui-datepicker-buttonpane {
162 clear: left; }
163
164 .ui-datepicker-rtl .ui-datepicker-buttonpane button {
165 float: right; }
166
167 .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
168 .ui-datepicker-rtl .ui-datepicker-group {
169 float: left; }
170
171 .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
172 .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
173 border-left-width: 0;
174 border-right-width: 1px; }
175
176 /* Icons */
177 .ui-datepicker .ui-icon {
178 display: block;
179 text-indent: -99999px;
180 overflow: hidden;
181 background-repeat: no-repeat;
182 right: .5em;
183 top: .3em; }
184
185 .ui-icon {
186 width: 16px;
187 height: 16px; }
188
189 .ui-icon,
190 .ui-widget-content .ui-icon {
191 background-image: url("../img/ui-icons_444444_256x240.png"); }
192
193 .ui-widget-header .ui-icon {
194 background-image: url("../img/ui-icons_444444_256x240.png"); }
195
196 .ui-state-hover .ui-icon,
197 .ui-state-focus .ui-icon,
198 .ui-button:hover .ui-icon,
199 .ui-button:focus .ui-icon {
200 background-image: url("../img/ui-icons_555555_256x240.png"); }
201
202 .ui-state-active .ui-icon,
203 .ui-button:active .ui-icon {
204 background-image: url("../img/ui-icons_ffffff_256x240.png"); }
205
206 .ui-state-highlight .ui-icon,
207 .ui-button .ui-state-highlight.ui-icon {
208 background-image: url("../img/ui-icons_777620_256x240.png"); }
209
210 /* positioning */
211 .ui-icon-blank {
212 background-position: 16px 16px; }
213
214 .ui-icon-caret-1-n {
215 background-position: 100% 0; }
216
217 .ui-icon-caret-1-ne {
218 background-position: -16px 0; }
219
220 .ui-icon-caret-1-e {
221 background-position: -32px 0; }
222
223 .ui-icon-caret-1-se {
224 background-position: -48px 0; }
225
226 .ui-icon-caret-1-s {
227 background-position: -65px 0; }
228
229 .ui-icon-caret-1-sw {
230 background-position: -80px 0; }
231
232 .ui-icon-caret-1-w {
233 background-position: -96px 0; }
234
235 .ui-icon-caret-1-nw {
236 background-position: -112px 0; }
237
238 .ui-icon-caret-2-n-s {
239 background-position: -128px 0; }
240
241 .ui-icon-caret-2-e-w {
242 background-position: -144px 0; }
243
244 .ui-icon-triangle-1-n {
245 background-position: 100% -16px; }
246
247 .ui-icon-triangle-1-ne {
248 background-position: -16px -16px; }
249
250 .ui-icon-triangle-1-e {
251 background-position: -32px -16px; }
252
253 .ui-icon-triangle-1-se {
254 background-position: -48px -16px; }
255
256 .ui-icon-triangle-1-s {
257 background-position: -65px -16px; }
258
259 .ui-icon-triangle-1-sw {
260 background-position: -80px -16px; }
261
262 .ui-icon-triangle-1-w {
263 background-position: -96px -16px; }
264
265 .ui-icon-triangle-1-nw {
266 background-position: -112px -16px; }
267
268 .ui-icon-triangle-2-n-s {
269 background-position: -128px -16px; }
270
271 .ui-icon-triangle-2-e-w {
272 background-position: -144px -16px; }
273
274 .ui-icon-arrow-1-n {
275 background-position: 100% -32px; }
276
277 .ui-icon-arrow-1-ne {
278 background-position: -16px -32px; }
279
280 .ui-icon-arrow-1-e {
281 background-position: -32px -32px; }
282
283 .ui-icon-arrow-1-se {
284 background-position: -48px -32px; }
285
286 .ui-icon-arrow-1-s {
287 background-position: -65px -32px; }
288
289 .ui-icon-arrow-1-sw {
290 background-position: -80px -32px; }
291
292 .ui-icon-arrow-1-w {
293 background-position: -96px -32px; }
294
295 .ui-icon-arrow-1-nw {
296 background-position: -112px -32px; }
297
298 .ui-icon-arrow-2-n-s {
299 background-position: -128px -32px; }
300
301 .ui-icon-arrow-2-ne-sw {
302 background-position: -144px -32px; }
303
304 .ui-icon-arrow-2-e-w {
305 background-position: -160px -32px; }
306
307 .ui-icon-arrow-2-se-nw {
308 background-position: -176px -32px; }
309
310 .ui-icon-arrowstop-1-n {
311 background-position: -192px -32px; }
312
313 .ui-icon-arrowstop-1-e {
314 background-position: -208px -32px; }
315
316 .ui-icon-arrowstop-1-s {
317 background-position: -224px -32px; }
318
319 .ui-icon-arrowstop-1-w {
320 background-position: -240px -32px; }
321
322 .ui-icon-arrowthick-1-n {
323 background-position: 1px -48px; }
324
325 .ui-icon-arrowthick-1-ne {
326 background-position: -16px -48px; }
327
328 .ui-icon-arrowthick-1-e {
329 background-position: -32px -48px; }
330
331 .ui-icon-arrowthick-1-se {
332 background-position: -48px -48px; }
333
334 .ui-icon-arrowthick-1-s {
335 background-position: -64px -48px; }
336
337 .ui-icon-arrowthick-1-sw {
338 background-position: -80px -48px; }
339
340 .ui-icon-arrowthick-1-w {
341 background-position: -96px -48px; }
342
343 .ui-icon-arrowthick-1-nw {
344 background-position: -112px -48px; }
345
346 .ui-icon-arrowthick-2-n-s {
347 background-position: -128px -48px; }
348
349 .ui-icon-arrowthick-2-ne-sw {
350 background-position: -144px -48px; }
351
352 .ui-icon-arrowthick-2-e-w {
353 background-position: -160px -48px; }
354
355 .ui-icon-arrowthick-2-se-nw {
356 background-position: -176px -48px; }
357
358 .ui-icon-arrowthickstop-1-n {
359 background-position: -192px -48px; }
360
361 .ui-icon-arrowthickstop-1-e {
362 background-position: -208px -48px; }
363
364 .ui-icon-arrowthickstop-1-s {
365 background-position: -224px -48px; }
366
367 .ui-icon-arrowthickstop-1-w {
368 background-position: -240px -48px; }
369
370 .ui-icon-arrowreturnthick-1-w {
371 background-position: 100% -64px; }
372
373 .ui-icon-arrowreturnthick-1-n {
374 background-position: -16px -64px; }
375
376 .ui-icon-arrowreturnthick-1-e {
377 background-position: -32px -64px; }
378
379 .ui-icon-arrowreturnthick-1-s {
380 background-position: -48px -64px; }
381
382 .ui-icon-arrowreturn-1-w {
383 background-position: -64px -64px; }
384
385 .ui-icon-arrowreturn-1-n {
386 background-position: -80px -64px; }
387
388 .ui-icon-arrowreturn-1-e {
389 background-position: -96px -64px; }
390
391 .ui-icon-arrowreturn-1-s {
392 background-position: -112px -64px; }
393
394 .ui-icon-arrowrefresh-1-w {
395 background-position: -128px -64px; }
396
397 .ui-icon-arrowrefresh-1-n {
398 background-position: -144px -64px; }
399
400 .ui-icon-arrowrefresh-1-e {
401 background-position: -160px -64px; }
402
403 .ui-icon-arrowrefresh-1-s {
404 background-position: -176px -64px; }
405
406 .ui-icon-arrow-4 {
407 background-position: 100% -80px; }
408
409 .ui-icon-arrow-4-diag {
410 background-position: -16px -80px; }
411
412 .ui-icon-extlink {
413 background-position: -32px -80px; }
414
415 .ui-icon-newwin {
416 background-position: -48px -80px; }
417
418 .ui-icon-refresh {
419 background-position: -64px -80px; }
420
421 .ui-icon-shuffle {
422 background-position: -80px -80px; }
423
424 .ui-icon-transfer-e-w {
425 background-position: -96px -80px; }
426
427 .ui-icon-transferthick-e-w {
428 background-position: -112px -80px; }
429
430 .ui-icon-folder-collapsed {
431 background-position: 100% -96px; }
432
433 .ui-icon-folder-open {
434 background-position: -16px -96px; }
435
436 .ui-icon-document {
437 background-position: -32px -96px; }
438
439 .ui-icon-document-b {
440 background-position: -48px -96px; }
441
442 .ui-icon-note {
443 background-position: -64px -96px; }
444
445 .ui-icon-mail-closed {
446 background-position: -80px -96px; }
447
448 .ui-icon-mail-open {
449 background-position: -96px -96px; }
450
451 .ui-icon-suitcase {
452 background-position: -112px -96px; }
453
454 .ui-icon-comment {
455 background-position: -128px -96px; }
456
457 .ui-icon-person {
458 background-position: -144px -96px; }
459
460 .ui-icon-print {
461 background-position: -160px -96px; }
462
463 .ui-icon-trash {
464 background-position: -176px -96px; }
465
466 .ui-icon-locked {
467 background-position: -192px -96px; }
468
469 .ui-icon-unlocked {
470 background-position: -208px -96px; }
471
472 .ui-icon-bookmark {
473 background-position: -224px -96px; }
474
475 .ui-icon-tag {
476 background-position: -240px -96px; }
477
478 .ui-icon-home {
479 background-position: 100% -112px; }
480
481 .ui-icon-flag {
482 background-position: -16px -112px; }
483
484 .ui-icon-calendar {
485 background-position: -32px -112px; }
486
487 .ui-icon-cart {
488 background-position: -48px -112px; }
489
490 .ui-icon-pencil {
491 background-position: -64px -112px; }
492
493 .ui-icon-clock {
494 background-position: -80px -112px; }
495
496 .ui-icon-disk {
497 background-position: -96px -112px; }
498
499 .ui-icon-calculator {
500 background-position: -112px -112px; }
501
502 .ui-icon-zoomin {
503 background-position: -128px -112px; }
504
505 .ui-icon-zoomout {
506 background-position: -144px -112px; }
507
508 .ui-icon-search {
509 background-position: -160px -112px; }
510
511 .ui-icon-wrench {
512 background-position: -176px -112px; }
513
514 .ui-icon-gear {
515 background-position: -192px -112px; }
516
517 .ui-icon-heart {
518 background-position: -208px -112px; }
519
520 .ui-icon-star {
521 background-position: -224px -112px; }
522
523 .ui-icon-link {
524 background-position: -240px -112px; }
525
526 .ui-icon-cancel {
527 background-position: 100% -128px; }
528
529 .ui-icon-plus {
530 background-position: -16px -128px; }
531
532 .ui-icon-plusthick {
533 background-position: -32px -128px; }
534
535 .ui-icon-minus {
536 background-position: -48px -128px; }
537
538 .ui-icon-minusthick {
539 background-position: -64px -128px; }
540
541 .ui-icon-close {
542 background-position: -80px -128px; }
543
544 .ui-icon-closethick {
545 background-position: -96px -128px; }
546
547 .ui-icon-key {
548 background-position: -112px -128px; }
549
550 .ui-icon-lightbulb {
551 background-position: -128px -128px; }
552
553 .ui-icon-scissors {
554 background-position: -144px -128px; }
555
556 .ui-icon-clipboard {
557 background-position: -160px -128px; }
558
559 .ui-icon-copy {
560 background-position: -176px -128px; }
561
562 .ui-icon-contact {
563 background-position: -192px -128px; }
564
565 .ui-icon-image {
566 background-position: -208px -128px; }
567
568 .ui-icon-video {
569 background-position: -224px -128px; }
570
571 .ui-icon-script {
572 background-position: -240px -128px; }
573
574 .ui-icon-alert {
575 background-position: 100% -144px; }
576
577 .ui-icon-info {
578 background-position: -16px -144px; }
579
580 .ui-icon-notice {
581 background-position: -32px -144px; }
582
583 .ui-icon-help {
584 background-position: -48px -144px; }
585
586 .ui-icon-check {
587 background-position: -64px -144px; }
588
589 .ui-icon-bullet {
590 background-position: -80px -144px; }
591
592 .ui-icon-radio-on {
593 background-position: -96px -144px; }
594
595 .ui-icon-radio-off {
596 background-position: -112px -144px; }
597
598 .ui-icon-pin-w {
599 background-position: -128px -144px; }
600
601 .ui-icon-pin-s {
602 background-position: -144px -144px; }
603
604 .ui-icon-play {
605 background-position: 100% -160px; }
606
607 .ui-icon-pause {
608 background-position: -16px -160px; }
609
610 .ui-icon-seek-next {
611 background-position: -32px -160px; }
612
613 .ui-icon-seek-prev {
614 background-position: -48px -160px; }
615
616 .ui-icon-seek-end {
617 background-position: -64px -160px; }
618
619 .ui-icon-seek-start {
620 background-position: -80px -160px; }
621
622 /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
623 .ui-icon-seek-first {
624 background-position: -80px -160px; }
625
626 .ui-icon-stop {
627 background-position: -96px -160px; }
628
629 .ui-icon-eject {
630 background-position: -112px -160px; }
631
632 .ui-icon-volume-off {
633 background-position: -128px -160px; }
634
635 .ui-icon-volume-on {
636 background-position: -144px -160px; }
637
638 .ui-icon-power {
639 background-position: 100% -176px; }
640
641 .ui-icon-signal-diag {
642 background-position: -16px -176px; }
643
644 .ui-icon-signal {
645 background-position: -32px -176px; }
646
647 .ui-icon-battery-0 {
648 background-position: -48px -176px; }
649
650 .ui-icon-battery-1 {
651 background-position: -64px -176px; }
652
653 .ui-icon-battery-2 {
654 background-position: -80px -176px; }
655
656 .ui-icon-battery-3 {
657 background-position: -96px -176px; }
658
659 .ui-icon-circle-plus {
660 background-position: 100% -192px; }
661
662 .ui-icon-circle-minus {
663 background-position: -16px -192px; }
664
665 .ui-icon-circle-close {
666 background-position: -32px -192px; }
667
668 .ui-icon-circle-triangle-e {
669 background-position: -48px -192px; }
670
671 .ui-icon-circle-triangle-s {
672 background-position: -64px -192px; }
673
674 .ui-icon-circle-triangle-w {
675 background-position: -80px -192px; }
676
677 .ui-icon-circle-triangle-n {
678 background-position: -96px -192px; }
679
680 .ui-icon-circle-arrow-e {
681 background-position: -112px -192px; }
682
683 .ui-icon-circle-arrow-s {
684 background-position: -128px -192px; }
685
686 .ui-icon-circle-arrow-w {
687 background-position: -144px -192px; }
688
689 .ui-icon-circle-arrow-n {
690 background-position: -160px -192px; }
691
692 .ui-icon-circle-zoomin {
693 background-position: -176px -192px; }
694
695 .ui-icon-circle-zoomout {
696 background-position: -192px -192px; }
697
698 .ui-icon-circle-check {
699 background-position: -208px -192px; }
700
701 .ui-icon-circlesmall-plus {
702 background-position: 100% -208px; }
703
704 .ui-icon-circlesmall-minus {
705 background-position: -16px -208px; }
706
707 .ui-icon-circlesmall-close {
708 background-position: -32px -208px; }
709
710 .ui-icon-squaresmall-plus {
711 background-position: -48px -208px; }
712
713 .ui-icon-squaresmall-minus {
714 background-position: -64px -208px; }
715
716 .ui-icon-squaresmall-close {
717 background-position: -80px -208px; }
718
719 .ui-icon-grip-dotted-vertical {
720 background-position: 100% -224px; }
721
722 .ui-icon-grip-dotted-horizontal {
723 background-position: -16px -224px; }
724
725 .ui-icon-grip-solid-vertical {
726 background-position: -32px -224px; }
727
728 .ui-icon-grip-solid-horizontal {
729 background-position: -48px -224px; }
730
731 .ui-icon-gripsmall-diagonal-se {
732 background-position: -64px -224px; }
733
734 .ui-icon-grip-diagonal-se {
735 background-position: -80px -224px; }
736
737 /* Corner radius */
738 .ui-corner-all,
739 .ui-corner-top,
740 .ui-corner-left,
741 .ui-corner-tl {
742 border-top-right-radius: 3px; }
743
744 .ui-corner-all,
745 .ui-corner-top,
746 .ui-corner-right,
747 .ui-corner-tr {
748 border-top-left-radius: 3px; }
749
750 .ui-corner-all,
751 .ui-corner-bottom,
752 .ui-corner-left,
753 .ui-corner-bl {
754 border-bottom-right-radius: 3px; }
755
756 .ui-corner-all,
757 .ui-corner-bottom,
758 .ui-corner-right,
759 .ui-corner-br {
760 border-bottom-left-radius: 3px; }
761
762 .emr_upload_form .wrapper {
763 margin: 15px 0;
764 padding: 18px;
765 border: 1px solid #ccc; }
766 .emr_upload_form .wrapper .section-header {
767 font-size: 18px;
768 border-bottom: 1px solid #ccc;
769 padding: 6px 0;
770 margin: 0 0 15px 0; }
771 .emr_upload_form .image_chooser.wrapper {
772 min-height: 350px; }
773 .emr_upload_form .image_chooser.wrapper .image_previews {
774 margin: 15px 0; }
775 .emr_upload_form .option-flex-wrapper {
776 display: flex; }
777 .emr_upload_form .replace_type.wrapper {
778 flex: 1;
779 border: 1px solid #ccc;
780 margin: 15px 0; }
781 .emr_upload_form .replace_type.wrapper label {
782 font-size: 1.2em; }
783 .emr_upload_form .options.wrapper {
784 flex: 1;
785 border: 1px solid #ccc;
786 padding: 15px;
787 margin: 15px 35px 15px 0; }
788 .emr_upload_form .options.wrapper .custom_date .emr_datepicker {
789 width: 150px; }
790 .emr_upload_form .options.wrapper .custom_date .emr_hour, .emr_upload_form .options.wrapper .custom_date .emr_minute {
791 width: 45px; }
792 .emr_upload_form .options.wrapper ul li input {
793 margin-left: 8px; }
794 .emr_upload_form .options.wrapper .option label {
795 vertical-align: top; }
796 .emr_upload_form .options.wrapper .small {
797 font-size: 10px;
798 vertical-align: top;
799 margin-right: 8px; }
800 .emr_upload_form .options.wrapper .custom_date {
801 margin: 8px 25px 0 0;
802 visibility: hidden;
803 opacity: 0; }
804 .emr_upload_form .options.wrapper .custom_date span.field-title {
805 display: inline-block;
806 margin-bottom: 4px;
807 color: #444;
808 font-size: 12px;
809 width: 100%;
810 text-align: right;
811 vertical-align: middle;
812 line-height: 26px; }
813 .emr_upload_form .options.wrapper .custom_date span.field-title::before {
814 font-size: 20px;
815 vertical-align: top;
816 margin-left: 4px; }
817 .emr_upload_form .form_controls.wrapper {
818 clear: both;
819 margin: 8px 0 15px 0;
820 border: 0;
821 padding: 0; }
822 .emr_upload_form .form_controls.wrapper .button {
823 padding-right: 20px;
824 padding-left: 20px; }
825 .emr_upload_form .shortpixel-notice {
826 background: #fff;
827 width: 250px;
828 min-height: 270px;
829 border: 1px solid #ccc;
830 padding: 15px;
831 margin: 0 0 10px;
832 float: left; }
833 .emr_upload_form .shortpixel-notice h3 {
834 line-height: 1.3em; }
1 /* Styling for the edit attachment screen */
2 #emr-replace-box .previewwrapper, #emr-showthumbs-box .previewwrapper {
3 display: inline-block;
4 position: relative;
5 clear: both;
6 margin: 3px 0;
7 }
8 #emr-replace-box .previewwrapper img, #emr-showthumbs-box .previewwrapper img {
9 max-width: 100%;
10 }
11 #emr-replace-box .previewwrapper span.label, #emr-showthumbs-box .previewwrapper span.label {
12 font-size: 14px;
13 color: #fff;
14 position: absolute;
15 line-height: 16px;
16 margin-top: -8px;
17 top: 50%;
18 left: 0;
19 right: 0;
20 background: rgba(0, 0, 0, 0.5);
21 text-align: center;
22 padding: 4px 0;
23 }
24
25 /*# sourceMappingURL=edit_attachment.css.map */
1 {"version":3,"sourceRoot":"","sources":["../scss/edit_attachment.scss"],"names":[],"mappings":"AACA;AAGE;EAEE;EACA;EACA;EACA;;AAEA;EAAM;;AACN;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EAAS;EACT;EACA;EACA","file":"edit_attachment.css"}
...\ No newline at end of file ...\ No newline at end of file
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 */
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"}
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2 /**
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.1.4
7 * Author: ShortPixel
8 * Author URI: https://shortpixel.com
9 * GitHub Plugin URI: https://github.com/short-pixel-optimizer/enable-media-replace
10 * Text Domain: enable-media-replace
11 * Domain Path: /languages
12 * Dual licensed under the MIT and GPL licenses:
13 * License URI: http://www.opensource.org/licenses/mit-license.php
14 * License URI: http://www.gnu.org/licenses/gpl.html
15 */
16
17 /**
18 * Main Plugin file
19 * Set action hooks and add shortcode
20 *
21 * @author ShortPixel <https://shortpixel.com>
22 * @copyright ShortPixel 2018-2020
23 * @package WordPress
24 * @subpackage enable-media-replace
25 *
26 */
27
28 define( 'EMR_VERSION', '4.1.4' );
29
30 if ( ! defined( 'ABSPATH' ) ) {
31 exit; // Exit if accessed directly.
32 }
33
34 /* Not sure why we define this?
35 if(!defined("S3_UPLOADS_AUTOENABLE")) {
36 define('S3_UPLOADS_AUTOENABLE', true);
37 } */
38
39 if ( ! defined( 'EMR_ROOT_FILE' ) ) {
40 define( 'EMR_ROOT_FILE', __FILE__ );
41 }
42
43 if ( ! defined( 'SHORTPIXEL_AFFILIATE_CODE' ) ) {
44 define( 'SHORTPIXEL_AFFILIATE_CODE', 'VKG6LYN28044' );
45 }
46
47 /** Usage:
48 * Define in wp-config.php
49 * // User must have this capability to replace all
50 * define('EMR_CAPABILITY' ,'edit_upload_all' );
51 * // User must have first capability to replace all OR second capability to replace only own files
52 * define('EMR_CAPABILITY' ,array('edit_upload_all', 'edit_upload_user') );
53 *
54 *
55 **/
56 if ( ! defined( 'EMR_CAPABILITY' ) ) {
57 define( 'EMR_CAPABILITY', false );
58 }
59
60 /* if (! defined('EMR_CAPABILITY_USERONLY'))
61 define('EMR_CAPABILITY_USERONLY', false); */
62
63 $plugin_path = plugin_dir_path( EMR_ROOT_FILE );
64
65 require_once( $plugin_path . 'build/shortpixel/autoload.php' );
66 require_once( $plugin_path . 'classes/compat.php' );
67 require_once( $plugin_path . 'classes/functions.php' );
68 //require_once( $plugin_path . 'classes/replacer.php' );
69 require_once( $plugin_path . 'classes/uihelper.php' );
70 //require_once( $plugin_path . 'classes/file.php' );
71 require_once( $plugin_path . 'classes/cache.php' );
72 require_once( $plugin_path . 'classes/api.php' );
73 require_once( $plugin_path . 'classes/ajax.php' );
74 require_once( $plugin_path . 'classes/emr-plugin.php' );
75 require_once( $plugin_path . 'classes/installHelper.php' );
76
77 // @todo Needs replacing with PSR-4
78 require_once( $plugin_path . 'classes/Controller/ReplaceController.php');
79 require_once( $plugin_path . 'classes/Controller/RemoteNoticeController.php');
80
81 require_once( $plugin_path . 'classes/ViewController.php');
82 require_once( $plugin_path . 'classes/ViewController/UploadViewController.php');
83 require_once( $plugin_path . 'classes/ViewController/ReplaceViewController.php');
84 require_once( $plugin_path . 'classes/ViewController/RemoveBackgroundViewController.php');
85
86 require_once( $plugin_path . 'classes/externals.php' );
87 require_once( $plugin_path . 'classes/external/elementor.php' );
88 require_once( $plugin_path . 'classes/external/wpbakery.php' );
89 require_once( $plugin_path . 'classes/external/upsell_installer.php' );
90 require_once( $plugin_path . 'classes/external/siteorigin.php' );
91 require_once( $plugin_path . 'classes/external/wp-offload.php' );
92
93 require_once( $plugin_path . 'thumbnail_updater.php' );
94
95 function emr()
96 {
97 return EnableMediaReplace\EnableMediaReplacePlugin::get();
98 }
99 emr(); // runtime.
100
101 //register_uninstall_hook( __FILE__, '\EnableMediaReplace\emr_uninstall' );
102 register_deactivation_hook( __FILE__, array('\EnableMediaReplace\InstallHelper','deactivatePlugin') );
103 register_uninstall_hook(__FILE__, array('\EnableMediaReplace\InstallHelper','uninstallPlugin') );
1 <?php // Silence is golden
...\ No newline at end of file ...\ No newline at end of file
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4 <svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="51.877094mm"
13 height="53.161201mm"
14 viewBox="0 0 51.877094 53.161201"
15 version="1.1"
16 id="svg985"
17 inkscape:version="0.92.3 (2405546, 2018-03-11)"
18 sodipodi:docname="SHORTPIXEL Robot Regular.svg">
19 <defs
20 id="defs979" />
21 <sodipodi:namedview
22 id="base"
23 pagecolor="#ffffff"
24 bordercolor="#666666"
25 borderopacity="1.0"
26 inkscape:pageopacity="0.0"
27 inkscape:pageshadow="2"
28 inkscape:zoom="0.35"
29 inkscape:cx="-407.38638"
30 inkscape:cy="99.20227"
31 inkscape:document-units="mm"
32 inkscape:current-layer="layer1"
33 showgrid="false"
34 fit-margin-top="0"
35 fit-margin-left="0"
36 fit-margin-right="0"
37 fit-margin-bottom="0"
38 inkscape:window-width="1853"
39 inkscape:window-height="1025"
40 inkscape:window-x="67"
41 inkscape:window-y="27"
42 inkscape:window-maximized="1" />
43 <metadata
44 id="metadata982">
45 <rdf:RDF>
46 <cc:Work
47 rdf:about="">
48 <dc:format>image/svg+xml</dc:format>
49 <dc:type
50 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
51 <dc:title></dc:title>
52 </cc:Work>
53 </rdf:RDF>
54 </metadata>
55 <g
56 inkscape:label="Layer 1"
57 inkscape:groupmode="layer"
58 id="layer1"
59 transform="translate(-79.061456,-121.9194)">
60 <g
61 id="g6698"
62 transform="matrix(0.72173333,0,0,0.72173333,220.51847,157.29398)">
63 <g
64 transform="matrix(0.35277777,0,0,-0.35277777,-171.70416,8.601389)"
65 id="g1336">
66 <path
67 inkscape:connector-curvature="0"
68 id="path1338"
69 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
70 d="m 0,0 c 0,0 -4.484,-4.226 -4.484,-14.851 0,-10.625 16.769,-30.625 56.384,-30.625 39.616,0 53.116,13.875 53.116,25.25 0,11.375 -2.792,12.708 -2.792,12.708 z" />
71 </g>
72 <g
73 transform="matrix(0.35277777,0,0,-0.35277777,-170.27428,8.482539)"
74 id="g1340">
75 <path
76 inkscape:connector-curvature="0"
77 id="path1342"
78 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
79 d="m 0,0 -4.086,-0.337 c 0,0 -1.243,-11.309 -1.243,-14.687 0,-7.673 15.443,-27.664 52.565,-27.664 31.872,0 50.935,9.953 50.935,22.099 0,3.253 0,13.985 0,13.985 l -7.591,3.897 z" />
80 </g>
81 <g
82 transform="matrix(0.35277777,0,0,-0.35277777,-170.80208,5.020899)"
83 id="g1344">
84 <path
85 inkscape:connector-curvature="0"
86 id="path1346"
87 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
88 d="m 0,0 c 0,0 -2.59,-1.499 -2.59,-12.169 0,-7.581 15.257,-27.331 51.933,-27.331 31.49,0 50.324,9.833 50.324,21.833 0,14.5 -7.5,17.667 -7.5,17.667 z" />
89 </g>
90 <g
91 transform="matrix(0.35277777,0,0,-0.35277777,-177.76203,-43.276561)"
92 id="g1348">
93 <path
94 inkscape:connector-curvature="0"
95 id="path1350"
96 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
97 d="m 0,0 -28.562,-27.74 v -95.166 l 29.624,-11.75 z" />
98 </g>
99 <g
100 transform="matrix(0.35277777,0,0,-0.35277777,-157.45526,7.534449)"
101 id="g1352">
102 <path
103 inkscape:connector-curvature="0"
104 id="path1354"
105 style="fill:#d7292a;fill-opacity:1;fill-rule:nonzero;stroke:none"
106 d="m 0,0 0.574,7.125 h -38.407 c 0,0 -2.591,-1.499 -2.591,-12.169 0,-2.132 1.207,-5.227 3.758,-8.575 z" />
107 </g>
108 <g
109 transform="matrix(0.35277777,0,0,-0.35277777,-148.37123,8.601389)"
110 id="g1356">
111 <path
112 inkscape:connector-curvature="0"
113 id="path1358"
114 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none"
115 d="m 0,0 c 0,-3.266 -6.375,-5.913 -14.24,-5.913 -7.865,0 -14.24,2.647 -14.24,5.913 0,3.267 6.375,5.913 14.24,5.913 C -6.375,5.913 0,3.267 0,0" />
116 </g>
117 <path
118 inkscape:connector-curvature="0"
119 id="path1360"
120 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
121 d="m -148.37123,5.791719 h -10.04711 v 2.80988 h 10.04711 z" />
122 <g
123 transform="matrix(0.35277777,0,0,-0.35277777,-148.37123,5.791579)"
124 id="g1362">
125 <path
126 inkscape:connector-curvature="0"
127 id="path1364"
128 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none"
129 d="m 0,0 c 0,-3.267 -6.375,-5.913 -14.24,-5.913 -7.865,0 -14.24,2.646 -14.24,5.913 0,3.266 6.375,5.913 14.24,5.913 C -6.375,5.913 0,3.266 0,0" />
130 </g>
131 <path
132 inkscape:connector-curvature="0"
133 id="path1366"
134 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
135 d="m -148.37123,3.903659 h -10.04711 v 1.73461 h 10.04711 z" />
136 <g
137 transform="matrix(0.35277777,0,0,-0.35277777,-150.92887,-44.367981)"
138 id="g1368">
139 <path
140 inkscape:connector-curvature="0"
141 id="path1370"
142 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
143 d="m 0,0 h -68 c -6.6,0 -12,-5.4 -12,-12 v -116 c 0,-6.6 5.4,-12 12,-12 H 0 Z" />
144 </g>
145 <g
146 transform="matrix(0.35277777,0,0,-0.35277777,-128.35109,-44.367981)"
147 id="g1372">
148 <path
149 inkscape:connector-curvature="0"
150 id="path1374"
151 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
152 d="M 0,0 H -64 V -140 H 0 c 6.6,0 12,5.4 12,12 V -12 C 12,-5.4 6.6,0 0,0" />
153 </g>
154 <g
155 transform="matrix(0.35277777,0,0,-0.35277777,-146.69554,-48.380831)"
156 id="g1376">
157 <path
158 inkscape:connector-curvature="0"
159 id="path1378"
160 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
161 d="m 0,0 v -5.5 l 32.648,-4.046 0.227,8.171 z" />
162 </g>
163 <g
164 transform="matrix(0.35277777,0,0,-0.35277777,-153.48651,-47.807571)"
165 id="g1380">
166 <path
167 inkscape:connector-curvature="0"
168 id="path1382"
169 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
170 d="M 0,0 -41.083,3.418 -41.5,-6.666 -0.375,-6.5 Z" />
171 </g>
172 <g
173 transform="matrix(0.35277777,0,0,-0.35277777,-181.97331,-17.909651)"
174 id="g1384">
175 <path
176 inkscape:connector-curvature="0"
177 id="path1386"
178 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
179 d="m 0,0 c 0,-15.33 -7.219,-27.758 -16.125,-27.758 -8.906,0 -16.125,12.428 -16.125,27.758 0,15.33 7.219,27.758 16.125,27.758 C -7.219,27.758 0,15.33 0,0" />
180 </g>
181 <g
182 transform="matrix(0.35277777,0,0,-0.35277777,-187.5466,-26.596801)"
183 id="g1388">
184 <path
185 inkscape:connector-curvature="0"
186 id="path1390"
187 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
188 d="m 0,0 c -0.108,0 -5.711,0.023 -5.82,0.023 -6.93,0 -12.548,-11.037 -12.548,-24.636 0,-13.6 5.618,-24.631 12.548,-24.631 0.109,0 5.712,-0.012 5.82,-0.012 6.931,0 12.548,11.027 12.548,24.628 C 12.548,-11.027 6.931,0 0,0" />
189 </g>
190 <g
191 transform="matrix(0.35277777,0,0,-0.35277777,-185.23651,-17.909651)"
192 id="g1392">
193 <path
194 inkscape:connector-curvature="0"
195 id="path1394"
196 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
197 d="m 0,0 c 0,-13.6 -5.578,-24.625 -12.458,-24.625 -6.881,0 -12.459,11.025 -12.459,24.625 0,13.6 5.578,24.625 12.459,24.625 C -5.578,24.625 0,13.6 0,0" />
198 </g>
199 <g
200 transform="matrix(0.35277777,0,0,-0.35277777,-189.55803,-18.130141)"
201 id="g1396">
202 <path
203 inkscape:connector-curvature="0"
204 id="path1398"
205 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
206 d="m 0,0 c 0,-3.383 -1.679,-6.125 -3.75,-6.125 -2.071,0 -3.75,2.742 -3.75,6.125 0,3.383 1.679,6.125 3.75,6.125 C -1.679,6.125 0,3.383 0,0" />
207 </g>
208 <g
209 transform="matrix(0.35277777,0,0,-0.35277777,-191.45422,-20.290901)"
210 id="g1400">
211 <path
212 inkscape:connector-curvature="0"
213 id="path1402"
214 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
215 d="m 0,0 h -10.875 c -1.104,0 -2,-2.742 -2,-6.125 0,-3.383 0.896,-6.125 2,-6.125 H 0 c 1.104,0 2,2.742 2,6.125 C 2,-2.742 1.104,0 0,0" />
216 </g>
217 <g
218 transform="matrix(0.35277777,0,0,-0.35277777,-139.99276,-31.551531)"
219 id="g1404">
220 <path
221 inkscape:connector-curvature="0"
222 id="path1406"
223 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
224 d="m 0,0 c -1.042,0.008 -4.266,0.84 -4.349,0.832 -12.234,-1.086 -20.401,-9.461 -20.401,-20.127 0,-10.666 8.908,-19.326 19.896,-19.326 0.084,0 3.812,-0.002 4.854,-0.002 z" />
225 </g>
226 <g
227 transform="matrix(0.35277777,0,0,-0.35277777,-133.23124,-24.943161)"
228 id="g1408">
229 <path
230 inkscape:connector-curvature="0"
231 id="path1410"
232 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
233 d="m 0,0 c 0,-10.988 -8.908,-19.896 -19.896,-19.896 -10.989,0 -19.897,8.908 -19.897,19.896 0,10.988 8.908,19.896 19.897,19.896 C -8.908,19.896 0,10.988 0,0" />
234 </g>
235 <g
236 transform="matrix(0.35277777,0,0,-0.35277777,-135.0387,-25.075451)"
237 id="g1412">
238 <path
239 inkscape:connector-curvature="0"
240 id="path1414"
241 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
242 d="m 0,0 c 0,-7.951 -6.446,-14.398 -14.398,-14.398 -7.952,0 -14.398,6.447 -14.398,14.398 0,7.951 6.446,14.398 14.398,14.398 C -6.446,14.398 0,7.951 0,0" />
243 </g>
244 <g
245 transform="matrix(0.35277777,0,0,-0.35277777,-137.49128,-25.031351)"
246 id="g1416">
247 <path
248 inkscape:connector-curvature="0"
249 id="path1418"
250 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
251 d="m 0,0 c 0,-4.182 -3.39,-7.57 -7.571,-7.57 -4.181,0 -7.571,3.388 -7.571,7.57 0,4.182 3.39,7.57 7.571,7.57 C -3.39,7.57 0,4.182 0,0" />
252 </g>
253 <g
254 transform="matrix(0.35277777,0,0,-0.35277777,-159.39554,-31.551531)"
255 id="g1420">
256 <path
257 inkscape:connector-curvature="0"
258 id="path1422"
259 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
260 d="m 0,0 c -1.042,0.008 -4.266,0.84 -4.349,0.832 -12.234,-1.086 -20.401,-9.461 -20.401,-20.127 0,-10.666 8.908,-19.326 19.896,-19.326 0.084,0 3.812,-0.002 4.854,-0.002 z" />
261 </g>
262 <g
263 transform="matrix(0.35277777,0,0,-0.35277777,-152.63402,-24.943161)"
264 id="g1424">
265 <path
266 inkscape:connector-curvature="0"
267 id="path1426"
268 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
269 d="m 0,0 c 0,-10.988 -8.908,-19.896 -19.896,-19.896 -10.989,0 -19.897,8.908 -19.897,19.896 0,10.988 8.908,19.896 19.897,19.896 C -8.908,19.896 0,10.988 0,0" />
270 </g>
271 <g
272 transform="matrix(0.35277777,0,0,-0.35277777,-154.44148,-25.075451)"
273 id="g1428">
274 <path
275 inkscape:connector-curvature="0"
276 id="path1430"
277 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
278 d="m 0,0 c 0,-7.951 -6.446,-14.398 -14.398,-14.398 -7.952,0 -14.398,6.447 -14.398,14.398 0,7.951 6.446,14.398 14.398,14.398 C -6.446,14.398 0,7.951 0,0" />
279 </g>
280 <g
281 transform="matrix(0.35277777,0,0,-0.35277777,-156.89406,-25.031351)"
282 id="g1432">
283 <path
284 inkscape:connector-curvature="0"
285 id="path1434"
286 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
287 d="m 0,0 c 0,-4.182 -3.39,-7.57 -7.571,-7.57 -4.181,0 -7.571,3.388 -7.571,7.57 0,4.182 3.39,7.57 7.571,7.57 C -3.39,7.57 0,4.182 0,0" />
288 </g>
289 <g
290 transform="matrix(0.35277777,0,0,-0.35277777,-156.80498,-7.286001)"
291 id="g1436">
292 <path
293 inkscape:connector-curvature="0"
294 id="path1438"
295 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
296 d="m 0,0 c 0.07,-4.725 -3.702,-8.61 -8.426,-8.68 -4.724,-0.07 -8.609,3.702 -8.679,8.427 -0.07,4.723 3.702,8.608 8.426,8.678 C -3.956,8.495 -0.07,4.724 0,0" />
297 </g>
298 <g
299 transform="matrix(0.35277777,0,0,-0.35277777,-138.27367,-7.560241)"
300 id="g1440">
301 <path
302 inkscape:connector-curvature="0"
303 id="path1442"
304 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
305 d="m 0,0 c 0.07,-4.724 -3.702,-8.61 -8.426,-8.68 -4.724,-0.07 -8.609,3.702 -8.679,8.427 -0.07,4.724 3.702,8.609 8.425,8.679 C -3.956,8.495 -0.07,4.724 0,0" />
306 </g>
307 <g
308 transform="matrix(0.35277777,0,0,-0.35277777,-139.38005,-5.182771)"
309 id="g1444">
310 <path
311 inkscape:connector-curvature="0"
312 id="path1446"
313 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
314 d="m 0,0 c 0,0 -11.162,-12.342 -32.756,-12.661 -18.804,-0.278 -31.421,12.65 -31.421,12.65 l 12.321,11.853 c 0,0 9.5,-8.029 19.708,-7.822 13.944,0.28 20.337,8.343 20.337,8.343 z" />
315 </g>
316 <g
317 transform="matrix(0.35277777,0,0,-0.35277777,-156.51906,-8.281291)"
318 id="g1448">
319 <path
320 inkscape:connector-curvature="0"
321 id="path1450"
322 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
323 d="m 0,0 c 0,-4.788 -3.881,-8.67 -8.668,-8.67 -4.788,0 -8.669,3.882 -8.669,8.67 0,4.787 3.881,8.668 8.669,8.668 C -3.881,8.668 0,4.787 0,0" />
324 </g>
325 <g
326 transform="matrix(0.35277777,0,0,-0.35277777,-137.73674,-8.281291)"
327 id="g1452">
328 <path
329 inkscape:connector-curvature="0"
330 id="path1454"
331 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
332 d="m 0,0 c 0,-4.788 -3.881,-8.67 -8.668,-8.67 -4.789,0 -8.669,3.882 -8.669,8.67 0,4.787 3.88,8.668 8.669,8.668 C -3.881,8.668 0,4.787 0,0" />
333 </g>
334 <g
335 transform="matrix(0.35277777,0,0,-0.35277777,-138.89343,-5.888321)"
336 id="g1456">
337 <path
338 inkscape:connector-curvature="0"
339 id="path1458"
340 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
341 d="m 0,0 c 0,0 -11.496,-12.338 -33.383,-12.338 -19.058,0 -31.65,13.291 -31.65,13.291 l 12.663,11.824 c 0,0 9.507,-8.277 19.853,-8.222 14.135,0.076 20.734,8.15 20.734,8.15 z" />
342 </g>
343 <path
344 inkscape:connector-curvature="0"
345 id="path1460"
346 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
347 d="m -149.10854,-3.483861 h -1.47496 v -1.88701 h 1.47496 z" />
348 <g
349 transform="matrix(0.35277777,0,0,-0.35277777,-151.67284,-3.580141)"
350 id="g1462">
351 <path
352 inkscape:connector-curvature="0"
353 id="path1464"
354 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
355 d="M 0,0 -4.076,0.926 -2.893,6.143 1.185,5.217 Z" />
356 </g>
357 <g
358 transform="matrix(0.35277777,0,0,-0.35277777,-154.05857,-4.178521)"
359 id="g1466">
360 <path
361 inkscape:connector-curvature="0"
362 id="path1468"
363 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
364 d="m 0,0 -3.894,1.521 1.946,4.982 3.894,-1.52 z" />
365 </g>
366 <g
367 transform="matrix(0.35277777,0,0,-0.35277777,-156.28908,-5.076651)"
368 id="g1470">
369 <path
370 inkscape:connector-curvature="0"
371 id="path1472"
372 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
373 d="M 0,0 -3.727,1.894 -1.304,6.662 2.423,4.769 Z" />
374 </g>
375 <g
376 transform="matrix(0.35277777,0,0,-0.35277777,-158.47725,-6.252141)"
377 id="g1474">
378 <path
379 inkscape:connector-curvature="0"
380 id="path1476"
381 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
382 d="M 0,0 -3.383,2.456 -0.241,6.783 3.142,4.327 Z" />
383 </g>
384 <path
385 inkscape:connector-curvature="0"
386 id="path1478"
387 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
388 d="m -150.5835,-3.483861 h 1.47496 v -1.88701 h -1.47496 z" />
389 <g
390 transform="matrix(0.35277777,0,0,-0.35277777,-148.01898,-3.580141)"
391 id="g1480">
392 <path
393 inkscape:connector-curvature="0"
394 id="path1482"
395 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
396 d="M 0,0 4.076,0.926 2.893,6.143 -1.185,5.217 Z" />
397 </g>
398 <g
399 transform="matrix(0.35277777,0,0,-0.35277777,-145.63325,-4.178521)"
400 id="g1484">
401 <path
402 inkscape:connector-curvature="0"
403 id="path1486"
404 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
405 d="M 0,0 3.894,1.521 1.948,6.503 -1.946,4.983 Z" />
406 </g>
407 <g
408 transform="matrix(0.35277777,0,0,-0.35277777,-143.40271,-5.076651)"
409 id="g1488">
410 <path
411 inkscape:connector-curvature="0"
412 id="path1490"
413 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
414 d="M 0,0 3.727,1.894 1.304,6.662 -2.423,4.769 Z" />
415 </g>
416 <g
417 transform="matrix(0.35277777,0,0,-0.35277777,-141.21457,-6.252141)"
418 id="g1492">
419 <path
420 inkscape:connector-curvature="0"
421 id="path1494"
422 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
423 d="M 0,0 3.383,2.456 0.241,6.783 -3.142,4.327 Z" />
424 </g>
425 <g
426 transform="matrix(0.35277777,0,0,-0.35277777,-132.42508,-42.839041)"
427 id="g1496">
428 <path
429 inkscape:connector-curvature="0"
430 id="path1498"
431 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
432 d="m 0,0 v -3.166 c 0,0 6.917,0.75 11.333,-3.666 4.416,-4.417 4,-10.668 4,-10.668 h 3 c 0,0 1.334,9.668 -3.833,14.168 C 9.333,1.168 0,0 0,0" />
433 </g>
434 </g>
435 </g>
436 </svg>
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4 <svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="52.027439mm"
13 height="53.148357mm"
14 viewBox="0 0 52.027439 53.148357"
15 version="1.1"
16 id="svg4043"
17 inkscape:version="0.92.3 (2405546, 2018-03-11)"
18 sodipodi:docname="SHORTPIXEL Robot Winky.svg">
19 <defs
20 id="defs4037" />
21 <sodipodi:namedview
22 id="base"
23 pagecolor="#ffffff"
24 bordercolor="#666666"
25 borderopacity="1.0"
26 inkscape:pageopacity="0.0"
27 inkscape:pageshadow="2"
28 inkscape:zoom="0.35"
29 inkscape:cx="-1798.8233"
30 inkscape:cy="486.15214"
31 inkscape:document-units="mm"
32 inkscape:current-layer="layer1"
33 showgrid="false"
34 fit-margin-top="0"
35 fit-margin-left="0"
36 fit-margin-right="0"
37 fit-margin-bottom="0"
38 inkscape:window-width="1853"
39 inkscape:window-height="1025"
40 inkscape:window-x="67"
41 inkscape:window-y="27"
42 inkscape:window-maximized="1" />
43 <metadata
44 id="metadata4040">
45 <rdf:RDF>
46 <cc:Work
47 rdf:about="">
48 <dc:format>image/svg+xml</dc:format>
49 <dc:type
50 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
51 <dc:title></dc:title>
52 </cc:Work>
53 </rdf:RDF>
54 </metadata>
55 <g
56 inkscape:label="Layer 1"
57 inkscape:groupmode="layer"
58 id="layer1"
59 transform="translate(-447.21247,-224.31273)">
60 <g
61 id="g6944"
62 transform="matrix(-0.53078769,0,0,0.53078769,489.41328,252.13805)">
63 <g
64 transform="matrix(0.35277777,0,0,-0.35277777,46.460542,25.88487)"
65 id="g997">
66 <path
67 inkscape:connector-curvature="0"
68 id="path999"
69 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
70 d="m 0,0 c 0,0 6.101,-5.748 6.101,-20.201 0,-14.454 -22.813,-41.661 -76.704,-41.661 -53.889,0 -72.254,18.875 -72.254,34.348 0,15.475 3.796,17.287 3.796,17.287 z" />
71 </g>
72 <g
73 transform="matrix(0.35277777,0,0,-0.35277777,44.515462,25.72298)"
74 id="g1001">
75 <path
76 inkscape:connector-curvature="0"
77 id="path1003"
78 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
79 d="m 0,0 5.559,-0.459 c 0,0 1.689,-15.385 1.689,-19.979 0,-10.438 -21.007,-37.632 -71.505,-37.632 -43.358,0 -69.29,13.539 -69.29,30.062 v 19.024 l 10.327,5.301 z" />
80 </g>
81 <g
82 transform="matrix(0.35277777,0,0,-0.35277777,45.233402,21.01421)"
83 id="g1005">
84 <path
85 inkscape:connector-curvature="0"
86 id="path1007"
87 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
88 d="m 0,0 c 0,0 3.523,-2.039 3.523,-16.554 0,-10.312 -20.755,-37.179 -70.647,-37.179 -42.837,0 -68.458,13.377 -68.458,29.7 0,19.725 10.202,24.033 10.202,24.033 z" />
89 </g>
90 <g
91 transform="matrix(0.35277777,0,0,-0.35277777,54.701222,-44.619004)"
92 id="g1009">
93 <path
94 inkscape:connector-curvature="0"
95 id="path1011"
96 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
97 d="m 0,0 38.855,-37.736 v -129.459 l -40.3,-15.984 z" />
98 </g>
99 <g
100 transform="matrix(0.35277777,0,0,-0.35277777,27.077062,24.43347)"
101 id="g1013">
102 <path
103 inkscape:connector-curvature="0"
104 id="path1015"
105 style="fill:#d7292a;fill-opacity:1;fill-rule:nonzero;stroke:none"
106 d="m 0,0 -0.781,9.692 h 52.248 c 0,0 3.523,-2.039 3.523,-16.553 0,-2.9 -1.641,-7.112 -5.111,-11.665 z" />
107 </g>
108 <g
109 transform="matrix(0.35277777,0,0,-0.35277777,14.719852,25.9531)"
110 id="g1017">
111 <path
112 inkscape:connector-curvature="0"
113 id="path1019"
114 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none"
115 d="m 0,0 c 0,-4.442 8.672,-8.044 19.371,-8.044 10.699,0 19.372,3.602 19.372,8.044 0,4.444 -8.673,8.044 -19.372,8.044 C 8.672,8.044 0,4.444 0,0" />
116 </g>
117 <path
118 inkscape:connector-curvature="0"
119 id="path1021"
120 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
121 d="m 14.719822,22.13075 h 13.66767 v 3.82235 h -13.66767 z" />
122 <g
123 transform="matrix(0.35277777,0,0,-0.35277777,14.719852,22.13075)"
124 id="g1023">
125 <path
126 inkscape:connector-curvature="0"
127 id="path1025"
128 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none"
129 d="m 0,0 c 0,-4.443 8.672,-8.044 19.371,-8.044 10.699,0 19.372,3.601 19.372,8.044 0,4.442 -8.673,8.044 -19.372,8.044 C 8.672,8.044 0,4.442 0,0" />
130 </g>
131 <path
132 inkscape:connector-curvature="0"
133 id="path1027"
134 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
135 d="m 14.719822,19.56253 h 13.66767 v 2.56822 h -13.66767 z" />
136 <g
137 transform="matrix(0.35277777,0,0,-0.35277777,17.822852,-46.01357)"
138 id="g1029">
139 <path
140 inkscape:connector-curvature="0"
141 id="path1031"
142 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
143 d="M 0,0 H 93.569 C 102.547,0 109,-7.092 109,-16.069 v -157.8 C 109,-182.847 102.547,-190 93.569,-190 H 0 Z" />
144 </g>
145 <g
146 transform="matrix(0.35277777,0,0,-0.35277777,-12.514658,-46.01357)"
147 id="g1033">
148 <path
149 inkscape:connector-curvature="0"
150 id="path1035"
151 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
152 d="M 0,0 H 85.996 V -190 H 0 c -8.978,0 -17.004,7.153 -17.004,16.131 v 157.8 C -17.004,-7.092 -8.978,0 0,0" />
153 </g>
154 <g
155 transform="matrix(0.35277777,0,0,-0.35277777,12.178412,-51.562235)"
156 id="g1037">
157 <path
158 inkscape:connector-curvature="0"
159 id="path1039"
160 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
161 d="m 0,0 v -7.481 l -44.041,-5.504 -0.494,11.115 z" />
162 </g>
163 <g
164 transform="matrix(0.35277777,0,0,-0.35277777,21.678252,-50.782631)"
165 id="g1041">
166 <path
167 inkscape:connector-curvature="0"
168 id="path1043"
169 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
170 d="M 0,0 55.889,4.649 56.454,-9.068 0.51,-8.843 Z" />
171 </g>
172 <g
173 transform="matrix(0.35277777,0,0,-0.35277777,60.430082,-10.1112)"
174 id="g1045">
175 <path
176 inkscape:connector-curvature="0"
177 id="path1047"
178 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
179 d="m 0,0 c 0,-20.854 9.82,-37.761 21.936,-37.761 12.115,0 21.935,16.907 21.935,37.761 0,20.854 -9.82,37.76 -21.935,37.76 C 9.82,37.76 0,20.854 0,0" />
180 </g>
181 <g
182 transform="matrix(0.35277777,0,0,-0.35277777,68.012052,-21.92855)"
183 id="g1049">
184 <path
185 inkscape:connector-curvature="0"
186 id="path1051"
187 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
188 d="m 0,0 c 0.146,0 7.769,0.032 7.916,0.032 9.428,0 17.069,-15.014 17.069,-33.514 0,-18.5 -7.641,-33.506 -17.069,-33.506 -0.147,0 -7.77,-0.017 -7.916,-0.017 -9.43,0 -17.071,15.001 -17.071,33.503 C -17.071,-15.001 -9.43,0 0,0" />
189 </g>
190 <g
191 transform="matrix(0.35277777,0,0,-0.35277777,64.869082,-10.1112)"
192 id="g1053">
193 <path
194 inkscape:connector-curvature="0"
195 id="path1055"
196 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
197 d="m 0,0 c 0,-18.5 7.588,-33.499 16.947,-33.499 9.361,0 16.948,14.999 16.948,33.499 0,18.5 -7.587,33.498 -16.948,33.498 C 7.588,33.498 0,18.5 0,0" />
198 </g>
199 <g
200 transform="matrix(0.35277777,0,0,-0.35277777,70.747772,-10.41092)"
201 id="g1057">
202 <path
203 inkscape:connector-curvature="0"
204 id="path1059"
205 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
206 d="m 0,0 c 0,-4.602 2.285,-8.332 5.102,-8.332 2.816,0 5.101,3.73 5.101,8.332 0,4.603 -2.285,8.333 -5.101,8.333 C 2.285,8.333 0,4.603 0,0" />
207 </g>
208 <g
209 transform="matrix(0.35277777,0,0,-0.35277777,73.327462,-13.35062)"
210 id="g1061">
211 <path
212 inkscape:connector-curvature="0"
213 id="path1063"
214 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
215 d="m 0,0 h 14.793 c 1.503,0 2.721,-3.73 2.721,-8.333 0,-4.602 -1.218,-8.332 -2.721,-8.332 H 0 c -1.503,0 -2.721,3.73 -2.721,8.332 C -2.721,-3.73 -1.503,0 0,0" />
216 </g>
217 <g
218 transform="matrix(0.35277777,0,0,-0.35277777,29.464522,-28.6689)"
219 id="g1065">
220 <path
221 inkscape:connector-curvature="0"
222 id="path1067"
223 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
224 d="m 0,0 c 3,0.011 6.16,1.143 6.273,1.132 16.645,-1.478 27.931,-12.87 27.931,-27.38 0,-14.51 -12.386,-26.29 -27.334,-26.29 -0.114,0 -3.87,-0.003 -6.87,-0.003 z" />
225 </g>
226 <g
227 transform="matrix(0.35277777,0,0,-0.35277777,20.518992,-19.67927)"
228 id="g1069">
229 <path
230 inkscape:connector-curvature="0"
231 id="path1071"
232 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
233 d="m 0,0 c 0,-14.948 12.117,-27.066 27.065,-27.066 14.949,0 27.067,12.118 27.067,27.066 0,14.947 -12.118,27.065 -27.067,27.065 C 12.117,27.065 0,14.947 0,0" />
234 </g>
235 <g
236 transform="matrix(0.35277777,0,0,-0.35277777,22.977392,-19.85908)"
237 id="g1073">
238 <path
239 inkscape:connector-curvature="0"
240 id="path1075"
241 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
242 d="m 0,0 c 0,-10.816 8.77,-19.587 19.587,-19.587 10.816,0 19.585,8.771 19.585,19.587 0,10.816 -8.769,19.587 -19.585,19.587 C 8.77,19.587 0,10.816 0,0" />
243 </g>
244 <g
245 transform="matrix(0.35277777,0,0,-0.35277777,26.313652,-19.79915)"
246 id="g1077">
247 <path
248 inkscape:connector-curvature="0"
249 id="path1079"
250 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
251 d="m 0,0 c 0,-5.688 4.611,-10.299 10.3,-10.299 5.686,0 10.298,4.611 10.298,10.299 0,5.688 -4.612,10.298 -10.298,10.298 C 4.611,10.298 0,5.688 0,0" />
252 </g>
253 <g
254 transform="matrix(0.35277777,0,0,-0.35277777,26.192362,4.34062)"
255 id="g1081">
256 <path
257 inkscape:connector-curvature="0"
258 id="path1083"
259 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
260 d="M 0,0 C -0.095,-6.427 5.036,-11.713 11.462,-11.808 17.889,-11.903 23.174,-6.771 23.27,-0.344 23.365,6.08 18.232,11.366 11.807,11.461 5.381,11.557 0.096,6.426 0,0" />
261 </g>
262 <g
263 transform="matrix(0.35277777,0,0,-0.35277777,0.9835719,3.96753)"
264 id="g1085">
265 <path
266 inkscape:connector-curvature="0"
267 id="path1087"
268 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
269 d="M 0,0 C -0.096,-6.426 5.035,-11.713 11.462,-11.808 17.889,-11.903 23.174,-6.771 23.27,-0.345 23.365,6.081 18.233,11.367 11.808,11.462 5.38,11.556 0.096,6.426 0,0" />
270 </g>
271 <g
272 transform="matrix(0.35277777,0,0,-0.35277777,2.4887319,7.20179)"
273 id="g1089">
274 <path
275 inkscape:connector-curvature="0"
276 id="path1091"
277 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
278 d="m 0,0 c 0,0 15.184,-16.789 44.56,-17.224 25.579,-0.379 42.743,17.209 42.743,17.209 L 70.541,16.109 c 0,0 -12.923,-10.922 -26.808,-10.641 -18.97,0.382 -27.667,11.35 -27.667,11.35 z" />
279 </g>
280 <g
281 transform="matrix(0.35277777,0,0,-0.35277777,25.803432,2.9867)"
282 id="g1093">
283 <path
284 inkscape:connector-curvature="0"
285 id="path1095"
286 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
287 d="m 0,0 c 0,-6.514 5.279,-11.794 11.792,-11.794 6.514,0 11.793,5.28 11.793,11.794 0,6.512 -5.279,11.791 -11.793,11.791 C 5.279,11.791 0,6.512 0,0" />
288 </g>
289 <g
290 transform="matrix(0.35277777,0,0,-0.35277777,0.2535719,2.9867)"
291 id="g1097">
292 <path
293 inkscape:connector-curvature="0"
294 id="path1099"
295 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
296 d="m 0,0 c 0,-6.514 5.279,-11.794 11.791,-11.794 6.514,0 11.793,5.28 11.793,11.794 0,6.512 -5.279,11.791 -11.793,11.791 C 5.279,11.791 0,6.512 0,0" />
297 </g>
298 <g
299 transform="matrix(0.35277777,0,0,-0.35277777,1.8265719,6.24199)"
300 id="g1101">
301 <path
302 inkscape:connector-curvature="0"
303 id="path1103"
304 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
305 d="m 0,0 c 0,0 15.639,-16.784 45.412,-16.784 25.925,0 43.055,18.081 43.055,18.081 L 71.241,17.382 c 0,0 -12.932,-11.261 -27.007,-11.186 C 25.006,6.3 16.028,17.283 16.028,17.283 Z" />
306 </g>
307 <path
308 inkscape:connector-curvature="0"
309 id="path1105"
310 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
311 d="m 15.722772,9.51259 h 2.00624 V 6.94578 h -2.00624 z" />
312 <g
313 transform="matrix(0.35277777,0,0,-0.35277777,19.211212,9.38185)"
314 id="g1107">
315 <path
316 inkscape:connector-curvature="0"
317 id="path1109"
318 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
319 d="M 0,0 5.545,1.259 3.936,8.355 -1.612,7.097 Z" />
320 </g>
321 <g
322 transform="matrix(0.35277777,0,0,-0.35277777,22.456162,8.56778)"
323 id="g1111">
324 <path
325 inkscape:connector-curvature="0"
326 id="path1113"
327 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
328 d="M 0,0 5.298,2.069 2.65,8.846 -2.646,6.778 Z" />
329 </g>
330 <g
331 transform="matrix(0.35277777,0,0,-0.35277777,25.490622,7.34615)"
332 id="g1115">
333 <path
334 inkscape:connector-curvature="0"
335 id="path1117"
336 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
337 d="M 0,0 5.069,2.576 1.773,9.063 -3.296,6.487 Z" />
338 </g>
339 <g
340 transform="matrix(0.35277777,0,0,-0.35277777,28.467182,5.74694)"
341 id="g1119">
342 <path
343 inkscape:connector-curvature="0"
344 id="path1121"
345 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
346 d="M 0,0 4.602,3.341 0.328,9.227 -4.273,5.886 Z" />
347 </g>
348 <path
349 inkscape:connector-curvature="0"
350 id="path1123"
351 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277778"
352 d="m 17.728662,9.51259 h -2.00625 V 6.94578 h 2.00625 z" />
353 <g
354 transform="matrix(0.35277777,0,0,-0.35277777,14.240642,9.38185)"
355 id="g1125">
356 <path
357 inkscape:connector-curvature="0"
358 id="path1127"
359 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
360 d="M 0,0 -5.545,1.259 -3.936,8.355 1.61,7.097 Z" />
361 </g>
362 <g
363 transform="matrix(0.35277777,0,0,-0.35277777,10.995012,8.56778)"
364 id="g1129">
365 <path
366 inkscape:connector-curvature="0"
367 id="path1131"
368 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
369 d="M 0,0 -5.297,2.069 -2.649,8.846 2.648,6.778 Z" />
370 </g>
371 <g
372 transform="matrix(0.35277777,0,0,-0.35277777,7.9609119,7.34615)"
373 id="g1133">
374 <path
375 inkscape:connector-curvature="0"
376 id="path1135"
377 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
378 d="M 0,0 -5.069,2.576 -1.775,9.063 3.296,6.487 Z" />
379 </g>
380 <g
381 transform="matrix(0.35277777,0,0,-0.35277777,4.9840019,5.74694)"
382 id="g1137">
383 <path
384 inkscape:connector-curvature="0"
385 id="path1139"
386 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
387 d="M 0,0 -4.602,3.341 -0.327,9.227 4.274,5.886 Z" />
388 </g>
389 <g
390 transform="matrix(0.35277777,0,0,-0.35277777,-6.9722081,-44.023691)"
391 id="g1141">
392 <path
393 inkscape:connector-curvature="0"
394 id="path1143"
395 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
396 d="m 0,0 v -4.307 c 0,0 -9.411,1.02 -15.418,-4.987 -6.008,-6.009 -5.441,-14.512 -5.441,-14.512 h -4.081 c 0,0 -1.816,13.152 5.214,19.273 C -12.697,1.589 0,0 0,0" />
397 </g>
398 <g
399 transform="matrix(0.35277777,0,0,-0.35277777,-10.267078,-18.98197)"
400 id="g1145">
401 <path
402 inkscape:connector-curvature="0"
403 id="path1147"
404 style="fill:#ee2824;fill-opacity:1;fill-rule:nonzero;stroke:none"
405 d="m 0,0 c 0,0 8.375,10.125 21.125,10.125 12.75,0 19.125,-12.875 19.125,-12.875 l 5.375,2 c 0,0 -5.25,19.5 -24.75,19.5 C 1.375,18.75 -6.25,2.125 -6.25,2.125 Z" />
406 </g>
407 <g
408 transform="matrix(0.35277777,0,0,-0.35277777,-11.991348,-20.57413)"
409 id="g1149">
410 <path
411 inkscape:connector-curvature="0"
412 id="path1151"
413 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
414 d="M 0,0 C 0.002,0.001 11.373,9.681 26.263,9.362 39.589,9.076 46.138,3.528 46.183,3.49 42.526,8.674 36.179,14.237 25.763,14.237 10.909,14.237 2.952,4.597 0,0" />
415 </g>
416 </g>
417 </g>
418 </svg>
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4 <svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 version="1.1"
13 id="svg3767"
14 xml:space="preserve"
15 width="128"
16 height="128"
17 viewBox="0 0 128 128"
18 sodipodi:docname="SPAI_logo.svg"
19 inkscape:version="0.92.3 (2405546, 2018-03-11)"
20 inkscape:export-filename="/home/eros/Desktop/SPAI_logo.svg.png"
21 inkscape:export-xdpi="96"
22 inkscape:export-ydpi="96"><metadata
23 id="metadata3773"><rdf:RDF><cc:Work
24 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
25 rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
26 id="defs3771"><clipPath
27 clipPathUnits="userSpaceOnUse"
28 id="clipPath3797"><path
29 d="M 0,972 H 1400 V 0 H 0 Z"
30 id="path3795"
31 inkscape:connector-curvature="0" /></clipPath><clipPath
32 clipPathUnits="userSpaceOnUse"
33 id="clipPath3973"><path
34 d="M 0,972 H 1400 V 0 H 0 Z"
35 id="path3971"
36 inkscape:connector-curvature="0" /></clipPath><clipPath
37 clipPathUnits="userSpaceOnUse"
38 id="clipPath4159"><path
39 d="M 0,972 H 1400 V 0 H 0 Z"
40 id="path4157"
41 inkscape:connector-curvature="0" /></clipPath><clipPath
42 clipPathUnits="userSpaceOnUse"
43 id="clipPath4193"><path
44 d="M 0,972 H 1400 V 0 H 0 Z"
45 id="path4191"
46 inkscape:connector-curvature="0" /></clipPath><clipPath
47 clipPathUnits="userSpaceOnUse"
48 id="clipPath4618"><g
49 inkscape:label="Clip"
50 id="use4620"><g
51 transform="translate(551.1328,622.08282)"
52 id="g4790"><path
53 inkscape:connector-curvature="0"
54 id="path4788"
55 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
56 d="m 0,0 c 0,0 -4.484,-4.226 -4.484,-14.851 0,-10.625 16.769,-30.625 56.384,-30.625 39.616,0 53.116,13.875 53.116,25.25 0,11.375 -2.791,12.708 -2.791,12.708 z" /></g><g
57 transform="translate(555.1855,622.41972)"
58 id="g4794"><path
59 inkscape:connector-curvature="0"
60 id="path4792"
61 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
62 d="m 0,0 -4.086,-0.337 c 0,0 -1.242,-11.309 -1.242,-14.687 0,-7.673 15.443,-27.664 52.564,-27.664 31.873,0 50.936,9.953 50.936,22.099 v 13.985 l -7.592,3.897 z" /></g><g
63 transform="translate(553.6894,632.23222)"
64 id="g4798"><path
65 inkscape:connector-curvature="0"
66 id="path4796"
67 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
68 d="m 0,0 c 0,0 -2.59,-1.499 -2.59,-12.169 0,-7.581 15.258,-27.331 51.934,-27.331 31.49,0 50.324,9.833 50.324,21.833 0,14.5 -7.5,17.667 -7.5,17.667 z" /></g><g
69 transform="translate(533.9609,769.13842)"
70 id="g4802"><path
71 inkscape:connector-curvature="0"
72 id="path4800"
73 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
74 d="m 0,0 -28.562,-27.74 v -95.166 l 29.624,-11.75 z" /></g><g
75 transform="translate(591.5234,625.10722)"
76 id="g4806"><path
77 inkscape:connector-curvature="0"
78 id="path4804"
79 style="fill:#d7292a;fill-opacity:1;fill-rule:nonzero;stroke:none"
80 d="m 0,0 0.574,7.125 h -38.408 c 0,0 -2.59,-1.499 -2.59,-12.169 0,-2.132 1.207,-5.227 3.758,-8.575 z" /></g><g
81 transform="translate(617.2734,622.08282)"
82 id="g4810"><path
83 inkscape:connector-curvature="0"
84 id="path4808"
85 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none"
86 d="m 0,0 c 0,-3.266 -6.375,-5.913 -14.24,-5.913 -7.865,0 -14.24,2.647 -14.24,5.913 0,3.267 6.375,5.913 14.24,5.913 C -6.375,5.913 0,3.267 0,0" /></g><path
87 inkscape:connector-curvature="0"
88 id="path4812"
89 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none"
90 d="m 617.2729,630.04723 h -28.48 v -7.96501 h 28.48 z" /><g
91 transform="translate(617.2734,630.04762)"
92 id="g4816"><path
93 inkscape:connector-curvature="0"
94 id="path4814"
95 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none"
96 d="m 0,0 c 0,-3.267 -6.375,-5.913 -14.24,-5.913 -7.865,0 -14.24,2.646 -14.24,5.913 0,3.266 6.375,5.913 14.24,5.913 C -6.375,5.913 0,3.266 0,0" /></g><path
97 inkscape:connector-curvature="0"
98 id="path4818"
99 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none"
100 d="m 617.2729,635.39922 h -28.48 v -4.917 h 28.48 z" /><g
101 transform="translate(609.7109,772.23222)"
102 id="g4822"><path
103 inkscape:connector-curvature="0"
104 id="path4820"
105 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
106 d="M 0,0 H -67.688 C -74.287,0 -80,-5.4 -80,-12 v -116 c 0,-6.6 5.713,-12 12.312,-12 H 0 Z" /></g><g
107 transform="translate(674.0234,772.23222)"
108 id="g4826"><path
109 inkscape:connector-curvature="0"
110 id="path4824"
111 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
112 d="M 0,0 H -64.313 V -140 H 0 c 6.6,0 11.688,5.4 11.688,12 V -12 C 11.688,-5.4 6.6,0 0,0" /></g><g
113 transform="translate(621.7109,783.60722)"
114 id="g4830"><path
115 inkscape:connector-curvature="0"
116 id="path4828"
117 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
118 d="m 0,0 v -5.5 l 32.805,-4.046 0.148,8.171 z" /></g><g
119 transform="translate(602.7734,781.98222)"
120 id="g4834"><path
121 inkscape:connector-curvature="0"
122 id="path4832"
123 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
124 d="M 0,0 -41.084,3.418 -41.5,-6.666 -0.375,-6.5 Z" /></g><g
125 transform="translate(522.0234,697.23222)"
126 id="g4838"><path
127 inkscape:connector-curvature="0"
128 id="path4836"
129 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
130 d="m 0,0 c 0,-15.33 -7.219,-27.758 -16.125,-27.758 -8.906,0 -16.125,12.428 -16.125,27.758 0,15.33 7.219,27.758 16.125,27.758 C -7.219,27.758 0,15.33 0,0" /></g><g
131 transform="translate(506.2246,721.85722)"
132 id="g4842"><path
133 inkscape:connector-curvature="0"
134 id="path4840"
135 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
136 d="m 0,0 c -0.107,0 -5.711,0.023 -5.818,0.023 -6.932,0 -12.549,-11.037 -12.549,-24.636 0,-13.6 5.617,-24.631 12.549,-24.631 0.107,0 5.711,-0.012 5.818,-0.012 6.932,0 12.549,11.027 12.549,24.628 C 12.549,-11.027 6.932,0 0,0" /></g><g
137 transform="translate(512.7734,697.23222)"
138 id="g4846"><path
139 inkscape:connector-curvature="0"
140 id="path4844"
141 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
142 d="m 0,0 c 0,-13.6 -5.578,-24.625 -12.457,-24.625 -6.881,0 -12.459,11.025 -12.459,24.625 0,13.6 5.578,24.625 12.459,24.625 C -5.578,24.625 0,13.6 0,0" /></g><g
143 transform="translate(500.5234,697.85722)"
144 id="g4850"><path
145 inkscape:connector-curvature="0"
146 id="path4848"
147 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
148 d="m 0,0 c 0,-3.383 -1.68,-6.125 -3.75,-6.125 -2.07,0 -3.75,2.742 -3.75,6.125 0,3.383 1.68,6.125 3.75,6.125 C -1.68,6.125 0,3.383 0,0" /></g><g
149 transform="translate(495.1484,703.98222)"
150 id="g4854"><path
151 inkscape:connector-curvature="0"
152 id="path4852"
153 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
154 d="m 0,0 h -10.875 c -1.104,0 -2,-2.742 -2,-6.125 0,-3.383 0.896,-6.125 2,-6.125 H 0 c 1.105,0 2,2.742 2,6.125 C 2,-2.742 1.105,0 0,0" /></g><g
155 transform="translate(640.7109,735.90212)"
156 id="g4858"><path
157 inkscape:connector-curvature="0"
158 id="path4856"
159 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
160 d="m 0,0 c -1,0.008 -4.109,0.84 -4.193,0.832 -12.235,-1.086 -20.323,-9.461 -20.323,-20.127 0,-10.666 8.791,-19.326 19.78,-19.326 0.084,0 3.736,-0.002 4.736,-0.002 z" /></g><g
161 transform="translate(660.1894,717.16972)"
162 id="g4862"><path
163 inkscape:connector-curvature="0"
164 id="path4860"
165 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
166 d="m 0,0 c 0,-10.988 -8.908,-19.896 -19.896,-19.896 -10.989,0 -19.897,8.908 -19.897,19.896 0,10.988 8.908,19.896 19.897,19.896 C -8.908,19.896 0,10.988 0,0" /></g><g
167 transform="translate(655.0664,717.54472)"
168 id="g4866"><path
169 inkscape:connector-curvature="0"
170 id="path4864"
171 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
172 d="m 0,0 c 0,-7.951 -6.445,-14.398 -14.398,-14.398 -7.952,0 -14.397,6.447 -14.397,14.398 0,7.951 6.445,14.398 14.397,14.398 C -6.445,14.398 0,7.951 0,0" /></g><g
173 transform="translate(648.1152,717.41972)"
174 id="g4870"><path
175 inkscape:connector-curvature="0"
176 id="path4868"
177 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
178 d="m 0,0 c 0,-4.182 -3.391,-7.57 -7.572,-7.57 -4.18,0 -7.571,3.388 -7.571,7.57 0,4.182 3.391,7.57 7.571,7.57 C -3.391,7.57 0,4.182 0,0" /></g><g
179 transform="translate(585.7109,735.90212)"
180 id="g4874"><path
181 inkscape:connector-curvature="0"
182 id="path4872"
183 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
184 d="m 0,0 c -1,0.008 -4.109,0.84 -4.193,0.832 -12.235,-1.086 -20.323,-9.461 -20.323,-20.127 0,-10.666 8.791,-19.326 19.78,-19.326 0.084,0 3.736,-0.002 4.736,-0.002 z" /></g><g
185 transform="translate(605.1894,717.16972)"
186 id="g4878"><path
187 inkscape:connector-curvature="0"
188 id="path4876"
189 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
190 d="m 0,0 c 0,-10.988 -8.908,-19.896 -19.896,-19.896 -10.989,0 -19.897,8.908 -19.897,19.896 0,10.988 8.908,19.896 19.897,19.896 C -8.908,19.896 0,10.988 0,0" /></g><g
191 transform="translate(600.0664,717.54472)"
192 id="g4882"><path
193 inkscape:connector-curvature="0"
194 id="path4880"
195 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
196 d="m 0,0 c 0,-7.951 -6.445,-14.398 -14.398,-14.398 -7.952,0 -14.397,6.447 -14.397,14.398 0,7.951 6.445,14.398 14.397,14.398 C -6.445,14.398 0,7.951 0,0" /></g><g
197 transform="translate(593.1152,717.41972)"
198 id="g4886"><path
199 inkscape:connector-curvature="0"
200 id="path4884"
201 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
202 d="m 0,0 c 0,-4.182 -3.391,-7.57 -7.572,-7.57 -4.18,0 -7.571,3.388 -7.571,7.57 0,4.182 3.391,7.57 7.571,7.57 C -3.391,7.57 0,4.182 0,0" /></g><g
203 transform="translate(593.3671,667.11793)"
204 id="g4890"><path
205 inkscape:connector-curvature="0"
206 id="path4888"
207 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
208 d="m 0,0 c 0.07,-4.725 -3.701,-8.61 -8.426,-8.68 -4.724,-0.07 -8.609,3.702 -8.679,8.427 -0.071,4.723 3.703,8.608 8.425,8.678 C -3.955,8.495 -0.07,4.724 0,0" /></g><g
209 transform="translate(645.8964,667.89532)"
210 id="g4894"><path
211 inkscape:connector-curvature="0"
212 id="path4892"
213 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
214 d="m 0,0 c 0.07,-4.724 -3.701,-8.61 -8.426,-8.68 -4.724,-0.07 -8.609,3.702 -8.679,8.427 -0.071,4.724 3.703,8.609 8.425,8.679 C -3.955,8.495 -0.07,4.724 0,0" /></g><g
215 transform="translate(642.7597,661.15602)"
216 id="g4898"><path
217 inkscape:connector-curvature="0"
218 id="path4896"
219 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
220 d="m 0,0 c 0,0 -11.16,-12.342 -32.756,-12.661 -18.803,-0.278 -31.42,12.65 -31.42,12.65 l 12.321,11.853 c 0,0 9.5,-8.029 19.707,-7.822 13.945,0.28 20.337,8.343 20.337,8.343 z" /></g><g
221 transform="translate(594.1777,669.93922)"
222 id="g4902"><path
223 inkscape:connector-curvature="0"
224 id="path4900"
225 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
226 d="m 0,0 c 0,-4.788 -3.881,-8.67 -8.668,-8.67 -4.789,0 -8.67,3.882 -8.67,8.67 0,4.787 3.881,8.668 8.67,8.668 C -3.881,8.668 0,4.787 0,0" /></g><g
227 transform="translate(647.4179,669.93922)"
228 id="g4906"><path
229 inkscape:connector-curvature="0"
230 id="path4904"
231 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
232 d="m 0,0 c 0,-4.788 -3.881,-8.67 -8.668,-8.67 -4.787,0 -8.668,3.882 -8.668,8.67 0,4.787 3.881,8.668 8.668,8.668 C -3.881,8.668 0,4.787 0,0" /></g><g
233 transform="translate(644.1406,663.15602)"
234 id="g4910"><path
235 inkscape:connector-curvature="0"
236 id="path4908"
237 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
238 d="m 0,0 c 0,0 -11.496,-12.338 -33.383,-12.338 -19.058,0 -31.65,13.291 -31.65,13.291 l 12.662,11.824 c 0,0 9.508,-8.277 19.853,-8.222 14.135,0.076 20.735,8.15 20.735,8.15 z" /></g><path
239 inkscape:connector-curvature="0"
240 id="path4912"
241 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
242 d="m 615.1839,656.34022 h -4.18 v 5.349 h 4.18 z" /><g
243 transform="translate(607.914,656.61312)"
244 id="g4916"><path
245 inkscape:connector-curvature="0"
246 id="path4914"
247 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
248 d="M 0,0 -4.076,0.926 -2.893,6.143 1.186,5.217 Z" /></g><g
249 transform="translate(601.1523,658.30932)"
250 id="g4920"><path
251 inkscape:connector-curvature="0"
252 id="path4918"
253 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
254 d="m 0,0 -3.895,1.521 1.948,4.982 3.892,-1.52 z" /></g><g
255 transform="translate(594.83,660.85523)"
256 id="g4924"><path
257 inkscape:connector-curvature="0"
258 id="path4922"
259 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
260 d="M 0,0 -3.727,1.894 -1.305,6.662 2.422,4.769 Z" /></g><g
261 transform="translate(588.6269,664.18732)"
262 id="g4928"><path
263 inkscape:connector-curvature="0"
264 id="path4926"
265 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
266 d="M 0,0 -3.383,2.456 -0.24,6.783 3.143,4.327 Z" /></g><path
267 inkscape:connector-curvature="0"
268 id="path4930"
269 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
270 d="m 611.0039,656.34022 h 4.18 v 5.349 h -4.18 z" /><g
271 transform="translate(618.2714,656.61312)"
272 id="g4934"><path
273 inkscape:connector-curvature="0"
274 id="path4932"
275 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
276 d="M 0,0 4.076,0.926 2.895,6.143 -1.184,5.217 Z" /></g><g
277 transform="translate(625.0351,658.30932)"
278 id="g4938"><path
279 inkscape:connector-curvature="0"
280 id="path4936"
281 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
282 d="M 0,0 3.895,1.521 1.947,6.503 -1.947,4.983 Z" /></g><g
283 transform="translate(631.3574,660.85523)"
284 id="g4942"><path
285 inkscape:connector-curvature="0"
286 id="path4940"
287 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
288 d="M 0,0 3.727,1.894 1.305,6.662 -2.422,4.769 Z" /></g><g
289 transform="translate(637.5605,664.18732)"
290 id="g4946"><path
291 inkscape:connector-curvature="0"
292 id="path4944"
293 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
294 d="M 0,0 3.383,2.456 0.24,6.783 -3.143,4.327 Z" /></g><g
295 transform="translate(662.4746,767.89823)"
296 id="g4950"><path
297 inkscape:connector-curvature="0"
298 id="path4948"
299 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
300 d="m 0,0 v -3.166 c 0,0 6.918,0.75 11.334,-3.666 4.416,-4.417 4,-10.668 4,-10.668 h 3 c 0,0 1.334,9.668 -3.834,14.168 C 9.334,1.168 0,0 0,0" /></g></g></clipPath><marker
301 style="overflow:visible"
302 id="Scissors"
303 orient="auto"
304 refY="0"
305 refX="0"><path
306 style="marker-start:none"
307 id="schere"
308 d="M 9.0898857,-3.6061018 C 8.1198849,-4.7769976 6.3697607,-4.7358294 5.0623558,-4.2327734 l -8.2124046,3.0779029 c -2.3882933,-1.3067135 -4.7482873,-0.9325372 -4.7482873,-1.5687873 0,-0.4973164 0.4566662,-0.3883222 0.3883068,-1.6831941 -0.065635,-1.2432767 -1.3635771,-2.1630796 -2.5903987,-2.0816435 -1.227271,-0.00735 -2.499439,0.9331613 -2.510341,2.2300611 -0.09143,1.3063864 1.007209,2.5196896 2.306764,2.6052316 1.5223406,0.2266616 4.218258,-0.6955566 5.482945,1.57086006 -0.9422847,1.73825774 -2.6140244,1.74307674 -4.1255107,1.65607034 -1.2548743,-0.072235 -2.7620933,0.2873979 -3.3606483,1.5208605 -0.578367,1.1820862 -0.0112,2.8646022 1.316749,3.226412 1.3401912,0.4918277 3.1806689,-0.129711 3.4993722,-1.6707242 0.2456585,-1.187823 -0.5953659,-1.7459574 -0.2725074,-2.1771537 0.2436135,-0.32536 1.7907806,-0.1368452 4.5471053,-1.3748244 L 5.6763468,4.2330688 C 6.8000164,4.5467672 8.1730685,4.5362646 9.1684433,3.4313614 l -9.22008423,-3.48508362 z m -18.3078016,-1.900504 c 1.294559,0.7227998 1.1888392,2.6835702 -0.1564272,3.0632889 -1.2165179,0.423661 -2.7710269,-0.7589694 -2.3831779,-2.0774648 0.227148,-1.0818519 1.653387,-1.480632 2.5396051,-0.9858241 z m 0.056264,8.0173649 c 1.3508301,0.4988648 1.1214429,2.7844356 -0.2522207,3.091609 -0.9110594,0.3163391 -2.2135494,-0.1387976 -2.3056964,-1.2121394 -0.177609,-1.305055 1.356085,-2.4841482 2.5579171,-1.8794696 z"
309 inkscape:connector-curvature="0" /></marker></defs><sodipodi:namedview
310 pagecolor="#ffffff"
311 bordercolor="#666666"
312 borderopacity="1"
313 objecttolerance="10"
314 gridtolerance="10"
315 guidetolerance="10"
316 inkscape:pageopacity="0"
317 inkscape:pageshadow="2"
318 inkscape:window-width="1853"
319 inkscape:window-height="1025"
320 id="namedview3769"
321 showgrid="false"
322 inkscape:zoom="3.6042856"
323 inkscape:cx="75.458418"
324 inkscape:cy="48.883031"
325 inkscape:window-x="67"
326 inkscape:window-y="27"
327 inkscape:window-maximized="1"
328 inkscape:current-layer="g3775" /><g
329 id="g3775"
330 inkscape:groupmode="layer"
331 inkscape:label="SP_landing_page_BF"
332 transform="matrix(1.3333333,0,0,-1.3333333,0,128)"><g
333 transform="matrix(0.3749127,0,0,0.3749127,-57.012955,-199.31617)"
334 id="g7806-3"><g
335 id="g3799-7-67"
336 transform="translate(220.92934,624.37578)"><path
337 d="m 0,0 c 0,0 -4.484,-4.226 -4.484,-14.851 0,-10.625 16.769,-30.625 56.384,-30.625 39.616,0 53.116,13.875 53.116,25.25 0,11.375 -2.791,12.708 -2.791,12.708 z"
338 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
339 id="path3801-6-5"
340 inkscape:connector-curvature="0" /></g><g
341 id="g3803-1-3"
342 transform="translate(224.98204,624.71268)"><path
343 d="m 0,0 -4.086,-0.337 c 0,0 -1.242,-11.309 -1.242,-14.687 0,-7.673 15.443,-27.664 52.564,-27.664 31.873,0 50.936,9.953 50.936,22.099 v 13.985 l -7.592,3.897 z"
344 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
345 id="path3805-3-5"
346 inkscape:connector-curvature="0" /></g><g
347 id="g3807-2-6"
348 transform="translate(223.48594,634.52518)"><path
349 d="m 0,0 c 0,0 -2.59,-1.499 -2.59,-12.169 0,-7.581 15.258,-27.331 51.934,-27.331 31.49,0 50.324,9.833 50.324,21.833 0,14.5 -7.5,17.667 -7.5,17.667 z"
350 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
351 id="path3809-1-29"
352 inkscape:connector-curvature="0" /></g><g
353 id="g3815-59-1"
354 transform="translate(261.31996,627.40018)"><path
355 d="m 0,0 0.574,7.125 h -38.408 c 0,0 -2.59,-1.499 -2.59,-12.169 0,-2.132 1.207,-5.227 3.758,-8.575 z"
356 style="fill:#d7292a;fill-opacity:1;fill-rule:nonzero;stroke:none"
357 id="path3817-9-2"
358 inkscape:connector-curvature="0" /></g><g
359 id="g3811-0-6"
360 transform="translate(203.75745,771.43138)"><path
361 d="m 0,0 -28.562,-27.74 v -95.166 l 29.624,-11.75 z"
362 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
363 id="path3813-7-0"
364 inkscape:connector-curvature="0" /></g><g
365 id="g3819-5-6"
366 transform="translate(287.06995,624.37578)"><path
367 d="m 0,0 c 0,-3.266 -6.375,-5.913 -14.24,-5.913 -7.865,0 -14.24,2.647 -14.24,5.913 0,3.267 6.375,5.913 14.24,5.913 C -6.375,5.913 0,3.267 0,0"
368 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none"
369 id="path3821-8-2"
370 inkscape:connector-curvature="0" /></g><path
371 d="m 287.06945,632.34019 h -28.48 v -7.96501 h 28.48 z"
372 style="fill:#ebeadb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994"
373 id="path3823-0-1"
374 inkscape:connector-curvature="0" /><g
375 id="g3825-4-8"
376 transform="translate(287.06995,632.34058)"><path
377 d="m 0,0 c 0,-3.267 -6.375,-5.913 -14.24,-5.913 -7.865,0 -14.24,2.646 -14.24,5.913 0,3.266 6.375,5.913 14.24,5.913 C -6.375,5.913 0,3.266 0,0"
378 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none"
379 id="path3827-8-7"
380 inkscape:connector-curvature="0" /></g><path
381 d="m 287.06945,637.69218 h -28.48 v -4.917 h 28.48 z"
382 style="fill:#cbcdc5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994"
383 id="path3829-0-9"
384 inkscape:connector-curvature="0" /><g
385 id="g3831-4-2"
386 transform="translate(279.50746,774.52517)"><path
387 d="M 0,0 H -67.688 C -74.287,0 -80,-5.4 -80,-12 v -116 c 0,-6.6 5.713,-12 12.312,-12 H 0 Z"
388 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
389 id="path3833-2-0"
390 inkscape:connector-curvature="0" /></g><g
391 id="g3835-9-23"
392 transform="translate(343.81995,774.52517)"><path
393 d="M 0,0 H -64.313 V -140 H 0 c 6.6,0 11.688,5.4 11.688,12 V -12 C 11.688,-5.4 6.6,0 0,0"
394 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
395 id="path3837-61-7"
396 inkscape:connector-curvature="0" /></g><g
397 id="g3839-0-5"
398 transform="translate(291.50746,785.90018)"><path
399 d="m 0,0 v -5.5 l 32.805,-4.046 0.148,8.171 z"
400 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
401 id="path3841-4-9"
402 inkscape:connector-curvature="0" /></g><g
403 id="g3843-2-2"
404 transform="translate(272.56996,784.27517)"><path
405 d="M 0,0 -41.084,3.418 -41.5,-6.666 -0.375,-6.5 Z"
406 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
407 id="path3845-2-2"
408 inkscape:connector-curvature="0" /></g><g
409 id="g3847-2-89"
410 transform="translate(191.81995,699.52518)"><path
411 d="m 0,0 c 0,-15.33 -7.219,-27.758 -16.125,-27.758 -8.906,0 -16.125,12.428 -16.125,27.758 0,15.33 7.219,27.758 16.125,27.758 C -7.219,27.758 0,15.33 0,0"
412 style="fill:#1fbfca;fill-opacity:1;fill-rule:nonzero;stroke:none"
413 id="path3849-0-7"
414 inkscape:connector-curvature="0" /></g><g
415 id="g3851-55-3"
416 transform="translate(176.02115,724.15018)"><path
417 d="m 0,0 c -0.107,0 -5.711,0.023 -5.818,0.023 -6.932,0 -12.549,-11.037 -12.549,-24.636 0,-13.6 5.617,-24.631 12.549,-24.631 0.107,0 5.711,-0.012 5.818,-0.012 6.932,0 12.549,11.027 12.549,24.628 C 12.549,-11.027 6.932,0 0,0"
418 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
419 id="path3853-2-6"
420 inkscape:connector-curvature="0" /></g><g
421 id="g3855-9-1"
422 transform="translate(182.56995,699.52518)"><path
423 d="m 0,0 c 0,-13.6 -5.578,-24.625 -12.457,-24.625 -6.881,0 -12.459,11.025 -12.459,24.625 0,13.6 5.578,24.625 12.459,24.625 C -5.578,24.625 0,13.6 0,0"
424 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
425 id="path3857-0-29"
426 inkscape:connector-curvature="0" /></g><g
427 id="g3859-2-3"
428 transform="translate(170.31995,700.15018)"><path
429 d="m 0,0 c 0,-3.383 -1.68,-6.125 -3.75,-6.125 -2.07,0 -3.75,2.742 -3.75,6.125 0,3.383 1.68,6.125 3.75,6.125 C -1.68,6.125 0,3.383 0,0"
430 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
431 id="path3861-8-1"
432 inkscape:connector-curvature="0" /></g><g
433 id="g3863-3-9"
434 transform="translate(164.94495,706.27518)"><path
435 d="m 0,0 h -10.875 c -1.104,0 -2,-2.742 -2,-6.125 0,-3.383 0.896,-6.125 2,-6.125 H 0 c 1.105,0 2,2.742 2,6.125 C 2,-2.742 1.105,0 0,0"
436 style="fill:#92d5e3;fill-opacity:1;fill-rule:nonzero;stroke:none"
437 id="path3865-8-4"
438 inkscape:connector-curvature="0" /></g><g
439 id="g3867-0-7"
440 transform="translate(310.50745,738.19508)"><path
441 d="m 0,0 c -1,0.008 -4.109,0.84 -4.193,0.832 -12.235,-1.086 -20.323,-9.461 -20.323,-20.127 0,-10.666 8.791,-19.326 19.78,-19.326 0.084,0 3.736,-0.002 4.736,-0.002 z"
442 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
443 id="path3869-4-8"
444 inkscape:connector-curvature="0" /></g><g
445 id="g3871-0-4"
446 transform="translate(329.98595,719.46268)"><path
447 d="m 0,0 c 0,-10.988 -8.908,-19.896 -19.896,-19.896 -10.989,0 -19.897,8.908 -19.897,19.896 0,10.988 8.908,19.896 19.897,19.896 C -8.908,19.896 0,10.988 0,0"
448 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
449 id="path3873-9-5"
450 inkscape:connector-curvature="0" /></g><g
451 id="g3875-1-0"
452 transform="translate(324.86295,719.83768)"><path
453 d="m 0,0 c 0,-7.951 -6.445,-14.398 -14.398,-14.398 -7.952,0 -14.397,6.447 -14.397,14.398 0,7.951 6.445,14.398 14.397,14.398 C -6.445,14.398 0,7.951 0,0"
454 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
455 id="path3877-9-3"
456 inkscape:connector-curvature="0" /></g><g
457 id="g3879-6-6"
458 transform="translate(317.91175,719.71268)"><path
459 d="m 0,0 c 0,-4.182 -3.391,-7.57 -7.572,-7.57 -4.18,0 -7.571,3.388 -7.571,7.57 0,4.182 3.391,7.57 7.571,7.57 C -3.391,7.57 0,4.182 0,0"
460 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
461 id="path3881-2-1"
462 inkscape:connector-curvature="0" /></g><g
463 id="g3883-5-0"
464 transform="translate(255.50746,738.19508)"><path
465 d="m 0,0 c -1,0.008 -4.109,0.84 -4.193,0.832 -12.235,-1.086 -20.323,-9.461 -20.323,-20.127 0,-10.666 8.791,-19.326 19.78,-19.326 0.084,0 3.736,-0.002 4.736,-0.002 z"
466 style="fill:#c22828;fill-opacity:1;fill-rule:nonzero;stroke:none"
467 id="path3885-4-6"
468 inkscape:connector-curvature="0" /></g><g
469 id="g3887-4-3"
470 transform="translate(274.98595,719.46268)"><path
471 d="m 0,0 c 0,-10.988 -8.908,-19.896 -19.896,-19.896 -10.989,0 -19.897,8.908 -19.897,19.896 0,10.988 8.908,19.896 19.897,19.896 C -8.908,19.896 0,10.988 0,0"
472 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
473 id="path3889-9-2"
474 inkscape:connector-curvature="0" /></g><g
475 id="g3891-9-0"
476 transform="translate(269.86295,719.83768)"><path
477 d="m 0,0 c 0,-7.951 -6.445,-14.398 -14.398,-14.398 -7.952,0 -14.397,6.447 -14.397,14.398 0,7.951 6.445,14.398 14.397,14.398 C -6.445,14.398 0,7.951 0,0"
478 style="fill:#b4d9e8;fill-opacity:1;fill-rule:nonzero;stroke:none"
479 id="path3893-3-6"
480 inkscape:connector-curvature="0" /></g><g
481 id="g3895-6-15"
482 transform="translate(262.91176,719.71268)"><path
483 d="m 0,0 c 0,-4.182 -3.391,-7.57 -7.572,-7.57 -4.18,0 -7.571,3.388 -7.571,7.57 0,4.182 3.391,7.57 7.571,7.57 C -3.391,7.57 0,4.182 0,0"
484 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
485 id="path3897-0-5"
486 inkscape:connector-curvature="0" /></g><g
487 id="g3899-5-4"
488 transform="translate(263.16365,669.41089)"><path
489 d="m 0,0 c 0.07,-4.725 -3.701,-8.61 -8.426,-8.68 -4.724,-0.07 -8.609,3.702 -8.679,8.427 -0.071,4.723 3.703,8.608 8.425,8.678 C -3.955,8.495 -0.07,4.724 0,0"
490 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
491 id="path3901-0-7"
492 inkscape:connector-curvature="0" /></g><g
493 id="g3903-2-6"
494 transform="translate(315.69295,670.18828)"><path
495 d="m 0,0 c 0.07,-4.724 -3.701,-8.61 -8.426,-8.68 -4.724,-0.07 -8.609,3.702 -8.679,8.427 -0.071,4.724 3.703,8.609 8.425,8.679 C -3.955,8.495 -0.07,4.724 0,0"
496 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
497 id="path3905-9-5"
498 inkscape:connector-curvature="0" /></g><g
499 id="g3907-4-6"
500 transform="translate(312.55625,663.44898)"><path
501 d="m 0,0 c 0,0 -11.16,-12.342 -32.756,-12.661 -18.803,-0.278 -31.42,12.65 -31.42,12.65 l 12.321,11.853 c 0,0 9.5,-8.029 19.707,-7.822 13.945,0.28 20.337,8.343 20.337,8.343 z"
502 style="fill:#116c7e;fill-opacity:1;fill-rule:nonzero;stroke:none"
503 id="path3909-3-9"
504 inkscape:connector-curvature="0" /></g><g
505 id="g3911-5-3"
506 transform="translate(263.97425,672.23218)"><path
507 d="m 0,0 c 0,-4.788 -3.881,-8.67 -8.668,-8.67 -4.789,0 -8.67,3.882 -8.67,8.67 0,4.787 3.881,8.668 8.67,8.668 C -3.881,8.668 0,4.787 0,0"
508 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
509 id="path3913-1-7"
510 inkscape:connector-curvature="0" /></g><g
511 id="g3915-7-45"
512 transform="translate(317.21446,672.23218)"><path
513 d="m 0,0 c 0,-4.788 -3.881,-8.67 -8.668,-8.67 -4.787,0 -8.668,3.882 -8.668,8.67 0,4.787 3.881,8.668 8.668,8.668 C -3.881,8.668 0,4.787 0,0"
514 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
515 id="path3917-4-2"
516 inkscape:connector-curvature="0" /></g><g
517 id="g3919-3-5"
518 transform="translate(313.93715,665.44898)"><path
519 d="m 0,0 c 0,0 -11.496,-12.338 -33.383,-12.338 -19.058,0 -31.65,13.291 -31.65,13.291 l 12.662,11.824 c 0,0 9.508,-8.277 19.853,-8.222 14.135,0.076 20.735,8.15 20.735,8.15 z"
520 style="fill:#ed2d26;fill-opacity:1;fill-rule:nonzero;stroke:none"
521 id="path3921-1-4"
522 inkscape:connector-curvature="0" /></g><path
523 d="m 284.98045,658.63318 h -4.18 v 5.349 h 4.18 z"
524 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994"
525 id="path3923-4-7"
526 inkscape:connector-curvature="0" /><g
527 id="g3925-6-4"
528 transform="translate(277.71055,658.90608)"><path
529 d="M 0,0 -4.076,0.926 -2.893,6.143 1.186,5.217 Z"
530 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
531 id="path3927-9-4"
532 inkscape:connector-curvature="0" /></g><g
533 id="g3929-4-3"
534 transform="translate(270.94885,660.60228)"><path
535 d="m 0,0 -3.895,1.521 1.948,4.982 3.892,-1.52 z"
536 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
537 id="path3931-2-0"
538 inkscape:connector-curvature="0" /></g><g
539 id="g3933-2-7"
540 transform="translate(264.62656,663.14819)"><path
541 d="M 0,0 -3.727,1.894 -1.305,6.662 2.422,4.769 Z"
542 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
543 id="path3935-6-8"
544 inkscape:connector-curvature="0" /></g><g
545 id="g3937-4-6"
546 transform="translate(258.42346,666.48028)"><path
547 d="M 0,0 -3.383,2.456 -0.24,6.783 3.143,4.327 Z"
548 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
549 id="path3939-1-8"
550 inkscape:connector-curvature="0" /></g><path
551 d="m 280.80045,658.63318 h 4.18 v 5.349 h -4.18 z"
552 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994"
553 id="path3941-2-8"
554 inkscape:connector-curvature="0" /><g
555 id="g3943-8-4"
556 transform="translate(288.06796,658.90608)"><path
557 d="M 0,0 4.076,0.926 2.895,6.143 -1.184,5.217 Z"
558 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
559 id="path3945-8-3"
560 inkscape:connector-curvature="0" /></g><g
561 id="g3947-92-1"
562 transform="translate(294.83165,660.60228)"><path
563 d="M 0,0 3.895,1.521 1.947,6.503 -1.947,4.983 Z"
564 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
565 id="path3949-8-4"
566 inkscape:connector-curvature="0" /></g><g
567 id="g3951-8-9"
568 transform="translate(301.15396,663.14819)"><path
569 d="M 0,0 3.727,1.894 1.305,6.662 -2.422,4.769 Z"
570 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
571 id="path3953-8-2"
572 inkscape:connector-curvature="0" /></g><g
573 id="g3955-6-0"
574 transform="translate(307.35706,666.48028)"><path
575 d="M 0,0 3.383,2.456 0.24,6.783 -3.143,4.327 Z"
576 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
577 id="path3957-8-6"
578 inkscape:connector-curvature="0" /></g><g
579 id="g3959-3-8"
580 transform="translate(332.27115,770.19119)"><path
581 d="m 0,0 v -3.166 c 0,0 6.918,0.75 11.334,-3.666 4.416,-4.417 4,-10.668 4,-10.668 h 3 c 0,0 1.334,9.668 -3.834,14.168 C 9.334,1.168 0,0 0,0"
582 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
583 id="path3961-8-9"
584 inkscape:connector-curvature="0" /></g></g><g
585 transform="matrix(0.19045745,0,0,-0.19045745,-58.039109,104.03018)"
586 id="layer1-3"><g
587 transform="matrix(4.4799,0,0,2.9866,-22.373,-404.56)"
588 id="g3953"
589 style="stroke:#000000"><rect
590 x="79.462997"
591 y="291.39001"
592 width="88.001999"
593 height="26.205999"
594 id="rect2953"
595 style="fill:#ffcc00;stroke-width:1.5" /><g
596 transform="matrix(1,0,0,0.76435,0.78697,68.514)"
597 id="g3915-9"><rect
598 x="81.922997"
599 y="291.14001"
600 width="1.8688999"
601 height="19.539"
602 id="rect3725"
603 style="stroke-width:0.26172" /><rect
604 x="92.504997"
605 y="291.10001"
606 width="1.9425"
607 height="9.7123003"
608 id="rect3727"
609 style="stroke-width:0.18811999" /><rect
610 x="87.195999"
611 y="291.10001"
612 width="1.9425"
613 height="9.7123003"
614 id="rect3729"
615 style="stroke-width:0.18811999" /><rect
616 x="97.814003"
617 y="291.10001"
618 width="1.9425"
619 height="9.7123003"
620 id="rect3731"
621 style="stroke-width:0.18811999" /><rect
622 x="103.12"
623 y="291.10001"
624 width="1.9425"
625 height="9.7123003"
626 id="rect3733"
627 style="stroke-width:0.18811999" /><rect
628 x="108.47"
629 y="291.14001"
630 width="1.8688999"
631 height="19.539"
632 id="rect3765"
633 style="stroke-width:0.26172" /><rect
634 x="119.05"
635 y="291.10001"
636 width="1.9425"
637 height="9.7123003"
638 id="rect3767"
639 style="stroke-width:0.18811999" /><rect
640 x="113.74"
641 y="291.10001"
642 width="1.9425"
643 height="9.7123003"
644 id="rect3769"
645 style="stroke-width:0.18811999" /><rect
646 x="124.36"
647 y="291.10001"
648 width="1.9425"
649 height="9.7123003"
650 id="rect3771"
651 style="stroke-width:0.18811999" /><rect
652 x="129.67"
653 y="291.10001"
654 width="1.9425"
655 height="9.7123003"
656 id="rect3773"
657 style="stroke-width:0.18811999" /><rect
658 x="135.00999"
659 y="291.14001"
660 width="1.8688999"
661 height="19.539"
662 id="rect3779"
663 style="stroke-width:0.26172" /><rect
664 x="145.60001"
665 y="291.10001"
666 width="1.9425"
667 height="9.7123003"
668 id="rect3781"
669 style="stroke-width:0.18811999" /><rect
670 x="140.28999"
671 y="291.10001"
672 width="1.9425"
673 height="9.7123003"
674 id="rect3783"
675 style="stroke-width:0.18811999" /><rect
676 x="150.91"
677 y="291.10001"
678 width="1.9425"
679 height="9.7123003"
680 id="rect3785"
681 style="stroke-width:0.18811999" /><rect
682 x="156.21001"
683 y="291.10001"
684 width="1.9425"
685 height="9.7123003"
686 id="rect3787"
687 style="stroke-width:0.18811999" /><rect
688 x="161.56"
689 y="291.14001"
690 width="1.8688999"
691 height="19.539"
692 id="rect3907"
693 style="stroke-width:0.26172" /></g></g></g><g
694 transform="matrix(0,0.19045745,0.19045745,0,-8.0301814,-48.206035)"
695 id="layer1-3-4"><g
696 transform="matrix(4.4799,0,0,2.9866,-22.373,-404.56)"
697 id="g3953-8"
698 style="stroke:#000000"><rect
699 x="79.462997"
700 y="291.39001"
701 width="88.001999"
702 height="26.205999"
703 id="rect2953-1"
704 style="fill:#ffcc00;stroke-width:1.5" /><g
705 transform="matrix(1,0,0,0.76435,0.78697,68.514)"
706 id="g3915-9-2"><rect
707 x="81.922997"
708 y="291.14001"
709 width="1.8688999"
710 height="19.539"
711 id="rect3725-9"
712 style="stroke-width:0.26172" /><rect
713 x="92.504997"
714 y="291.10001"
715 width="1.9425"
716 height="9.7123003"
717 id="rect3727-3"
718 style="stroke-width:0.18811999" /><rect
719 x="87.195999"
720 y="291.10001"
721 width="1.9425"
722 height="9.7123003"
723 id="rect3729-9"
724 style="stroke-width:0.18811999" /><rect
725 x="97.814003"
726 y="291.10001"
727 width="1.9425"
728 height="9.7123003"
729 id="rect3731-0"
730 style="stroke-width:0.18811999" /><rect
731 x="103.12"
732 y="291.10001"
733 width="1.9425"
734 height="9.7123003"
735 id="rect3733-8"
736 style="stroke-width:0.18811999" /><rect
737 x="108.47"
738 y="291.14001"
739 width="1.8688999"
740 height="19.539"
741 id="rect3765-8"
742 style="stroke-width:0.26172" /><rect
743 x="119.05"
744 y="291.10001"
745 width="1.9425"
746 height="9.7123003"
747 id="rect3767-5"
748 style="stroke-width:0.18811999" /><rect
749 x="113.74"
750 y="291.10001"
751 width="1.9425"
752 height="9.7123003"
753 id="rect3769-0"
754 style="stroke-width:0.18811999" /><rect
755 x="124.36"
756 y="291.10001"
757 width="1.9425"
758 height="9.7123003"
759 id="rect3771-9"
760 style="stroke-width:0.18811999" /><rect
761 x="129.67"
762 y="291.10001"
763 width="1.9425"
764 height="9.7123003"
765 id="rect3773-6"
766 style="stroke-width:0.18811999" /><rect
767 x="135.00999"
768 y="291.14001"
769 width="1.8688999"
770 height="19.539"
771 id="rect3779-3"
772 style="stroke-width:0.26172" /><rect
773 x="145.60001"
774 y="291.10001"
775 width="1.9425"
776 height="9.7123003"
777 id="rect3781-8"
778 style="stroke-width:0.18811999" /><rect
779 x="140.28999"
780 y="291.10001"
781 width="1.9425"
782 height="9.7123003"
783 id="rect3783-5"
784 style="stroke-width:0.18811999" /><rect
785 x="150.91"
786 y="291.10001"
787 width="1.9425"
788 height="9.7123003"
789 id="rect3785-6"
790 style="stroke-width:0.18811999" /><rect
791 x="156.21001"
792 y="291.10001"
793 width="1.9425"
794 height="9.7123003"
795 id="rect3787-1"
796 style="stroke-width:0.18811999" /><rect
797 x="161.56"
798 y="291.14001"
799 width="1.8688999"
800 height="19.539"
801 id="rect3907-1"
802 style="stroke-width:0.26172" /></g></g></g></g></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <?php // Silence is golden
...\ No newline at end of file ...\ No newline at end of file
1
2 // interface for emr.
3 var emrIf = function ($)
4 {
5 var source_type;
6 var source_is_image;
7 var target_type;
8 var target_is_image;
9
10 var is_debug = false;
11 var is_dragging = false;
12
13 this.init = function()
14 {
15
16 if ( emr_options.is_debug)
17 {
18 this.is_debug = true;
19 this.debug('EMR Debug is active');
20 }
21
22 $('input[name="timestamp_replace"]').on('change', $.proxy(this.checkCustomDate, this));
23 $('input[name="replace_type"]').on('change', $.proxy(this.showReplaceOptions, this));
24 $('input[name="userfile"]').on('change', $.proxy(this.handleImage, this));
25 $('.replace_custom_date').on('click', $.proxy(this.updateCustomDate, this));
26
27 // DragDrop
28 //$(document).on('dragover', $.proxy(this.dragOverArea, this));
29 //$(document).on('dragleave', $.proxy(this.dragOutArea, this));
30 document.addEventListener('dragover', this.dragOverArea.bind(this), false );
31 document.addEventListener('dragleave', this.dragOutArea.bind(this), false );
32
33
34
35 $('.emr_drop_area').on('drop', $.proxy(this.fileDrop, this));
36 $('.upload-file-action').on('click', function () {
37 var input = document.getElementById('upload-file').click();
38 });
39
40 this.checkCustomDate();
41 this.loadDatePicker();
42
43 var source = $('.image_placeholder').first();
44 if (typeof( $(source).data('filetype') ) !== 'undefined')
45 {
46 source_type = $(source).data('filetype').trim();
47 this.debug('detected type - ' + source_type);
48 }
49 else
50 source_type = ''; // error state
51
52 if (source.hasClass('is_image'))
53 {
54 source_is_image = true;
55 }
56
57 this.updateTextLayer(source, false);
58 this.showReplaceOptions();
59
60
61 }
62
63 this.loadDatePicker = function()
64 {
65 $('#emr_datepicker').datepicker({
66 dateFormat: emr_options.dateFormat,
67 onClose: function() {
68 var date = $(this).datepicker( 'getDate' );
69 if (date) {
70 var formattedDate = (date.getFullYear()) + "-" +
71 (date.getMonth()+1) + "-" +
72 date.getDate();
73 $('input[name="custom_date_formatted"]').val(formattedDate);
74 //$('input[name="custom_date"]').val($.datepicker.parseDate( emr_options.dateFormat, date));
75 }
76 },
77 });
78 }
79
80 this.checkCustomDate = function()
81 {
82 if ($('input[name="timestamp_replace"]:checked').val() == 3)
83 this.showCustomDate();
84 else
85 this.hideCustomDate();
86 }
87 this.showCustomDate = function()
88 {
89 $('.custom_date').css('visibility', 'visible').fadeTo(100, 1);
90 }
91 this.hideCustomDate = function()
92 {
93 $('.custom_date').fadeTo(100,0,
94 function ()
95 {
96 $('.custom_date').css('visibility', 'hidden');
97 });
98 }
99 this.handleImage = function(e)
100 {
101 this.toggleErrors(false);
102 var target = e.target;
103 var file = target.files[0];
104
105 if (! target.files || target.files.length <= 0) // FileAPI appears to be not present, handle files on backend.
106 {
107 if ($('input[name="userfile"]').val().length > 0)
108 this.checkSubmit();
109 console.log('FileAPI not detected');
110 return false;
111 }
112
113 var status = this.checkUpload(file);
114 this.debug('check upload status ' + status);
115 this.debug('file size:' + file.size);
116
117 if (status)
118 {
119 this.updatePreview(file);
120 }
121 else {
122 this.updatePreview(null);
123 }
124 this.checkSubmit();
125 }
126 this.updatePreview = function(file)
127 {
128 var preview = $('.image_placeholder').last();
129
130 $(preview).find('img').remove();
131 $(preview).removeClass('is_image not_image is_document');
132 var is_empty = false;
133
134 if (file !== null) /// file is null when empty, or error
135 {
136 target_is_image = (file.type.indexOf('image') >= 0) ? true : false;
137 target_type = file.type.trim();
138 }
139 else
140 {
141 is_empty = true;
142 }
143 // If image, load thumbnail and get dimensions.
144 if (file && target_is_image)
145 {
146 var img = new Image();
147 img.src = window.URL.createObjectURL(file);
148 self = this;
149
150 img.addEventListener("load", function () {
151 // with formats like svg it can be rough.
152
153 var width = img.naturalWidth;
154 var height = img.naturalHeight;
155
156 if (width == 0)
157 width = img.width;
158 if (height == 0)
159 height = img.height;
160
161 img.setAttribute('style', 'z-index:2; position: relative; max-width:100%; max-height: 100%; width: ' + width + 'px; height: ' + height + 'px;');
162
163 self.updateTextLayer(preview, width + ' x ' + height);
164 self.updateFileSize(preview, file);
165 });
166
167 $(preview).prepend(img);
168 $(preview).addClass('is_image');
169 }
170 else if(file === null)
171 {
172 $(preview).addClass('not_image');
173 $(preview).find('.dashicons').removeClass().addClass('dashicons dashicons-no');
174 //$(preview).find('.textlayer').text('');
175 this.updateTextLayer(preview, '');
176 this.updateFileSize(preview, null);
177 this.debug('File is null');
178 }
179 else { // not an image
180 $(preview).addClass('not_image is_document');
181 $(preview).find('.dashicons').removeClass().addClass('dashicons dashicons-media-document');
182 //$(preview).find('.textlayer').text(file.name);
183 this.updateTextLayer(preview, file.name);
184 this.updateFileSize(preview, file);
185 this.debug('Not image, media document');
186 }
187
188 if (! is_empty && target_type != source_type)
189 {
190 this.debug(target_type + ' not ' + source_type);
191 var falsePositive = this.checkFalsePositiveType(source_type, target_type);
192 if (! falsePositive)
193 this.warningFileType(source_type, target_type);
194 }
195
196 if (! is_empty && emr_options.allowed_mime.indexOf(target_type) == -1)
197 {
198 this.debug(target_type + ' not ' + ' in allowed types ');
199 var falsePositive = this.checkFalsePositiveType(source_type, target_type);
200
201 if (! falsePositive)
202 this.warningMimeType();
203 }
204 // this.debug(emr_options.allowed_mime);
205
206 }
207 this.checkFalsePositiveType = function(source_type, target_type)
208 {
209 // windows (sigh) reports application/zip as application/x-zip-compressed. Or something else, why not.
210 if (source_type.indexOf('zip') >= 0 && target_type.indexOf('zip') >= 0)
211 {
212 this.debug('Finding ' + source_type + ' ' + target_type + ' close enough, false positive');
213 return true;
214 }
215 return false;
216 }
217 // replace the text, check if text is there ( or hide ), and fix the layout.
218 this.updateTextLayer = function (preview, newtext)
219 {
220 textlayer = $(preview).find('.textlayer');
221 textlayer.css('opacity', '0');
222 if (newtext !== false)
223 textlayer.text(newtext);
224
225 if (textlayer.text() !== '')
226 {
227 textlayer.css('opacity', '0.7');
228 // textlayer.css('margin-left', '-' + (textlayer.width() / 2 ) + 'px');
229 }
230
231 }
232 this.updateFileSize = function(preview, file)
233 {
234 if (file === null)
235 {
236 $(preview).find('.image_size').text('');
237 return;
238 }
239 var bytes = file.size;
240 if (bytes == 0) { return "0.00 B"; }
241 var e = Math.floor(Math.log(bytes) / Math.log(1024));
242 var size = (bytes/Math.pow(1024, e)).toFixed(2)+' '+' KMGTP'.charAt(e)+'B';
243
244 $(preview).find('.image_size').text(size);
245 }
246 this.updateCustomDate = function(e)
247 {
248 var $target = $(e.target);
249 var min = $target.data('min');
250 var hour = $target.data('hour');
251 var date = $target.data('date');
252 var format = $target.data('format');
253
254 $('input[name="custom_date"]').val(date);
255 $('input[name="custom_hour"]').val(hour);
256 $('input[name="custom_minute"]').val(min);
257 $('input[name="custom_date_formatted"]').val(format);
258 }
259 this.checkSubmit = function()
260 {
261 var check = ($('input[name="userfile"]').val().length > 0) ? true : false;
262
263 if (check)
264 {
265 $('input[type="submit"]').prop('disabled', false);
266 }
267 else {
268 $('input[type="submit"]').prop('disabled', true);
269 }
270 }
271 this.toggleErrors = function(toggle)
272 {
273 $('.form-error').fadeOut();
274 $('.form-warning').fadeOut();
275 }
276 this.checkUpload = function(fileItem)
277 {
278 var maxsize = emr_options.maxfilesize;
279
280 if ($('input[name="userfile"]').val().length <= 0)
281 {
282 console.info('[EMR] - Upload file value not set in form. Pick a file');
283 $('input[name="userfile"]').val('');
284 return false;
285 }
286
287 if (fileItem.size > maxsize)
288 {
289 console.info('[EMR] - File too big for uploading - exceeds upload limits');
290 this.errorFileSize(fileItem);
291 $('input[name="userfile"]').val('');
292 return false;
293 }
294 return true;
295 }
296 this.errorFileSize = function(fileItem)
297 {
298 $('.form-error.filesize').find('.fn').text(fileItem.name);
299 $('.form-error.filesize').fadeIn();
300 }
301 this.warningFileType = function(source_type, target_type)
302 {
303 $('.form-warning.filetype').find('.source_type').text(source_type);
304 $('.form-warning.filetype').find('.target_type').text(target_type);
305 $('.form-warning.filetype').fadeIn();
306 }
307 this.warningMimeType = function(fileItem)
308 {
309 $('.form-warning.mimetype').fadeIn();
310 }
311 this.debug = function(message)
312 {
313 console.debug(message);
314 }
315 this.showReplaceOptions = function(e)
316 {
317 $('section.options .location_option').hide();
318 var replace_option = $('input[name="replace_type"]:checked').val();
319 if (replace_option == 'replace_and_search')
320 {
321 $('section.options .location_option').show();
322 }
323
324 }
325 this.dragOverArea = function(e)
326 {
327 e.preventDefault();
328 e.stopPropagation();
329 console.log(e);
330
331 if (true == this.is_dragging)
332 return;
333
334 var el = document.getElementById('emr-drop-area');
335 var showEl = el.cloneNode(true);
336 showEl.id = 'emr-drop-area-active';
337
338 var child = document.body.appendChild(showEl);
339
340 child.addEventListener('drop', this.fileDrop.bind(this), false);
341
342 child.addEventListener('dragover', function(event){
343 event.preventDefault();
344 })
345
346
347 this.is_dragging = true;
348 }
349
350 this.dragOutArea = function(e)
351 {
352
353 // event is not passed on filedrop. remove overlay then.
354 if (typeof e !== 'undefined')
355 {
356 e.preventDefault();
357 e.stopPropagation();
358
359 if (e.clientX != 0 || e.clientY != 0) {
360 return false;
361 }
362 }
363 var removeEl = document.getElementById('emr-drop-area-active');
364 if (removeEl !== null)
365 document.getElementById('emr-drop-area-active').remove();
366
367 this.is_dragging = false;
368 }
369 this.fileDrop = function (e)
370 {
371 // var ev = e.originalEvent;
372 this.dragOutArea();
373 //ev.preventDefault();
374 e.stopPropagation();
375 e.preventDefault();
376
377 if (e.dataTransfer.items) {
378 // Use DataTransferItemList interface to access the file(s)
379 document.getElementById('upload-file').files = e.dataTransfer.files;
380 $('input[name="userfile"]').trigger('change');
381 }
382 }
383 } // emrIf
384
385 jQuery(document).ready(function($)
386 {
387 window.enableMediaReplace = new emrIf($);
388 window.enableMediaReplace.init();
389 });
390
391
392 function emrDelayedInit() {
393 console.log('Checking delayed init ');
394 if(typeof window.enableMediaReplace == "undefined") {
395 console.log(emrIf);
396 window.enableMediaReplace = new emrIf(jQuery);
397 window.enableMediaReplace.init();
398 }
399 else if (typeof window.enableMediaReplace !== 'undefined')
400 {
401 // All fine.
402 }
403 else { // Nothing yet, try again.
404 setTimeout(emrdelayedInit, 3000);
405 }
406 }
407 setTimeout(emrDelayedInit, 3000);
1 window.addEventListener('load', function(event) {
2
3 var url = new URL(window.location.href);
4 url.searchParams.set('emr_success', 1);
5
6 var timeout = 10;
7 if (emr_success_options.timeout)
8 timeout = emr_success_options.timeout;
9 var counter = document.getElementById('redirect_counter');
10 var redirectUrl = document.getElementById('redirect_url');
11 var redirected = false;
12
13 counter.textContent = timeout;
14
15 var t = window.setInterval(function () {
16 counter.textContent = timeout;
17 timeout--;
18 if (timeout <= 0 && false == redirected)
19 {
20 window.location.href = redirectUrl;
21 redirected = true;
22 window.clearInterval(t);
23 }
24 }, 1000);
25
26 });
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 $('h1.response').remove();
52 $('#overlay').css('visibility', 'visible');
53 var preview = $('.image_placeholder').last();
54 preview.find('img').remove();
55 // $('#preview-area').hide();
56 },
57 success: function (response) {
58 var preview = $('.image_placeholder').last();
59
60 if (response.success) {
61
62
63 $('#overlay').css('visibility', 'hidden');
64 preview.find('img').remove();
65 preview.removeClass('is_image not_image is_document');
66
67 $('#replace_image_button').prop('disabled', false);
68
69 var img = new Image();
70 img.src = response.image;
71 img.setAttribute('style', 'height: inherit;');
72
73 preview.prepend(img);
74 // preview.removeClass('not_image');
75 preview.addClass('is_image');
76
77 $('input[name="key"]').val(response.key);
78 $('input[type=radio][name=background_type]').attr('disabled', false);
79 $('input[type=radio][name=compression_level]').attr('disabled', false);
80 $('#remove_background_button').attr('disabled', false);
81
82 var badBg = document.getElementById('bad-background-link');
83 var href = badBg.dataset.link;
84 href = href.replace('{url}', response.url);
85 href = href.replace('{settings}', response.settings);
86
87 badBg.setAttribute('href', href);
88
89 badBg.style.visibility = 'visible';
90 /* $('#removed_image').html(`
91 <div class="img-comp-container">
92 <div class="img-comp-img">
93 <img src="${image}" width="${width}" height="${height}" />
94 </div>
95
96 </div>
97 `); */
98 // initComparisons();
99 }else{
100
101 preview.prepend(`<h1 class='response'>${response.message}</h1>`);
102 $('#remove_background_button').attr('disabled', false)
103 $('input[type=radio][name=background_type]').attr('disabled', false)
104 $('input[type=radio][name=compression_level]').attr('disabled', false)
105 $('#overlay').css('visibility', 'hidden');
106 //$('#preview-area').show();
107 }
108 }
109 })
110 });
111
112 function backgroundInputs () {
113 const bgInputs = $('#solid_selecter');
114 var input = $('input[type=radio][name=background_type]:checked');
115 if (input.val() === 'solid') {
116 bgInputs.show();
117 } else {
118 bgInputs.hide();
119 }
120
121 };
122
123 $('#bg_display_picker').on('input', function () {
124 $('#color_range').html($(this).val());
125 $('#bg_color').val($(this).val());
126 });
127
128 function transparancyOptions() {
129 $('#transparency_range').html($('#bg_transparency').val());
130 };
131
132 });
1 jQuery(document).ready(function($)
2 {
3 $('.emr-installer').on('click', function(e){
4 e.preventDefault();
5 var button = $(this);
6 var plugin = button.data('plugin');
7 var nonce = $('#upsell-nonce').val();
8
9 button.text(emr_upsell.installing);
10
11 var enr_eg_opts = {
12 url: emr_upsell.ajax,
13 type: 'post',
14 async: true,
15 cache: false,
16 dataType: 'json',
17 data: {
18 action: 'emr_plugin_install',
19 nonce: nonce,
20 plugin: plugin //'https://downloads.wordpress.org/plugin/envira-gallery-lite.zip',
21 },
22 success: function(response) {
23 $(button).addClass('hidden');
24
25 $('.emr-activate[data-plugin="' + plugin + '"]').removeClass('hidden');
26
27 },
28 error: function(xhr, textStatus, e) {
29 },
30 };
31
32 $.ajax(enr_eg_opts);
33 });
34
35 $('.emr-activate').on('click', function(e){
36 e.preventDefault();
37
38 var button = $(this);
39 var plugin = button.data('plugin');
40 var nonce = $('#upsell-nonce-activate').val();
41
42 var enr_eg_opts = {
43 url: emr_upsell.ajax,
44 type: 'post',
45 async: true,
46 cache: false,
47 dataType: 'json',
48 data: {
49 action: 'emr_plugin_activate',
50 nonce: nonce,
51 plugin: plugin,
52 },
53 success: function(response) {
54 $(button).addClass('hidden')
55 $('.emr-activate-done[data-plugin="' + plugin + '"]').removeClass('hidden');
56
57 },
58 error: function(xhr, textStatus, e) {
59 },
60 };
61 $.ajax(enr_eg_opts);
62 });
63
64 });
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2010-09-13 14:57+0100\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: Michael Bering Petersen <michaelbering@gmail.com>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Poedit-SearchPath-0: .\n"
15
16 #: enable-media-replace.php:40
17 #: enable-media-replace.php:68
18 msgid "Replace media"
19 msgstr "Udskift media"
20
21 #: enable-media-replace.php:68
22 msgid "Upload a new file"
23 msgstr "Upload en ny fil"
24
25 #: enable-media-replace.php:68
26 msgid "To replace the current file, click the link and upload a replacement."
27 msgstr "Hvis du ønsker at udskifte den aktuelle fil - klik på linket og upload den nye fil."
28
29 #: popup.php:14
30 #: upload.php:21
31 msgid "You do not have permission to upload files."
32 msgstr "Du har ikke tilladelse til at uploade filer."
33
34 #: popup.php:30
35 msgid "Replace Media Upload"
36 msgstr "Replace Media Upload"
37
38 #: popup.php:41
39 msgid "NOTE: You are about to replace the media file"
40 msgstr "OBS: Du er nu ved at overskrive filen,"
41
42 #: popup.php:41
43 msgid "There is no undo. Think about it!"
44 msgstr "Der er ingen mulighed for at fortryde, så tænkt dig godt om inden du accepterer."
45
46 #: popup.php:43
47 msgid "Choose a file to upload from your computer"
48 msgstr "Vælg den fil du ønsker at uploade fra din computer."
49
50 #: popup.php:47
51 msgid "Select media replacement type:"
52 msgstr "Vælg media type:"
53
54 #: popup.php:49
55 msgid "Just replace the file"
56 msgstr "Udskift filen"
57
58 #: popup.php:50
59 msgid "Note: This option requires you to upload a file of the same type ("
60 msgstr "OBS: Denne mulighed kræver at du uploader en fil af samme type ("
61
62 #: popup.php:50
63 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
64 msgstr ") som den du overskriver. Navnet på filen vil forblive det samme ("
65
66 #: popup.php:50
67 msgid ") no matter what the file you upload is called."
68 msgstr ") uanset hvad den fil du uploader hedder."
69
70 #: popup.php:52
71 msgid "Replace the file, use new file name and update all links"
72 msgstr "Overskriv filen, brug det nye fil navn og opdater alle links-"
73
74 #: popup.php:53
75 msgid "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 ("
76 msgstr "OBS: Hvis du vælger denne mulighed - vil filnavnet og filtypen som du er ved at uploade overskrive den gamle fil. Alle links der peger på den nuværende fil ("
77
78 #: popup.php:53
79 msgid ") will be updated to point to the new file name."
80 msgstr ") vil blive opdateret så de peger på den nye fil."
81
82 #: popup.php:55
83 msgid "Upload"
84 msgstr "Upload"
85
86 #: popup.php:55
87 msgid "Cancel"
88 msgstr "Fortryd"
89
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2010-09-13 14:57+0100\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: Martin Lettner <m.lettner@gmail.com>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Poedit-SearchPath-0: .\n"
15
16 #: enable-media-replace.php:40
17 #: enable-media-replace.php:68
18 msgid "Replace media"
19 msgstr "Datei ersetzen"
20
21 #: enable-media-replace.php:68
22 msgid "Upload a new file"
23 msgstr "Eine neue Datei hochladen"
24
25 #: enable-media-replace.php:68
26 msgid "To replace the current file, click the link and upload a replacement."
27 msgstr "Eine neue Datei hochladen, die die aktuelle ersetzen soll."
28
29 #: popup.php:14
30 #: upload.php:21
31 msgid "You do not have permission to upload files."
32 msgstr "Sie haben nicht die benötigten Rechte um Dateien hochzuladen."
33
34 #: popup.php:30
35 msgid "Replace Media Upload"
36 msgstr "Medien-Datei ersetzen"
37
38 #: popup.php:41
39 msgid "NOTE: You are about to replace the media file"
40 msgstr "HINWEIS: Sie sind dabei, eine Datei in der Mediathek zu ersetzen"
41
42 #: popup.php:41
43 msgid "There is no undo. Think about it!"
44 msgstr "Diese Aktion kann nicht rückgängig gemacht werden!"
45
46 #: popup.php:43
47 msgid "Choose a file to upload from your computer"
48 msgstr "Datei vom Computer auswählen:"
49
50 #: popup.php:47
51 msgid "Select media replacement type:"
52 msgstr "Wählen Sie die gewünschte Aktion:"
53
54 #: popup.php:49
55 msgid "Just replace the file"
56 msgstr "Datei einfach ersetzen"
57
58 #: popup.php:50
59 msgid "Note: This option requires you to upload a file of the same type ("
60 msgstr "Hinweis: Die Datei muss vom selben Dateityp sein ("
61
62 #: popup.php:50
63 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
64 msgstr ") wie die Datei, die Sie ersetzen. Der Name der der Datei bleibt erhalten ("
65
66 #: popup.php:50
67 msgid ") no matter what the file you upload is called."
68 msgstr "), ganz egal wie die neue Datei heißt."
69
70 #: popup.php:52
71 msgid "Replace the file, use new file name and update all links"
72 msgstr "Datei ersetzen, aber neuen Dateinamen verwenden und alle Links automatisch aktualisieren"
73
74 #: popup.php:53
75 msgid "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 ("
76 msgstr "Hinweis: Der Name der neuen Datei wird die alte ersetzen. Alle Verweise auf die aktuelle Datei ("
77
78 #: popup.php:53
79 msgid ") will be updated to point to the new file name."
80 msgstr ") werden auf die neue aktualisiert."
81
82 #: popup.php:55
83 msgid "Upload"
84 msgstr "Hochladen"
85
86 #: popup.php:55
87 msgid "Cancel"
88 msgstr "Abbrechen"
89
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: Enable Media Replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2011-03-24 10:57+0100\n"
6 "PO-Revision-Date: 2011-03-24 11:41+0100\n"
7 "Last-Translator: François Collette <francois.collette@gmail.com>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: _e;__\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Poedit-SearchPath-0: .\n"
15
16 #: enable-media-replace.php:39
17 #: enable-media-replace.php:67
18 msgid "Replace media"
19 msgstr "Remplacer le média"
20
21 #: enable-media-replace.php:67
22 msgid "Upload a new file"
23 msgstr "Choisir le nouveau fichier"
24
25 #: enable-media-replace.php:67
26 msgid "To replace the current file, click the link and upload a replacement."
27 msgstr "Pour remplacer le fichier actuel, cliquez ci-dessus et choisissez le nouveau fichier."
28
29 #: popup.php:14
30 #: upload.php:3
31 msgid "You do not have permission to upload files."
32 msgstr "Vous n'avez pas la permission d'envoyer des fichiers."
33
34 #: popup.php:30
35 msgid "Replace Media Upload"
36 msgstr "Remplacement de média"
37
38 #: popup.php:46
39 msgid "NOTE: You are about to replace the media file"
40 msgstr "ATTENTION : vous vous apprêtez à remplacer le fichier"
41
42 #: popup.php:46
43 msgid "There is no undo. Think about it!"
44 msgstr "Cette opération est irréversible. Soyez sûr(e) de vous !"
45
46 #: popup.php:48
47 msgid "Choose a file to upload from your computer"
48 msgstr "Envoyez un fichier depuis votre ordinateur :"
49
50 #: popup.php:52
51 msgid "Select media replacement type:"
52 msgstr "Choisissez la méthode de remplacement :"
53
54 #: popup.php:54
55 msgid "Just replace the file"
56 msgstr "Remplacer le fichier seulement"
57
58 #: popup.php:55
59 msgid "Note: This option requires you to upload a file of the same type ("
60 msgstr "Avec cette option, vous devez choisir un fichier du même type ("
61
62 #: popup.php:55
63 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
64 msgstr ") que le précédent. Le nom du fichier actuel ("
65
66 #: popup.php:55
67 msgid ") no matter what the file you upload is called."
68 msgstr ") sera conservé, quel que soit le nom de votre nouveau fichier."
69
70 #: popup.php:57
71 msgid "Replace the file, use new file name and update all links"
72 msgstr "Remplacer le fichier, utiliser le nouveau nom de fichier et mettre à jour tous les liens"
73
74 #: popup.php:58
75 msgid "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 ("
76 msgstr "Avec cette option, le nom et le type de votre nouveau fichier vont remplacer ceux du fichier actuel. Tous les liens vers celui-ci ("
77
78 #: popup.php:58
79 msgid ") will be updated to point to the new file name."
80 msgstr ") seront mis à jour selon le nouveau nom du fichier."
81
82 #: popup.php:60
83 msgid "Upload"
84 msgstr "Envoyer"
85
86 #: popup.php:60
87 msgid "Cancel"
88 msgstr "Annuler"
89
90 #: upload.php:35
91 msgid "File type does not meet security guidelines. Try another."
92 msgstr "Ce type de fichier est incompatible avec les règles de sécurité. Essayez-en un autre."
93
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: Enable Media Replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2011-03-24 10:57+0100\n"
6 "PO-Revision-Date: 2012-11-27 12:03+0100\n"
7 "Last-Translator: Marco <marco@blackstudio.it>\n"
8 "Language-Team: Black Studio <info@blackstudio.it>\n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: _e;__\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Poedit-Language: Italian\n"
15 "X-Poedit-Country: ITALY\n"
16 "X-Poedit-SearchPath-0: .\n"
17
18 #: enable-media-replace.php:39
19 #: enable-media-replace.php:67
20 msgid "Replace media"
21 msgstr "Sostituisci file"
22
23 #: enable-media-replace.php:67
24 msgid "Upload a new file"
25 msgstr "Carica un nuovo file"
26
27 #: enable-media-replace.php:67
28 msgid "To replace the current file, click the link and upload a replacement."
29 msgstr "Per sostituire il file corrente, clicca il link e carica un file sostitutivo."
30
31 #: popup.php:14
32 #: upload.php:3
33 msgid "You do not have permission to upload files."
34 msgstr "Non hai sufficienti permessi per caricare file."
35
36 #: popup.php:30
37 msgid "Replace Media Upload"
38 msgstr "Caricamento file sostitutivo"
39
40 #: popup.php:46
41 msgid "NOTE: You are about to replace the media file"
42 msgstr "NOTA: Stai per sostituire il file"
43
44 #: popup.php:46
45 msgid "There is no undo. Think about it!"
46 msgstr "Questa operazione non è reversibile. Fai attenzione!"
47
48 #: popup.php:48
49 msgid "Choose a file to upload from your computer"
50 msgstr "Seleziona un file da caricare dal tuo computer"
51
52 #: popup.php:52
53 msgid "Select media replacement type:"
54 msgstr "Seleziona il tipo di sostituzione:"
55
56 #: popup.php:54
57 msgid "Just replace the file"
58 msgstr "Sostituire semplicemente il file"
59
60 #: popup.php:55
61 msgid "Note: This option requires you to upload a file of the same type ("
62 msgstr "Nota: Questa opzione richiede il caricamento di un file dello stesso tipo ("
63
64 #: popup.php:55
65 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
66 msgstr ") di quello sostituito. Il nome dell'allegato rimarrà invariato ("
67
68 #: popup.php:55
69 msgid ") no matter what the file you upload is called."
70 msgstr ") indipendentemente dal nome del file caricato."
71
72 #: popup.php:57
73 msgid "Replace the file, use new file name and update all links"
74 msgstr "Sostituire il file, usare il nome del nuovo file ed aggiornare tutti i collegamenti"
75
76 #: popup.php:58
77 msgid "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 ("
78 msgstr "Nota: Se selezioni questa opzione, il nome ed il tipo di file che stai per caricare sostituiranno quelli del file precedente. Tutti i collegamenti che puntavano al file precedente ("
79
80 #: popup.php:58
81 msgid ") will be updated to point to the new file name."
82 msgstr ") saranno aggiornati per puntare al nuovo file."
83
84 #: popup.php:60
85 msgid "Upload"
86 msgstr "Carica"
87
88 #: popup.php:60
89 msgid "Cancel"
90 msgstr "Annulla"
91
92 #: upload.php:35
93 msgid "File type does not meet security guidelines. Try another."
94 msgstr "Il tipo di file non rispetta le restrizioni di sicurezza. Riprova con un altro tipo."
95
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2015-01-19 09:00+0900\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: chacomv\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Generator: Poedit 1.5.7\n"
15 "X-Poedit-SearchPath-0: .\n"
16
17 #: enable-media-replace.php:40 enable-media-replace.php:79
18 #: enable-media-replace.php:120
19 msgid "Replace media"
20 msgstr "メディアを置換"
21
22 #: enable-media-replace.php:79
23 msgid "Upload a new file"
24 msgstr "新しいファイルをアップロード"
25
26 #: enable-media-replace.php:79
27 msgid "To replace the current file, click the link and upload a replacement."
28 msgstr ""
29 "現在のファイルを置換するには、このリンクをクリックしてアップロードします。"
30
31 #: enable-media-replace.php:168
32 msgid "Revised"
33 msgstr "変更されました"
34
35 #: popup.php:14 upload.php:3
36 msgid "You do not have permission to upload files."
37 msgstr "ファイルをアップロードする権限がありません。"
38
39 #: popup.php:30
40 msgid "Replace Media Upload"
41 msgstr "新しいメディアファイルをアップロード"
42
43 #: popup.php:46
44 msgid "NOTE: You are about to replace the media file"
45 msgstr "※ このメディアを置き換えようとしています。"
46
47 #: popup.php:46
48 msgid "There is no undo. Think about it!"
49 msgstr "元に戻せませんのでご注意ください。"
50
51 #: popup.php:48
52 msgid "Choose a file to upload from your computer"
53 msgstr "コンピューターからアップロードするファイルを選択"
54
55 #: popup.php:52
56 msgid "Select media replacement type:"
57 msgstr "メディアを置換する方式を選択:"
58
59 #: popup.php:54
60 msgid "Just replace the file"
61 msgstr "ファイルの置換のみ"
62
63 #: popup.php:55
64 msgid "Note: This option requires you to upload a file of the same type ("
65 msgstr ""
66 "※ このオプションは、元のファイルと同じファイル形式でアップロードする必要があ"
67 "ります。("
68
69 #: popup.php:55
70 msgid ""
71 ") as the one you are replacing. The name of the attachment will stay the "
72 "same ("
73 msgstr ") ファイル名は同じになります。("
74
75 #: popup.php:55
76 msgid ") no matter what the file you upload is called."
77 msgstr ")"
78
79 #: popup.php:57
80 msgid "Replace the file, use new file name and update all links"
81 msgstr "ファイルを置換して新しいファイル名で全てのリンクを更新する"
82
83 #: popup.php:58
84 msgid ""
85 "Note: If you check this option, the name and type of the file you are about "
86 "to upload will replace the old file. All links pointing to the current file ("
87 msgstr ""
88 "※ このオプションをチェックすると、アップロードしたファイルの名称と形式に置換"
89 "されます。現在のファイル名("
90
91 #: popup.php:58
92 msgid ") will be updated to point to the new file name."
93 msgstr ")へのリンクが全て新しいファイル名に更新されます。"
94
95 #: popup.php:60
96 msgid "Upload"
97 msgstr "アップロード"
98
99 #: popup.php:60
100 msgid "Cancel"
101 msgstr "キャンセル"
102
103 #: upload.php:33
104 msgid "File type does not meet security guidelines. Try another."
105 msgstr ""
106 "ファイル形式がセキュリティ的に許可されません。他の形式をお試しください。"
107
108 #: popup.php:63
109 msgid ""
110 "Please note that if you upload a new image, only embeds/links of the "
111 "original size image will be replaced in your posts."
112 msgstr ""
113 "新しいファイルをアップロードすると、元のサイズの画像へのリンクや埋め込みのみ"
114 "が置換されますのでご注意ください。"
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2010-09-10 20:53+0100\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: WarmStal D!sign | Ben ter Stal <mail@warmstal.nl>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Poedit-SearchPath-0: .\n"
15
16 #: enable-media-replace.php:26
17 #: enable-media-replace.php:45
18 msgid "Replace media"
19 msgstr "Vervangen media"
20
21 #: enable-media-replace.php:45
22 msgid "Upload a new file"
23 msgstr "Uploaden nieuw bestand"
24
25 #: enable-media-replace.php:45
26 msgid "To replace the current file, click the link and upload a replacement."
27 msgstr "Klik om het bestaande bestand te vervangen door een nieuw te uploaden bestand."
28
29 #: popup.php:7
30 #: upload.php:10
31 msgid "You do not have permission to upload files."
32 msgstr "U heeft geen rechten om bestanden te uplaoden"
33
34 #: popup.php:23
35 msgid "Replace Media Upload"
36 msgstr "Replace Media Upload"
37
38 #: popup.php:34
39 msgid "NOTE: You are about to replace the media file"
40 msgstr "Opmerking: U staat op het punt het media bestand"
41
42 #: popup.php:34
43 msgid "There is no undo. Think about it!"
44 msgstr "Deze bewerking kan niet ongedaan worden gemaakt!"
45
46 #: popup.php:36
47 msgid "Choose a file to upload from your computer"
48 msgstr "Selecteer een bestand op de PC om te uploaden"
49
50 #: popup.php:40
51 msgid "Select media replacement type:"
52 msgstr "Selecteer de wijze van vervanging"
53
54 #: popup.php:42
55 msgid "Just replace the file"
56 msgstr "Vervang alleen het bestand"
57
58 #: popup.php:43
59 msgid "Note: This option requires you to upload a file of the same type ("
60 msgstr "Opmerking: voor deze optie moet u een bestand van hetzelfde type uploaden ("
61
62 #: popup.php:43
63 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
64 msgstr ") De naam in de media bibliotheek blijft hetzelfde ("
65
66 #: popup.php:43
67 msgid ") no matter what the file you upload is called."
68 msgstr ") onafhankelijk van de naam van het nieuwe bestand."
69
70 #: popup.php:45
71 msgid "Replace the file, use new file name and update all links"
72 msgstr "Vervang het bestand, gebruik de nieuwe naam en werk alle links bij."
73
74 #: popup.php:46
75 msgid "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 ("
76 msgstr "Opmerking: bij deze keuze wordt het bestand volledig vervangen. Alle links naar het huidige bestand ("
77
78 #: popup.php:46
79 msgid ") will be updated to point to the new file name."
80 msgstr ") worden bijgewerkt naar het nieuwe bestand."
81
82 #: popup.php:48
83 msgid "Upload"
84 msgstr "Uploaden"
85
86 #: popup.php:48
87 msgid "Cancel"
88 msgstr "Annuleren"
89
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2010-09-13 14:57+0100\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: Roger <rogerhnn@hotmail.com>\n"
8 "Language-Team: Roger Nobrega <rogerhnn@hotmail.com>\n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Generator: Poedit 1.5.4\n"
15 "Language: Português - Brasil\n"
16 "X-Poedit-SourceCharset: UTF-8\n"
17 "X-Poedit-SearchPath-0: .\n"
18
19 #: enable-media-replace.php:40 enable-media-replace.php:68
20 msgid "Replace media"
21 msgstr "Substituir mídia"
22
23 #: enable-media-replace.php:68
24 msgid "Upload a new file"
25 msgstr "Enviar novo arquivo"
26
27 #: enable-media-replace.php:68
28 msgid "To replace the current file, click the link and upload a replacement."
29 msgstr ""
30 "Para subtituir o arquivo atual, clique no link e carregue um substituto"
31
32 #: popup.php:14 upload.php:21
33 msgid "You do not have permission to upload files."
34 msgstr "Você não tem permissões para enviar arquivos."
35
36 #: popup.php:30
37 msgid "Replace Media Upload"
38 msgstr "Enviar Mídia Substituta"
39
40 #: popup.php:41
41 msgid "NOTE: You are about to replace the media file"
42 msgstr "NOTA: Você irá substituir o arquivo de mídia"
43
44 #: popup.php:41
45 msgid "There is no undo. Think about it!"
46 msgstr "Não é possível cancelar esta ação."
47
48 #: popup.php:43
49 msgid "Choose a file to upload from your computer"
50 msgstr "Escolha um arquivo para enviar do seu computador"
51
52 #: popup.php:47
53 msgid "Select media replacement type:"
54 msgstr "Selecione o tipo de substituição"
55
56 #: popup.php:49
57 msgid "Just replace the file"
58 msgstr "Apenas substituir arquivo"
59
60 #: popup.php:50
61 msgid "Note: This option requires you to upload a file of the same type ("
62 msgstr "Nota: Esta opção requer o carregamento de um arquivo do mesmo tipo ("
63
64 #: popup.php:50
65 msgid ""
66 ") as the one you are replacing. The name of the attachment will stay the "
67 "same ("
68 msgstr ""
69 ") do que está sendo substituído. O nome do arquivo permanecerá o mesmo("
70
71 #: popup.php:50
72 msgid ") no matter what the file you upload is called."
73 msgstr "), independente do nome do arquivo enviado."
74
75 #: popup.php:52
76 msgid "Replace the file, use new file name and update all links"
77 msgstr ""
78 "Substituir o arquivo, usar o novo nome de arquivo, e atualizar todos os links"
79
80 #: popup.php:53
81 msgid ""
82 "Note: If you check this option, the name and type of the file you are about "
83 "to upload will replace the old file. All links pointing to the current file ("
84 msgstr ""
85 "Nota: Se selecionar esta opção, o nome e tipo do arquivo que você enviar irá "
86 "substituir os do arquivo antigo. Todos os links do arquivo atual ("
87
88 #: popup.php:53
89 msgid ") will be updated to point to the new file name."
90 msgstr ") serão atualizados para o novo arquivo."
91
92 #: popup.php:55
93 msgid "Upload"
94 msgstr "Enviar"
95
96 #: popup.php:55
97 msgid "Cancel"
98 msgstr "Cancelar"
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2017-02-10 08:48+0000\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: Pedro Mendonça <ped.gaspar@gmail.com>\n"
8 "Language-Team: Pedro Mendonça <ped.gaspar@gmail.com>\n"
9 "Language: pt_PT\n"
10 "MIME-Version: 1.0\n"
11 "Content-Type: text/plain; charset=UTF-8\n"
12 "Content-Transfer-Encoding: 8bit\n"
13 "X-Poedit-KeywordsList: __;_e\n"
14 "X-Poedit-Basepath: ..\n"
15 "X-Generator: Poedit 1.8.11\n"
16 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17 "X-Poedit-SourceCharset: UTF-8\n"
18 "X-Poedit-WPHeader: enable-media-replace.php\n"
19 "X-Poedit-SearchPath-0: .\n"
20
21 #: enable-media-replace.php:38 enable-media-replace.php:64
22 #: enable-media-replace.php:105
23 msgid "Replace media"
24 msgstr "Substituir multimédia"
25
26 #: enable-media-replace.php:64
27 msgid "Upload a new file"
28 msgstr "Carregar novo ficheiro"
29
30 #: enable-media-replace.php:64
31 msgid "To replace the current file, click the link and upload a replacement."
32 msgstr ""
33 "Para subtituir o ficheiro actual, clique na ligação e carregue um substituto."
34
35 #: enable-media-replace.php:156
36 msgid "Revised"
37 msgstr "Revisto"
38
39 #: popup.php:14 upload.php:3
40 msgid "You do not have permission to upload files."
41 msgstr "Não tem permissão para carregar ficheiros."
42
43 #: popup.php:29
44 msgid "Replace Media Upload"
45 msgstr "Carregar multimédia de substituição"
46
47 #: popup.php:45
48 #, php-format
49 msgid ""
50 "NOTE: You are about to replace the media file \"%s\". There is no undo. "
51 "Think about it!"
52 msgstr ""
53 "NOTA: Está prestes a substituir o ficheiro multimédia \"%s\". Não será "
54 "possível voltar a trás!"
55
56 #: popup.php:47
57 msgid "Choose a file to upload from your computer"
58 msgstr "Escolher um ficheiro para carregar a partir do computador"
59
60 #: popup.php:54
61 msgid "Select media replacement type:"
62 msgstr "Seleccione o tipo de substituição de multimédia:"
63
64 #: popup.php:56
65 msgid "Just replace the file"
66 msgstr "Apenas substituir o ficheiro"
67
68 #: popup.php:57
69 #, php-format
70 msgid ""
71 "Note: This option requires you to upload a file of the same type (%s) as the "
72 "one you are replacing. The name of the attachment will stay the same (%s) no "
73 "matter what the file you upload is called."
74 msgstr ""
75 "Nota: Esta opção requer o carregamento de um ficheiro do mesmo tipo (%s) "
76 "daquele a substituir. O nome do ficheiro permanecerá o mesmo (%s), "
77 "independentemente do nome do ficheiro carregado."
78
79 #: popup.php:60
80 msgid "Replace the file, use new file name and update all links"
81 msgstr ""
82 "Substituir o ficheiro, usar o novo nome de ficheiro e actualizar todas as "
83 "ligações"
84
85 #: popup.php:61
86 #, php-format
87 msgid ""
88 "Note: If you check this option, the name and type of the file you are about "
89 "to upload will replace the old file. All links pointing to the current file "
90 "(%s) will be updated to point to the new file name."
91 msgstr ""
92 "Nota: Se seleccionar esta opção, o nome e tipo do ficheiro que está prestes "
93 "a carregar irá substituir os do ficheiro antigo. Todas as ligações que "
94 "referenciam o ficheiro actual (%s) serão actualizadas de modo a referenciar "
95 "o novo nome de ficheiro."
96
97 #: popup.php:62
98 msgid ""
99 "Please note that if you upload a new image, only embeds/links of the "
100 "original size image will be replaced in your posts."
101 msgstr ""
102 "Por favor tenha em atenção que se carregar uma nova imagem, apenas as "
103 "imagens incorporadas e ligações com o tamanho original serão substituídas "
104 "nos seus artigos."
105
106 #: popup.php:67
107 msgid "Upload"
108 msgstr "Carregar"
109
110 #: popup.php:67
111 msgid "Cancel"
112 msgstr "Cancelar"
113
114 #: upload.php:26
115 #, php-format
116 msgid ""
117 "The file %1$s can not be deleted by the web server, most likely because the "
118 "permissions on the file are wrong."
119 msgstr ""
120 "O ficheiro %1$s não pode ser apagado pelo servidor web, provavelmente porque "
121 "as permissões do ficheiro estão incorrectas."
122
123 #: upload.php:84
124 msgid "File type does not meet security guidelines. Try another."
125 msgstr ""
126 "O tipo de ficheiro não está de acordo com os padrões de segurança. Tente "
127 "outro."
128
129 #. Plugin Name of the plugin/theme
130 msgid "Enable Media Replace"
131 msgstr "Enable Media Replace"
132
133 #. Plugin URI of the plugin/theme
134 msgid "http://www.mansjonasson.se/enable-media-replace"
135 msgstr "http://www.mansjonasson.se/enable-media-replace"
136
137 #. Description of the plugin/theme
138 msgid ""
139 "Enable replacing media files by uploading a new file in the \"Edit Media\" "
140 "section of the WordPress Media Library."
141 msgstr ""
142 "Permite substituir ficheiros multimédia através de carregar um novo ficheiro "
143 "na secção \"Editar multimédia\" da biblioteca multimédia do WordPress."
144
145 #. Author of the plugin/theme
146 msgid "Måns Jonasson"
147 msgstr "Måns Jonasson"
148
149 #. Author URI of the plugin/theme
150 msgid "http://www.mansjonasson.se"
151 msgstr "http://www.mansjonasson.se"
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2010-09-13 14:57+0100\n"
6 "PO-Revision-Date: 2013-01-18 14:58+0400\n"
7 "Last-Translator: Vladislav (me@dsigh.ru)\n"
8 "Language-Team: DigitalSigh\n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Poedit-Language: Russian\n"
15 "X-Poedit-Country: RUSSIAN FEDERATION\n"
16 "X-Poedit-SourceCharset: utf-8\n"
17 "X-Poedit-SearchPath-0: .\n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11) ? 0 : ((n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20)) ? 1 : 2)\n"
19
20 #: enable-media-replace.php:40
21 #: enable-media-replace.php:68
22 msgid "Replace media"
23 msgstr "Заменить"
24
25 #: enable-media-replace.php:68
26 msgid "Upload a new file"
27 msgstr "Загрузить новый файл"
28
29 #: enable-media-replace.php:68
30 msgid "To replace the current file, click the link and upload a replacement."
31 msgstr "Для того, чтобы заменить текущий файл, нажмите на ссылку и загрузите замену."
32
33 #: popup.php:14
34 #: upload.php:21
35 msgid "You do not have permission to upload files."
36 msgstr "У вас нет прав для загрузки файлов."
37
38 #: popup.php:30
39 msgid "Replace Media Upload"
40 msgstr "Загрузка файла для замены"
41
42 #: popup.php:41
43 msgid "NOTE: You are about to replace the media file"
44 msgstr "ПРИМЕЧАНИЕ: Вы собираетесь заменить медиафайл"
45
46 #: popup.php:41
47 msgid "There is no undo. Think about it!"
48 msgstr "Данную операцию нельзя отменить. Учтите это!"
49
50 #: popup.php:43
51 msgid "Choose a file to upload from your computer"
52 msgstr "Выберите файл для загрузки с вашего компьютера:"
53
54 #: popup.php:47
55 msgid "Select media replacement type:"
56 msgstr "Выберите тип замены:"
57
58 #: popup.php:49
59 msgid "Just replace the file"
60 msgstr "Только заменить файл"
61
62 #: popup.php:50
63 msgid "Note: This option requires you to upload a file of the same type ("
64 msgstr "Этот вариант требуется, если нужно загрузить файл того же типа ("
65
66 #: popup.php:50
67 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
68 msgstr "), как оригинальный. Имя вложения останется тем же ("
69
70 #: popup.php:50
71 msgid ") no matter what the file you upload is called."
72 msgstr ") не зависимо от того, как называется загружаемый файл."
73
74 #: popup.php:52
75 msgid "Replace the file, use new file name and update all links"
76 msgstr "Заменить файл, использовать новое имя и обновить все ссылки"
77
78 #: popup.php:53
79 msgid "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 ("
80 msgstr "Если выбрать этот вариант, то имя и тип файла, который вы собираетесь загрузить, заменят старые. Все ссылки, указывающие на текущий файл ("
81
82 #: popup.php:53
83 msgid ") will be updated to point to the new file name."
84 msgstr ") будут обновлены новыми."
85
86 #: popup.php:55
87 msgid "Upload"
88 msgstr "Загрузить"
89
90 #: popup.php:55
91 msgid "Cancel"
92 msgstr "Отмена"
93
94 #~ msgid "Replace media upload"
95 #~ msgstr "Загрузка файла для замены"
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2015-01-16 15:55+0100\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: Måns Jonasson <mans@thejonassons.com>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Generator: Poedit 1.5.5\n"
15 "X-Poedit-SearchPath-0: ..\n"
16
17 #: ../enable-media-replace.php:40 ../enable-media-replace.php:79
18 #: ../enable-media-replace.php:120
19 msgid "Replace media"
20 msgstr "Ersätt media"
21
22 #: ../enable-media-replace.php:79
23 msgid "Upload a new file"
24 msgstr "Ladda upp en ny fil"
25
26 #: ../enable-media-replace.php:79
27 msgid "To replace the current file, click the link and upload a replacement."
28 msgstr ""
29 "För att ersätta den nuvarande filen, klicka på länken och ladda upp en ny "
30 "fil."
31
32 #: ../enable-media-replace.php:161
33 msgid "Revised"
34 msgstr "Uppdaterad"
35
36 #: ../popup.php:14 ../upload.php:3
37 msgid "You do not have permission to upload files."
38 msgstr "Du har inte tillåtelse att ladda upp filer."
39
40 #: ../popup.php:30
41 msgid "Replace Media Upload"
42 msgstr "Ladda upp ny fil"
43
44 #: ../popup.php:46
45 msgid "NOTE: You are about to replace the media file"
46 msgstr "OBS: Du är på väg att ersätta filen"
47
48 #: ../popup.php:46
49 msgid "There is no undo. Think about it!"
50 msgstr "Det finns inget sätt att ångra. Tänk efter först!"
51
52 #: ../popup.php:48
53 msgid "Choose a file to upload from your computer"
54 msgstr "Välj en fil att ladda upp från din dator"
55
56 #: ../popup.php:55
57 msgid "Select media replacement type:"
58 msgstr "Välj ersättningsform:"
59
60 #: ../popup.php:57
61 msgid "Just replace the file"
62 msgstr "Ersätt bara filen"
63
64 #: ../popup.php:58
65 msgid "Note: This option requires you to upload a file of the same type ("
66 msgstr "Detta val kräver att du laddar upp en fil av exakt samma typ ("
67
68 #: ../popup.php:58
69 msgid ""
70 ") as the one you are replacing. The name of the attachment will stay the "
71 "same ("
72 msgstr ") som den du ersätter. Namnet på filen kommer att behållas ("
73
74 #: ../popup.php:58
75 msgid ") no matter what the file you upload is called."
76 msgstr ") oavsett vad filen du laddar upp heter på din dator."
77
78 #: ../popup.php:61
79 msgid "Replace the file, use new file name and update all links"
80 msgstr "Ersätt filen, använd det nya filnamnet och uppdatera alla länkar"
81
82 #: ../popup.php:62
83 msgid ""
84 "Note: If you check this option, the name and type of the file you are about "
85 "to upload will replace the old file. All links pointing to the current file ("
86 msgstr ""
87 "Om du klickar i den här rutan kommer den även namnet och typen på den nya "
88 "filen du laddar upp helt att ersätta den gamla. Alla länkar som pekar på den "
89 "gamla filen ("
90
91 #: ../popup.php:62
92 msgid ") will be updated to point to the new file name."
93 msgstr ") kommer att uppdateras så att de pekar på det nya filnamnet."
94
95 #: ../popup.php:63
96 msgid ""
97 "Please note that if you upload a new image, only embeds/links of the "
98 "original size image will be replaced in your posts."
99 msgstr ""
100 "Notera att om du laddar upp en ny bild kommer endast inbäddningar/länkar "
101 "till originalstorleken att bytas ut i dina poster."
102
103 #: ../popup.php:68
104 msgid "Upload"
105 msgstr "Ladda upp"
106
107 #: ../popup.php:68
108 msgid "Cancel"
109 msgstr "Avbryt"
110
111 #: ../upload.php:26
112 #, php-format
113 msgid ""
114 "The file %1$s can not be deleted by the web server, most likely because the "
115 "permissions on the file are wrong."
116 msgstr ""
117 "Filen %1$s kan inte raderas av webbservern, troligen på grund av "
118 "filrättigheterna."
119
120 #: ../upload.php:84
121 msgid "File type does not meet security guidelines. Try another."
122 msgstr ""
123 "Den här filen är inte tillåten enligt WordPress säkerhetsinställningar. "
124
125 #~ msgid "Enable Media Replace"
126 #~ msgstr "Ladda upp ny fil"
127
128 #~ msgid ""
129 #~ "This plugin allows you to replace any uploaded media file by uploading a "
130 #~ "new one."
131 #~ msgstr ""
132 #~ "Detta plugin låter dig ersätta en fil i mediebiblioteket genom att ladda "
133 #~ "upp en ny."
134
135 #~ msgid "First, locate the uploaded file you want to replace, using the"
136 #~ msgstr "Hitta först den fil du vill ersätta genom att bläddra i "
137
138 #~ msgid "media library browser"
139 #~ msgstr "mediebiblioteket"
140
141 #~ msgid "Click the \"Edit\" link"
142 #~ msgstr "Tryck på \"Redigera\"-länken"
143
144 #~ msgid ""
145 #~ "Second, click the link \"Upload a new file\" and follow the instructions."
146 #~ msgstr "Tryck sen på länken \"Ladda upp ny fil\" och följ instruktionerna."
147
148 #~ msgid "Replace media upload"
149 #~ msgstr "Ladda upp ny fil"
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: Enable Media Replace v2.6\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: \n"
6 "PO-Revision-Date: 2012-09-18 07:29:17+0000\n"
7 "Last-Translator: Tunghsiao Liu <info@sparanoid.com>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "Plural-Forms: nplurals=2; plural=n != 1;\n"
13 "X-Poedit-Language: Chinese\n"
14 "X-Poedit-Country: PEOPLE'S REPUBLIC OF CHINA\n"
15 "X-Poedit-SourceCharset: utf-8\n"
16 "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n"
17 "X-Poedit-Basepath: \n"
18 "X-Poedit-Bookmarks: \n"
19 "X-Poedit-SearchPath-0: .\n"
20 "X-Textdomain-Support: yes"
21
22 #: enable-media-replace.php:39
23 #: enable-media-replace.php:67
24 #@ enable-media-replace
25 msgid "Replace media"
26 msgstr "替换媒体"
27
28 #: enable-media-replace.php:67
29 #@ enable-media-replace
30 msgid "Upload a new file"
31 msgstr "上传新文件"
32
33 #: enable-media-replace.php:67
34 #@ enable-media-replace
35 msgid "To replace the current file, click the link and upload a replacement."
36 msgstr "想要替换当前文件,点击上述链接并上传新文件"
37
38 #: popup.php:14
39 #: upload.php:3
40 #@ enable-media-replace
41 #@ default
42 msgid "You do not have permission to upload files."
43 msgstr "您没有权限上传文件"
44
45 #: popup.php:30
46 #@ enable-media-replace
47 msgid "Replace Media Upload"
48 msgstr "替换媒体文件"
49
50 #: popup.php:46
51 #@ enable-media-replace
52 msgid "NOTE: You are about to replace the media file"
53 msgstr "注意:您将要替换媒体文件"
54
55 #: popup.php:46
56 #@ enable-media-replace
57 msgid "There is no undo. Think about it!"
58 msgstr "此操作无法撤销。"
59
60 #: popup.php:48
61 #@ enable-media-replace
62 msgid "Choose a file to upload from your computer"
63 msgstr "从计算机中选择文件上传"
64
65 #: popup.php:52
66 #@ enable-media-replace
67 msgid "Select media replacement type:"
68 msgstr "选择媒体替换类型"
69
70 #: popup.php:54
71 #@ enable-media-replace
72 msgid "Just replace the file"
73 msgstr "仅替换文件"
74
75 #: popup.php:55
76 #@ enable-media-replace
77 msgid "Note: This option requires you to upload a file of the same type ("
78 msgstr "说明:此选项要求您上传与之前文件相同的文件类型("
79
80 #: popup.php:55
81 #@ enable-media-replace
82 msgid ") as the one you are replacing. The name of the attachment will stay the same ("
83 msgstr "),替换后,无论您上传的文件名是什么,上传后的媒体文件名称与地址都会保持不变("
84
85 #: popup.php:55
86 #@ enable-media-replace
87 msgid ") no matter what the file you upload is called."
88 msgstr ")。"
89
90 #: popup.php:57
91 #@ enable-media-replace
92 msgid "Replace the file, use new file name and update all links"
93 msgstr "替换文件,使用新文件名并更新所有链接"
94
95 #: popup.php:58
96 #@ enable-media-replace
97 msgid "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 ("
98 msgstr "说明:使用此选项,将会使用新文件的文件名及文件类型,所有包含文件名("
99
100 #: popup.php:58
101 #@ enable-media-replace
102 msgid ") will be updated to point to the new file name."
103 msgstr ")的链接也将会被替换成新文件名。"
104
105 #: popup.php:60
106 #@ enable-media-replace
107 msgid "Upload"
108 msgstr "上传"
109
110 #: popup.php:60
111 #@ enable-media-replace
112 msgid "Cancel"
113 msgstr "取消"
114
115 #: upload.php:33
116 #@ default
117 msgid "File type does not meet security guidelines. Try another."
118 msgstr "文件类型不符合安全规范,请尝试其他文件。"
119
1 msgid ""
2 msgstr ""
3 "Project-Id-Version: enable-media-replace\n"
4 "Report-Msgid-Bugs-To: \n"
5 "POT-Creation-Date: 2015-01-16 17:42-0000\n"
6 "PO-Revision-Date: \n"
7 "Last-Translator: Pedro Mendonça <ped.gaspar@gmail.com>\n"
8 "Language-Team: \n"
9 "MIME-Version: 1.0\n"
10 "Content-Type: text/plain; charset=UTF-8\n"
11 "Content-Transfer-Encoding: 8bit\n"
12 "X-Poedit-KeywordsList: __;_e\n"
13 "X-Poedit-Basepath: .\n"
14 "X-Generator: Poedit 1.7.1\n"
15 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
16 "X-Poedit-SourceCharset: UTF-8\n"
17 "Language: en\n"
18 "X-Poedit-SearchPath-0: .\n"
19 "X-Poedit-SearchPath-1: ..\n"
20
21 #: ../enable-media-replace.php:40 ../enable-media-replace.php:79
22 #: ../enable-media-replace.php:120
23 msgid "Replace media"
24 msgstr ""
25
26 #: ../enable-media-replace.php:79
27 msgid "Upload a new file"
28 msgstr ""
29
30 #: ../enable-media-replace.php:79
31 msgid "To replace the current file, click the link and upload a replacement."
32 msgstr ""
33
34 #: ../enable-media-replace.php:161
35 msgid "Revised"
36 msgstr ""
37
38 #: ../popup.php:14 ../upload.php:3
39 msgid "You do not have permission to upload files."
40 msgstr ""
41
42 #: ../popup.php:30
43 msgid "Replace Media Upload"
44 msgstr ""
45
46 #: ../popup.php:46
47 msgid "NOTE: You are about to replace the media file"
48 msgstr ""
49
50 #: ../popup.php:46
51 msgid "There is no undo. Think about it!"
52 msgstr ""
53
54 #: ../popup.php:48
55 msgid "Choose a file to upload from your computer"
56 msgstr ""
57
58 #: ../popup.php:55
59 msgid "Select media replacement type:"
60 msgstr ""
61
62 #: ../popup.php:57
63 msgid "Just replace the file"
64 msgstr ""
65
66 #: ../popup.php:58
67 msgid "Note: This option requires you to upload a file of the same type ("
68 msgstr ""
69
70 #: ../popup.php:58
71 msgid ""
72 ") as the one you are replacing. The name of the attachment will stay the "
73 "same ("
74 msgstr ""
75
76 #: ../popup.php:58
77 msgid ") no matter what the file you upload is called."
78 msgstr ""
79
80 #: ../popup.php:61
81 msgid "Replace the file, use new file name and update all links"
82 msgstr ""
83
84 #: ../popup.php:62
85 msgid ""
86 "Note: If you check this option, the name and type of the file you are about "
87 "to upload will replace the old file. All links pointing to the current file ("
88 msgstr ""
89
90 #: ../popup.php:62
91 msgid ") will be updated to point to the new file name."
92 msgstr ""
93
94 #: ../popup.php:63
95 msgid ""
96 "Please note that if you upload a new image, only embeds/links of the "
97 "original size image will be replaced in your posts."
98 msgstr ""
99
100 #: ../popup.php:68
101 msgid "Upload"
102 msgstr ""
103
104 #: ../popup.php:68
105 msgid "Cancel"
106 msgstr ""
107
108 #: ../upload.php:26
109 #, php-format
110 msgid ""
111 "The file %1$s can not be deleted by the web server, most likely because the "
112 "permissions on the file are wrong."
113 msgstr ""
114
115 #: ../upload.php:84
116 msgid "File type does not meet security guidelines. Try another."
117 msgstr ""
1 <?php // Silence is golden
...\ No newline at end of file ...\ No newline at end of file
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.4
7 Requires PHP: 5.6
8 Stable tag: 4.1.4
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! Now fully compatible with <a href="https://wordpress.org/plugins/amazon-s3-and-cloudfront/" target="_blank">WP Offload Media!</a>
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 If you don't want to use the background removal feature, add this line to your theme's functions.php file, or use a plugin like <a href="https://wordpress.org/plugins/code-snippets/" target="_blank">Code Snippets</a>:
38
39 ```add_filter( 'emr/feature/background', '__return_false' );```
40
41 To shorten the wait time before redirecting to the media editing screen, use the following filter and specify the wait time in seconds (0 means that redirection is immediate, but may cause problems in certain configurations):
42
43 ```add_filter('emr/success/timeout', function () { return 3; });```
44
45 #### Show file modification time
46
47 There is a shortcode that takes the file modification date and displays it in a post or on a page. The code is:
48 `[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.
49
50 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)
51
52 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).
53
54 **Other plugins by [ShortPixel](https://shortpixel.com):**
55
56 * [ShortPixel Image Optimizer](https://wordpress.org/plugins/shortpixel-image-optimiser/) - Image optimization & compression for all the images on your website, including WebP & AVIF delivery
57 * [ShortPixel Adaptive Images](https://wordpress.org/plugins/shortpixel-adaptive-images/) - On-the-fly image optimization & CDN delivery
58 * [Resize Image After Upload](https://wordpress.org/plugins/resize-image-after-upload/) - Automatically resize each uploaded image
59 * [reGenerate Thumbnails Advanced](https://wordpress.org/plugins/regenerate-thumbnails-advanced/) - Easily regenerate thumbnails
60 * [WP SVG Images](https://wordpress.org/plugins/wp-svg-images/) - Secure upload of SVG files to Media Library
61 * [ShortPixel Critical CSS](https://wordpress.org/plugins/shortpixel-critical-css/) - Automatically generate above-the-fold CSS for fatster loading times and better SEO scores
62
63 **Get in touch!**
64
65 * Email <a href="https://shortpixel.com/contact" target="_blank">https://shortpixel.com/contact</a>
66 * Twitter <a href="https://twitter.com/shortpixel" target="_blank">https://twitter.com/shortpixel</a>
67 * Facebook <a href="https://www.facebook.com/ShortPixel" target="_blank">https://www.facebook.com/ShortPixel</a>
68 * LinkedIn <a href="https://www.linkedin.com/company/shortpixel" target="_blank">https://www.linkedin.com/company/shortpixel</a>
69
70 == Changelog ==
71
72 = 4.1.4 =
73
74 Release date: September 22, 2023
75 * Fix: The latest security fix was breaking the replacements made with Beaver Builder (and possibly other builders);
76
77 = 4.1.3 =
78
79 Release date: September 14, 2023
80 * Fix: A possible PHP Object Injection was patched, which could be exploited under certain conditions;
81 * Fix: ShortPixel Image Optimizer handles offloading when both plugins are used with WP Offload Media.
82
83 = 4.1.2 =
84
85 Release date: April 24, 2023
86 * Tweak: Reduced the wait time before redirecting to the media edit page to half (5s);
87 * Fix: If an item is not offloaded, no attempt is made to update its data;
88 * Fix: Removed some double spaces from the plugin strings (thanks to @Presskopp).
89
90 = 4.1.1 =
91
92 Release date: April 13, 2023
93 * Tweak: Added a filter to reduce or remove the wait time before redirecting to the media edit page;
94 * Fix: Replacement works again on multisite setups where the plugin reported the path outside the uploads folder;
95 * Fix: In some cases, replacement didn't work when images were offloaded;
96 * Fix: Some translation strings were corrected (thanks @alexclassroom !).
97
98 = 4.1.0 =
99
100 Release date: March 28, 2023
101 * New: Integration with WP Offload Media; items that are offloaded can now be replaced correctly;
102 * New: Switch to a Replacer module that improves the code and makes it more robust;
103 * New: Adding an intermediate confirmation page to solve possible redirection issues;
104 * Tweak: The new image can now be dragged and dropped directly into the preview area;
105 * Tweak: Improved the display of both the original image and the new image on the replacement screen;
106 * Tweak: Improved the notification system and added a mechanism for remote notifications;
107 * Tweak: Updated the texts and banners to look better on the replacement screen;
108 * Fix: Added documentation in the readme to disable the background removal feature (kudos to @jstask82);
109 * Fix: Increased the security of the plugin by adding checks for various AJAX calls;
110
111 = 4.0.3 =
112
113 Release date: February 21, 2023
114 * Fix: background removal notification is no longer displayed when this feature is disabled with the filter;
115 * Compat: improved compatibility with PHP 8.1 and 8.2
116 * Tweak: updated the banners on the replace page.
117
118 = 4.0.2 =
119
120 Release date: January 13, 2023
121 * Fix: patched a security vulnerability that could allow loading an unauthorized file during the replace operation;
122 * Fix: finish the replace operation even if the tmp file cannot be removed due to file permissions issues;
123 * Fix: when replacing a scaled file with a non-scaled file, some links were broken;
124 * Fix: under certain conditions a PHP warning about an undefined array key was displayed.
125
126 = 4.0.1 =
127
128 Release date: November 23, 2022
129 * Fix: files are now replaced correctly in environments running on Windows/IIC servers;
130 * Fix: the mime type is now correctly detected, so that a warning is no longer displayed when replacing PDFs;
131 * Fix: the plugin no longer tries to load its code for post IDs that are not attachments, like comments;
132 * Fix: some warnings were displayed when using the Members plugin;
133 * Compat: added integration for SiteOrigin Page Builder to correctly replace files in its widgets;
134 * Compat: added `emr_after_remove_current` action hook (thanks @luistar15);
135 * Tweak: added filter to disable the background replace functionality.
136
137 = 4.0.0 =
138
139 Release date: September 5th, 2022
140 * New: added the functionality to remove the background for any image;
141 * Fix: images added to the new block-style widgets were not replaced;
142 * Fix: the original file was not removed after replacement if a multilingual plugin was installed;
143 * Fix: additional checks were added to the new upload path for replacements, to avoid possible vulnerabilities, kudos to @soulseekah;
144 * Fix: an object cache flush was added after an image was replaced to prevent the content from still being cached in the post editor;
145 * Fix: if there was no `_wp_attached_file` in the postmeta table a fatal error was thrown;
146 * Fix: the time zone was not displayed correctly on the Replace Media screen;
147 * Fix: added some additional checks for file path to avoid `open_basedir` restrictions;
148 * Fix: added titles for the Replace Media and Remove Background screens;
149 * Fix: various small CSS/JS fixes, wording updates and code cleanups;
150
151 = 3.6.3 =
152
153 Release date: November 25th, 2021
154 * Fix: the shortcode `file-modified` works again for non-privileged visitors as well;
155 * Compat: if other plugins don't properly use WP Hooks, don't return errors;
156 * Tweak: updated banners list from the EMR screen.
157
158 = 3.6.2 =
159
160 Release date: November 8th, 2021
161 * Fix: there was an issue in file.php with checking mime-type, resulting in weird mime-type values in certain cases (thanks @dougaxe1 for the PR);
162 * Fix: if target mime type returns empty, don't store that;
163 * Tweak: removed old notification about SPIO.
164
165 = 3.6.1 =
166
167 Release date: October 27th, 2021
168 * Fix: there was a broken image in one of the plugin notices;
169 * Fix: added a check for the function `mime_content_type`, which doesn't seem to always be available on various hosts;
170 * Fix: the plugin checks if the current user has rights to install/activate plugins and only then displays the 1-click installers and also a filter is available now;
171
172 = 3.6.0 =
173
174 Release date: October 11th, 2021
175 * New: Constant EMR_CAPABILITY can define extra user roles to allow and deny replacements;
176 * New: Replacing an usual image with an SVG file will just replace all thumbnails by the main file;
177 * New: Also replaces caption when set in Exif data (when replacing with updating all links);
178 * Tweak: Added Now and Original options to Custom Date for usability;
179 * Tweak: changes/updates to the banners from the EMR screen;
180 * Fix: EMR now respects edit_other_posts permissions using default permissions;
181 * Fix: EMR with ALLOW_UNFILTERED_UPLOADS enabled (and allowed role) will not limit non-allowed uploads;
182 * Fix: some missing and wrong text-domains are now corrected (thanks @alexclassroom);
183 * Fix: a missing dot in the plugin description was preventing the transaltions to properly work;
184 * Fix: the plugin can now be uninstalled/deleted on PHP 8.
185
186 = 3.5.0 =
187
188 Release date: October 29th 2020
189 * New: EMR now replaces across all meta tables;
190 * New: the plugin remembers last used settings;
191 * New: integration with the LiteSpeed cache plugin and webserver;
192 * Tweak: manual Logging will no longer work if user is not logged as administrator;
193 * Tweak: added `post_id` to `enable-media-replace-upload-done` action args, props to @Jan Stiegler;
194 * Fix: fully works now with Elementor;
195 * Fix: the issue for WP-Bakery and URL-Encoded links is now fixed;
196 * Fix: the plugin should now work with images added through Visual Composer;
197 * Fix: EMR now uses queries instead of WordPress functions, correctly handling slashes and JSON formats;
198 * Fix: `-scaled` images generated by WordPress are now removed when replacing an image;
199
200 = 3.4.2 =
201
202 Release date: August 17th 2020
203 * New - Also display files size in the replacement windows;
204 * Fix - Replacing image with pdf file would cause noticesi;
205 * Fix - Compensation for faulty WordPress installation that return empty error string on wp_upload_dir and falsely return relative paths and urls on WP core functions;
206 * Fix - When choosing 'replace file and update names' and then uploading a file with identical name as source, it would postfix the duplicate name. This is no longer the case;
207 * Fix - Wording clarification for the situation when the filename is changed, which will lead to broken links from external sites;
208 * Fix - Added hard paths to require_once satements in order to prevent WP-CLI errrors;
209 * Fix - If source image doesn't exist or in error state, replacement would be prevented by Javascript issue.
210
211 = 3.4.1 =
212
213 Release date: June 18th 2020
214 * Fix - PHP Error when settings permissions failed.
215
216 = 3.4.0 =
217
218 Release date: June 9th 2020
219 * New - In edit media screen EMR notes user who replaced file if this was not uploader;
220 * New - Added Drag and Drop area for files;
221 * New - When replacing with new file name, now also possible to update upload path;
222 * Fix - Various minor CSS tweaks and fixes;
223 * Update of Underlying libraries
224
225 = 3.3.12 =
226
227 Release date: 27th April 2020
228 * Fixed - When trying to upload over-limit file would show too many error messages;
229 * Language – 2 new strings added, 0 updated, 0 fuzzied, and 0 obsoleted.
230
231 = 3.3.11 =
232
233 Release date: 10th March 2020
234 * Fix the crashing of certain frontend builders when the plugin is active.
235
236 = 3.3.10 =
237
238 Release date: 23rd February 2020
239 * Fix issue with JSON encoding which was interfering with Advanced Custom Fields and other plugins
240
241 = 3.3.9 =
242
243 Release date: 23rd February 2020
244 * Fix issue with JSON encoded strings
245 * Language – 0 new strings added, 2 updated, 0 fuzzied, and 0 obsoleted
246
247 Release date: 19th February 2020
248 * Fix issue with search/replacing non-image attachments and query
249 * Language – 0 new strings added, 2 updated, 0 fuzzied, and 0 obsoleted
250
251 = 3.3.8 =
252
253 Release date: 18th February 2020
254 * Fixes for WP 5.3+ -scaled images system. Will now replace those as well.
255 * Updating and replacing images and thumbnails should now work for serialized (and such) metadata.
256 - For Beaver Builder
257 * Add extra warning if mimetype is not allowed by WordPress for upload
258 * Put Javascript version in wp_register_script
259 * Hidden double 'replace media'
260 * Replace Image label to Replace Media
261 * Removed constant S3_UPLOADS_AUTOENABLE
262 * Improved detection of SVG image sizes
263 * Fixed - Logger doesn't call wp_upload_dir when not debugging
264 * Extra - Javascript tries to resume after external errors.
265 * Language – 0 new strings added, 2 updated, 0 fuzzied, and 0 obsoleted
266
267 = 3.3.7 =
268
269 Release date: 13th November 2019
270 * call the hook enable-media-replace-upload-done on both modes
271 * fix JSON compatibility for hostings that don't have JSON module activated
272 * Language – 0 new strings added, 0 updated, 0 fuzzied, and 0 obsoleted
273
274 = 3.3.6 =
275
276 Release date: 5th September 2019
277 * fix JSON compatibility for hostings that don't have JSON module activated
278
279 = 3.3.5 =
280
281 Release date: 25th July 2019
282 * fix Replace button on the MediaLibrary image details popup
283
284 = 3.3.4 =
285
286 Release date: 23rd July 2019
287 * compatibility fixes for WP version 4.8 and below
288 * cache killer
289
290 = 3.3.3 =
291
292 Release date: 19th July 2019
293 * Fix error "using $this when not in object context" on some PHP versions
294
295 = 3.3.2 =
296
297 Release date: 17th July 2019
298 * Check if medium size !> 400px, display that one, otherwise smallest.
299 * Fixed: Links not updated when using Advanced Custom Fields
300 * Fixed: Fails silently when file is too big for upload
301 * When source file does not exist, show placeholder instead of failed image load
302 * Fixed: Fatal error when replacing images
303 * Fixed: Not the right time zone on replace
304 * Fixed Beaver Builder incompatibility by not allowing replace with rename.
305 * Fixed: Cannot replace non default Wordpress file types, even those allowed to upload [ Media Library Assistant compat ]
306 * Fixed: error when trying to remove a file that doesn't exist - because the files are actually on another server
307
308 = 3.3.1 =
309
310 Release date: 18th June 2019
311 * Fix error class not found on WPEngine
312
313 = 3.3.0 =
314 * When replacing an image and changing the name, Search / Replace is now also done on the meta_value of postmeta.
315 * Replace PDF thumbnails too
316 * Copy title from EXIF
317 * RTL View incorporated into the CSS
318 * ‘wp_handle_upload’ filter should be treated as such (and not as action)
319 * Use wp_attached_file instead of the GUID
320 * Fix: replace missing file
321 * Fix: aphostrophe breaking the upload
322 * Fix: broken "before" image
323 * Fix: update properly the date
324 * Fix: errors for non-image items in Media Library
325 * Fix: empty admin menu item created
326 * Refactored all the code
327
328 = 3.2.9 =
329 * properly replace thumbnails names in the content when the replaced image has a different aspect ratio, thus the new thumbnails have a different height in the name.
330
331 = 3.2.8 =
332 * fix for failures in link updating when replacing file because of addslashes - use prepared query instead
333 * replace basename with wp_basename because basename doesn't work well with UTF8
334
335 = 3.2.7 =
336 * Add minimum required php version to run the plugin.
337 * Security: Prevent direct access to php files.
338 * Security: Prevent direct access to directories.
339 * Security: Escape translation strings using `esc_attr__()` and `esc_html__()` functions.
340 * Fix RTL issues.
341
342 = 3.2.6 =
343 * no more 404 error if no image was selected when trying to replace it
344 * added preview so you can check the image being replaced and also the image that's being replaced with
345 * .dat files can be replaced (functionality accidentally removed in the previous version)
346 * added compatibility with S3 upload plugin
347 * when an image is replaced the date is also updated
348
349 = 3.2.5 =
350 * remove the leftover setcookie and the plugins recommendations.
351
352 = 3.2.4 =
353 * Fix PDF thumbnails not replaced when replacing a PDF
354 * Fix not replacing text files with .dat extension
355
356 = 3.2.3 =
357 * disable ShortPixel recommendation on secondary sites of a multisite install when it was network activated.
358
359 = 3.2.2 =
360 * Fixed compatibility with ShortPixel and Resize Image After Upload
361 * Added ShortPixel links and images, fixed the problem of ShortPixel recommendation not dismissing.
362
363 = 3.2.1 =
364 * Bugfix, typo made metadata changes (thanks GitHub user icecandy!)
365 * Removed Shortpixel links and images
366
367 = 3.2 =
368 * Tested with WP 4.9.4
369 * Added Shortpixel link in replace media screen
370
371 = 3.1.1 =
372 * Fixed bug introduced in an earlier version, preventing the updating of URLs on pages/posts if the link did not contain the domain name
373
374 = 3.1 =
375 * Got rid of some pesky old code, and added some better filtering options, thanks to GitHub users speerface, aaemnnosttv, and ururk
376 * Brand new, shiny code to replace other image sizes in embedded media, thanks to GitHub user ianmjones!
377 * Tested with WP 4.8
378
379 = 3.0.6 =
380 * Tested with WP 4.7.2
381 * New PT translations (thanks Pedro Mendonca! https://github.com/mansj/enable-media-replace/commit/b6e63b9a8a3ae46b3a6664bd5bbf19b2beaf9d3f)
382
383 = 3.0.5 =
384 * Tested with WP 4.6.1
385
386 = 3.0.4 =
387 * Fixed typo in .pt translations (https://github.com/mansj/enable-media-replace/pull/18)
388 * Fixed better error handling in modification date functions (https://github.com/mansj/enable-media-replace/pull/16)
389 * Tested with WP 4.4.1
390
391 = 3.0.3 =
392 * Scrapped old method of detecting media screen, button to replace media will now show up in more places, yay!
393 * Made sure the call to get_attached_file() no longer skips filters, in response to several users wishes.
394 * Suppressed error messages on chmod()
395 * Added Japanese translation (Thank you, chacomv!)
396
397 = 3.0.2 =
398 * Cleaned up language files
399 * Added Portuguese translation (Thanks pedro-mendonca!)
400 * Tested with WP 4.1
401 * Added missing Swedish translation strings
402
403 = 3.0.1 =
404 * Tiny fix to re-insert the EMR link in the media list view.
405
406 = 3.0 =
407 * Updated for WordPress 4.0
408 * Now inheriting permissions of the replaced files, [Thank you Fiwad](https://github.com/fiwad)
409
410 = 2.9.7RC1 =
411 * Moved localization files into their own directory. [Thank you Michael](https://github.com/michael-cannon)
412 * Moved screenshots into their own directory. [Thank you Michael](https://github.com/michael-cannon)
413
414 = 2.9.6 =
415 * Added fix by Grant K Norwood to address a possible security problem in SQL statements. Thanks Grant!
416 * Created GitHub repo for this plugin, please feel free to contribute at github.com/mansj/enable-media-replace
417
418 = 2.9.5 =
419 * Bug fix for the short code displaying the modification date of a file
420 * Updated all database queries in preparation for WP 3.9
421
422 = 2.9.4 =
423 * Bug fix for timezone changes in WordPress
424 * Minor UI change to inform the user about what actually happens when replacing an image and using a new file name
425
426 = 2.9.3 =
427 * Added call to update_attached_file() which should purge changed files for various CDN and cache plugs. Thanks Dylan Barlett for the suggestion! (http://wordpress.org/support/topic/compatibility-with-w3-total-cache)
428 * Suppressed possible error in new hook added in 2.9.2
429
430 = 2.9.2 =
431 * Small bug fix
432 * Added hook for developers to enable purging possible CDN when updating files - thanks rubious for the suggestion!
433
434 = 2.9.1 =
435 * Added Brazilian Portuguese translation, thanks Roger Nobrega!
436 * Added filter hook for file name creation, thanks to Jonas Lundman for the code!
437 * Added modification date to the edit attachment screen, thanks to Jonas Lundman for the code!
438 * Enhanced the deletion method for old file/image thumbnails to never give unnecessary error messages and more accurately delete orphaned thumbs
439
440 = 2.9 =
441 * Added Portuguese translation, thanks Bruno Miguel Bras Silva!
442 * New edit link from media library
443 * After uploading, the plugin now takes you back to edit screen instead of library
444
445 = 2.8.2 =
446 * Made another change to the discovery of media context which will hopefully fix a bug in certain cases. Thanks to "Joolee" at the WordPress.org forums!
447 * Added a new, supposedly better Russian translation from "Vlad".
448
449 = 2.8.1 =
450 * Fixed a small bug which could create error messages on some systems when deleting old image files.
451
452 = 2.8 =
453 * New and safer method for deleting thumbnails when a new image file is uploaded.
454 * New translations for simplified Chinese (thanks Tunghsiao Liu) and Italian (grazie Marco Chiesi)
455 * Added method for detecting upload screen to ensure backward compatibility with versions pre 3.5
456
457 = 2.7 =
458 * A couple of changes made to ensure compatibility with WordPress 3.5. Thanks to Elizabeth Powell for the fixes!
459
460 = 2.6 =
461 * New and improved validation of uploaded files, now using WP's own functions for checking file type and extension. Thanks again to my old friend Ulf "Årsta" Härnhammar for keeping us all on our toes! :) This should also hopefully fix the problems people have been having with their installations claiming that perfectly good PDF files are not allowed file types.
462
463 = 2.5.2 =
464 * The "more reliable way" of determining MIME types turned out to be less reliable. Go figure. There seems to be no perfect way of performing a reliable check for MIME-types on an uploaded file that is also truly portable. I have now made checks for the availability of mime_content_type() before using it, using the old method as a fall-back. It is far from beautiful, so if anybody has a better way of doing it, please contact me!
465
466 = 2.5.1 =
467 * Bug fix - there is now a more reliable way of determining file type on your upload so you can upload PDF files without seeing that pesky "File type does not meet security guidelines" message.
468 * New translation to Danish - thanks to Michael Bering Petersen!
469
470 = 2.5 =
471 * Tested with WordPress 3.2.1
472 * New translation to German - thanks to Martin Lettner!
473 * New translation to French - thanks to François Collette!
474
475 = 2.4.1 =
476 * Bug fix for WordPress 3.1 RC. Now properly tested and should be working with 3.1 whenever it finally comes out. :)
477
478 = 2.4 =
479 * Bug fixes, security fixes. Thanks to my old pal Ulf "&Aring;rsta" H&auml;rnhammar for pointing them out!
480 * New method for uploading avoids going around WP, for greater security.
481
482 = 2.3 =
483 * Lots of code trimmed and enhanced, thanks to Ben ter Stal! Now working properly with Windows systems, better security, optimized loading, and much more.
484 * Added Dutch translation by Ben ter Stal.
485
486 = 2.2 =
487 * Bug fix, fixed typo in popup.php, thanks to Bill Dennen and others for pointing this out!
488
489 = 2.1 =
490 * New shortcode - display file modification date on your site (see description for more info)
491 * A couple of bug fixes for final release of 3.0 - Thanks to Jim Isaacs for pointing them out!
492
493 = 2.0.1 =
494 * Added support for SSL admin
495
496 = 2.0 =
497 * Replaced popup with inline navigation when replacing media
498 * Added instructions in admin link under Media
499
500 = 1.4.1 =
501 * Tested with WordPress 3.0 beta 2
502
503 = 1.4 =
504 * Removed short tags for better compatibility.
505
506 = 1.3 =
507 * Added support for wp_config setting "FORCE_SSL_ADMIN"
508
509 = 1.2 =
510 * Added Russian translation, thanks to Fat Cower.
511
512 = 1.1 =
513 * Minor bugfix, now working with IE8 too!
514
515 = 1.0 =
516 * First stable version of plugin.
517
518 == Installation ==
519
520 Quick and easy installation:
521
522 1. Upload the folder `enable-media-replace` to your plugin directory
523 1. Activate the plugin through the 'Plugins' menu in WordPress
524 1. Done!
525
526 == Frequently Asked Questions ==
527
528 = What does this plugin actually do? =
529
530 This plugin makes it easy to update/replace files that have been uploaded to the WordPress Media Library.
531
532 = How does it work? =
533
534 A new option will be available in the Edit Media view, called "Replace Media". This is where you can upload a new file to replace the old one.
535
536 = I replaced a file, but it didn't change! =
537
538 There are two main reasons this would happen.
539
540 First, make sure you are not viewing a cached version of the file, especially if you replaced an image. Press "Refresh" in your browser to make sure.
541
542 Second, if the file really looks unchanged, make sure WordPress has write permissions to the files in your uploads folder. If you have ever moved your WP installation (maybe when you moved it to a new server), the permissions on your uploaded files are commonly reset so that WordPress no longer has permissions to change the files. If you don't know how to do this, contact your web server operator.
543
544 == Screenshots ==
545
546 1. The new link in the media library.
547 2. The replace media-button as seen in the "Edit media" view.
548 3. The upload options.
549 4. Get the file ID in the edit file URL
550
551 == Wishlist / Coming attractions ==
552
553 Do you have suggestions? Feel free to contact ShortPixel <a href="https://shortpixel.com/contact" target="_blank">here</a>
554
555
556 == Contribute ==
557
558 Want to help us improve the plugin feel free to submit PRs via GitHub <a href="https://github.com/short-pixel-optimizer/enable-media-replace" target="_blank">here</a>.
1 // Straight from jquery UI css
2
3 .ui-widget-content {
4 border: 1px solid #dddddd;
5 background: #ffffff;
6 color: #333333;
7 }
8
9
10 .ui-widget-overlay {
11 position: fixed;
12 top: 0;
13 left: 0;
14 width: 100%;
15 height: 100%;
16 }
17 .ui-datepicker {
18 width: 17em;
19 padding: .2em .2em 0;
20 display: none;
21 }
22 .ui-datepicker .ui-datepicker-header {
23 position: relative;
24 padding: .2em 0;
25 }
26 .ui-datepicker .ui-datepicker-prev,
27 .ui-datepicker .ui-datepicker-next {
28 position: absolute;
29 top: 2px;
30 width: 1.8em;
31 height: 1.8em;
32 }
33 .ui-datepicker .ui-datepicker-prev-hover,
34 .ui-datepicker .ui-datepicker-next-hover {
35 top: 1px;
36 }
37 .ui-datepicker .ui-datepicker-prev {
38 left: 2px;
39 }
40 .ui-datepicker .ui-datepicker-next {
41 right: 2px;
42 }
43 .ui-datepicker .ui-datepicker-prev-hover {
44 left: 1px;
45 }
46 .ui-datepicker .ui-datepicker-next-hover {
47 right: 1px;
48 }
49 .ui-datepicker .ui-datepicker-prev span,
50 .ui-datepicker .ui-datepicker-next span {
51 display: block;
52 position: absolute;
53 left: 50%;
54 margin-left: -8px;
55 top: 50%;
56 margin-top: -8px;
57 }
58 .ui-datepicker .ui-datepicker-title {
59 margin: 0 2.3em;
60 line-height: 1.8em;
61 text-align: center;
62 }
63 .ui-datepicker .ui-datepicker-title select {
64 font-size: 1em;
65 margin: 1px 0;
66 }
67 .ui-datepicker select.ui-datepicker-month,
68 .ui-datepicker select.ui-datepicker-year {
69 width: 45%;
70 }
71 .ui-datepicker table {
72 width: 100%;
73 font-size: .9em;
74 border-collapse: collapse;
75 margin: 0 0 .4em;
76 }
77 .ui-datepicker th {
78 padding: .7em .3em;
79 text-align: center;
80 font-weight: bold;
81 border: 0;
82 }
83 .ui-datepicker td {
84 border: 0;
85 padding: 1px;
86 }
87 .ui-datepicker td span,
88 .ui-datepicker td a {
89 display: block;
90 padding: .2em;
91 text-align: center;
92 text-decoration: none;
93 }
94 .ui-datepicker .ui-datepicker-buttonpane {
95 background-image: none;
96 margin: .7em 0 0 0;
97 padding: 0 .2em;
98 border-left: 0;
99 border-right: 0;
100 border-bottom: 0;
101 }
102 .ui-datepicker .ui-datepicker-buttonpane button {
103 float: right;
104 margin: .5em .2em .4em;
105 cursor: pointer;
106 padding: .2em .6em .3em .6em;
107 width: auto;
108 overflow: visible;
109 }
110 .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
111 float: left;
112 }
113
114 /* with multiple calendars */
115 .ui-datepicker.ui-datepicker-multi {
116 width: auto;
117 }
118 .ui-datepicker-multi .ui-datepicker-group {
119 float: left;
120 }
121 .ui-datepicker-multi .ui-datepicker-group table {
122 width: 95%;
123 margin: 0 auto .4em;
124 }
125 .ui-datepicker-multi-2 .ui-datepicker-group {
126 width: 50%;
127 }
128 .ui-datepicker-multi-3 .ui-datepicker-group {
129 width: 33.3%;
130 }
131 .ui-datepicker-multi-4 .ui-datepicker-group {
132 width: 25%;
133 }
134 .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
135 .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
136 border-left-width: 0;
137 }
138 .ui-datepicker-multi .ui-datepicker-buttonpane {
139 clear: left;
140 }
141 .ui-datepicker-row-break {
142 clear: both;
143 width: 100%;
144 font-size: 0;
145 }
146
147 /* RTL support */
148 .ui-datepicker-rtl {
149 direction: rtl;
150 }
151 .ui-datepicker-rtl .ui-datepicker-prev {
152 right: 2px;
153 left: auto;
154 }
155 .ui-datepicker-rtl .ui-datepicker-next {
156 left: 2px;
157 right: auto;
158 }
159 .ui-datepicker-rtl .ui-datepicker-prev:hover {
160 right: 1px;
161 left: auto;
162 }
163 .ui-datepicker-rtl .ui-datepicker-next:hover {
164 left: 1px;
165 right: auto;
166 }
167 .ui-datepicker-rtl .ui-datepicker-buttonpane {
168 clear: right;
169 }
170 .ui-datepicker-rtl .ui-datepicker-buttonpane button {
171 float: left;
172 }
173 .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
174 .ui-datepicker-rtl .ui-datepicker-group {
175 float: right;
176 }
177 .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
178 .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
179 border-right-width: 0;
180 border-left-width: 1px;
181 }
182
183 /* Icons */
184 .ui-datepicker .ui-icon {
185 display: block;
186 text-indent: -99999px;
187 overflow: hidden;
188 background-repeat: no-repeat;
189 left: .5em;
190 top: .3em;
191 }
192
193 .ui-icon {
194 width: 16px;
195 height: 16px;
196 }
197 .ui-icon,
198 .ui-widget-content .ui-icon {
199 background-image: url("../img/ui-icons_444444_256x240.png");
200 }
201 .ui-widget-header .ui-icon {
202 background-image: url("../img/ui-icons_444444_256x240.png");
203 }
204 .ui-state-hover .ui-icon,
205 .ui-state-focus .ui-icon,
206 .ui-button:hover .ui-icon,
207 .ui-button:focus .ui-icon {
208 background-image: url("../img/ui-icons_555555_256x240.png");
209 }
210 .ui-state-active .ui-icon,
211 .ui-button:active .ui-icon {
212 background-image: url("../img/ui-icons_ffffff_256x240.png");
213 }
214 .ui-state-highlight .ui-icon,
215 .ui-button .ui-state-highlight.ui-icon {
216 background-image: url("../img/ui-icons_777620_256x240.png");
217 }
218
219 /* positioning */
220 .ui-icon-blank { background-position: 16px 16px; }
221 .ui-icon-caret-1-n { background-position: 0 0; }
222 .ui-icon-caret-1-ne { background-position: -16px 0; }
223 .ui-icon-caret-1-e { background-position: -32px 0; }
224 .ui-icon-caret-1-se { background-position: -48px 0; }
225 .ui-icon-caret-1-s { background-position: -65px 0; }
226 .ui-icon-caret-1-sw { background-position: -80px 0; }
227 .ui-icon-caret-1-w { background-position: -96px 0; }
228 .ui-icon-caret-1-nw { background-position: -112px 0; }
229 .ui-icon-caret-2-n-s { background-position: -128px 0; }
230 .ui-icon-caret-2-e-w { background-position: -144px 0; }
231 .ui-icon-triangle-1-n { background-position: 0 -16px; }
232 .ui-icon-triangle-1-ne { background-position: -16px -16px; }
233 .ui-icon-triangle-1-e { background-position: -32px -16px; }
234 .ui-icon-triangle-1-se { background-position: -48px -16px; }
235 .ui-icon-triangle-1-s { background-position: -65px -16px; }
236 .ui-icon-triangle-1-sw { background-position: -80px -16px; }
237 .ui-icon-triangle-1-w { background-position: -96px -16px; }
238 .ui-icon-triangle-1-nw { background-position: -112px -16px; }
239 .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
240 .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
241 .ui-icon-arrow-1-n { background-position: 0 -32px; }
242 .ui-icon-arrow-1-ne { background-position: -16px -32px; }
243 .ui-icon-arrow-1-e { background-position: -32px -32px; }
244 .ui-icon-arrow-1-se { background-position: -48px -32px; }
245 .ui-icon-arrow-1-s { background-position: -65px -32px; }
246 .ui-icon-arrow-1-sw { background-position: -80px -32px; }
247 .ui-icon-arrow-1-w { background-position: -96px -32px; }
248 .ui-icon-arrow-1-nw { background-position: -112px -32px; }
249 .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
250 .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
251 .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
252 .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
253 .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
254 .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
255 .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
256 .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
257 .ui-icon-arrowthick-1-n { background-position: 1px -48px; }
258 .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
259 .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
260 .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
261 .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
262 .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
263 .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
264 .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
265 .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
266 .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
267 .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
268 .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
269 .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
270 .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
271 .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
272 .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
273 .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
274 .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
275 .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
276 .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
277 .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
278 .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
279 .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
280 .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
281 .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
282 .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
283 .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
284 .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
285 .ui-icon-arrow-4 { background-position: 0 -80px; }
286 .ui-icon-arrow-4-diag { background-position: -16px -80px; }
287 .ui-icon-extlink { background-position: -32px -80px; }
288 .ui-icon-newwin { background-position: -48px -80px; }
289 .ui-icon-refresh { background-position: -64px -80px; }
290 .ui-icon-shuffle { background-position: -80px -80px; }
291 .ui-icon-transfer-e-w { background-position: -96px -80px; }
292 .ui-icon-transferthick-e-w { background-position: -112px -80px; }
293 .ui-icon-folder-collapsed { background-position: 0 -96px; }
294 .ui-icon-folder-open { background-position: -16px -96px; }
295 .ui-icon-document { background-position: -32px -96px; }
296 .ui-icon-document-b { background-position: -48px -96px; }
297 .ui-icon-note { background-position: -64px -96px; }
298 .ui-icon-mail-closed { background-position: -80px -96px; }
299 .ui-icon-mail-open { background-position: -96px -96px; }
300 .ui-icon-suitcase { background-position: -112px -96px; }
301 .ui-icon-comment { background-position: -128px -96px; }
302 .ui-icon-person { background-position: -144px -96px; }
303 .ui-icon-print { background-position: -160px -96px; }
304 .ui-icon-trash { background-position: -176px -96px; }
305 .ui-icon-locked { background-position: -192px -96px; }
306 .ui-icon-unlocked { background-position: -208px -96px; }
307 .ui-icon-bookmark { background-position: -224px -96px; }
308 .ui-icon-tag { background-position: -240px -96px; }
309 .ui-icon-home { background-position: 0 -112px; }
310 .ui-icon-flag { background-position: -16px -112px; }
311 .ui-icon-calendar { background-position: -32px -112px; }
312 .ui-icon-cart { background-position: -48px -112px; }
313 .ui-icon-pencil { background-position: -64px -112px; }
314 .ui-icon-clock { background-position: -80px -112px; }
315 .ui-icon-disk { background-position: -96px -112px; }
316 .ui-icon-calculator { background-position: -112px -112px; }
317 .ui-icon-zoomin { background-position: -128px -112px; }
318 .ui-icon-zoomout { background-position: -144px -112px; }
319 .ui-icon-search { background-position: -160px -112px; }
320 .ui-icon-wrench { background-position: -176px -112px; }
321 .ui-icon-gear { background-position: -192px -112px; }
322 .ui-icon-heart { background-position: -208px -112px; }
323 .ui-icon-star { background-position: -224px -112px; }
324 .ui-icon-link { background-position: -240px -112px; }
325 .ui-icon-cancel { background-position: 0 -128px; }
326 .ui-icon-plus { background-position: -16px -128px; }
327 .ui-icon-plusthick { background-position: -32px -128px; }
328 .ui-icon-minus { background-position: -48px -128px; }
329 .ui-icon-minusthick { background-position: -64px -128px; }
330 .ui-icon-close { background-position: -80px -128px; }
331 .ui-icon-closethick { background-position: -96px -128px; }
332 .ui-icon-key { background-position: -112px -128px; }
333 .ui-icon-lightbulb { background-position: -128px -128px; }
334 .ui-icon-scissors { background-position: -144px -128px; }
335 .ui-icon-clipboard { background-position: -160px -128px; }
336 .ui-icon-copy { background-position: -176px -128px; }
337 .ui-icon-contact { background-position: -192px -128px; }
338 .ui-icon-image { background-position: -208px -128px; }
339 .ui-icon-video { background-position: -224px -128px; }
340 .ui-icon-script { background-position: -240px -128px; }
341 .ui-icon-alert { background-position: 0 -144px; }
342 .ui-icon-info { background-position: -16px -144px; }
343 .ui-icon-notice { background-position: -32px -144px; }
344 .ui-icon-help { background-position: -48px -144px; }
345 .ui-icon-check { background-position: -64px -144px; }
346 .ui-icon-bullet { background-position: -80px -144px; }
347 .ui-icon-radio-on { background-position: -96px -144px; }
348 .ui-icon-radio-off { background-position: -112px -144px; }
349 .ui-icon-pin-w { background-position: -128px -144px; }
350 .ui-icon-pin-s { background-position: -144px -144px; }
351 .ui-icon-play { background-position: 0 -160px; }
352 .ui-icon-pause { background-position: -16px -160px; }
353 .ui-icon-seek-next { background-position: -32px -160px; }
354 .ui-icon-seek-prev { background-position: -48px -160px; }
355 .ui-icon-seek-end { background-position: -64px -160px; }
356 .ui-icon-seek-start { background-position: -80px -160px; }
357 /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
358 .ui-icon-seek-first { background-position: -80px -160px; }
359 .ui-icon-stop { background-position: -96px -160px; }
360 .ui-icon-eject { background-position: -112px -160px; }
361 .ui-icon-volume-off { background-position: -128px -160px; }
362 .ui-icon-volume-on { background-position: -144px -160px; }
363 .ui-icon-power { background-position: 0 -176px; }
364 .ui-icon-signal-diag { background-position: -16px -176px; }
365 .ui-icon-signal { background-position: -32px -176px; }
366 .ui-icon-battery-0 { background-position: -48px -176px; }
367 .ui-icon-battery-1 { background-position: -64px -176px; }
368 .ui-icon-battery-2 { background-position: -80px -176px; }
369 .ui-icon-battery-3 { background-position: -96px -176px; }
370 .ui-icon-circle-plus { background-position: 0 -192px; }
371 .ui-icon-circle-minus { background-position: -16px -192px; }
372 .ui-icon-circle-close { background-position: -32px -192px; }
373 .ui-icon-circle-triangle-e { background-position: -48px -192px; }
374 .ui-icon-circle-triangle-s { background-position: -64px -192px; }
375 .ui-icon-circle-triangle-w { background-position: -80px -192px; }
376 .ui-icon-circle-triangle-n { background-position: -96px -192px; }
377 .ui-icon-circle-arrow-e { background-position: -112px -192px; }
378 .ui-icon-circle-arrow-s { background-position: -128px -192px; }
379 .ui-icon-circle-arrow-w { background-position: -144px -192px; }
380 .ui-icon-circle-arrow-n { background-position: -160px -192px; }
381 .ui-icon-circle-zoomin { background-position: -176px -192px; }
382 .ui-icon-circle-zoomout { background-position: -192px -192px; }
383 .ui-icon-circle-check { background-position: -208px -192px; }
384 .ui-icon-circlesmall-plus { background-position: 0 -208px; }
385 .ui-icon-circlesmall-minus { background-position: -16px -208px; }
386 .ui-icon-circlesmall-close { background-position: -32px -208px; }
387 .ui-icon-squaresmall-plus { background-position: -48px -208px; }
388 .ui-icon-squaresmall-minus { background-position: -64px -208px; }
389 .ui-icon-squaresmall-close { background-position: -80px -208px; }
390 .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
391 .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
392 .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
393 .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
394 .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
395 .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
396
397 /* Corner radius */
398 .ui-corner-all,
399 .ui-corner-top,
400 .ui-corner-left,
401 .ui-corner-tl {
402 border-top-left-radius: 3px;
403 }
404 .ui-corner-all,
405 .ui-corner-top,
406 .ui-corner-right,
407 .ui-corner-tr {
408 border-top-right-radius: 3px;
409 }
410 .ui-corner-all,
411 .ui-corner-bottom,
412 .ui-corner-left,
413 .ui-corner-bl {
414 border-bottom-left-radius: 3px;
415 }
416 .ui-corner-all,
417 .ui-corner-bottom,
418 .ui-corner-right,
419 .ui-corner-br {
420 border-bottom-right-radius: 3px;
421 }
1
2 .enable-media-replace.emr-screen
3 {
4
5 width: 90%;
6 background: #fff;
7 border: 1px solid #fff;
8 position: relative;
9 margin: 30px 0;
10
11 h3 {
12 font-size: 24px;
13 position: absolute;
14 top: -15px;
15 left: 30px;
16 padding: 16px 8px;
17 border: 1px solid #000;
18 background: #fff;
19 margin: 0;
20 }
21 .content
22 {
23 padding: 32px;
24
25 }
26 h1 {
27 font-weight: 700;
28 }
29
30 p { font-size: 14px; }
31
32
33 .hide { display: none; }
34
35 &.error-screen {
36 h1 { color: #ff0000; }
37 }
38
39 }
1 @import 'datepicker';
2 @import 'screens';
3
4 #emr-drop-area-active
5 {
6 // border: 4px dashed #b4b9be;
7 // max-width: 600px;
8 position: fixed;
9 left: 0;
10 right: 0;
11 top: 0;
12 bottom: 0;
13 z-index: 9999;
14 display: block;
15 background: rgba(0,0,0,0.5);
16 border: 6px dashed #AAA;
17
18 h3 {
19 font-size: 36px;
20 color: #ddd;
21 width: 200px;
22 left: 50%;
23 top: 50%;
24 position:absolute;
25 }
26
27 .drop-wrapper
28 {
29 margin: 0 auto;
30 }
31 }
32
33 // The main form.
34 .emr_upload_form
35 {
36 form
37 {
38 display: flex; //editor and upsell
39 .upsell-wrapper
40 {
41 margin-left: 10px;
42 }
43 }
44
45 .editor-wrapper
46 {
47 width: 100%;
48 }
49 .emr_drop_area {
50 display: none;
51 }
52
53 .wrapper
54 {
55 // margin: 15px 0;
56 padding: 18px;
57 border: 1px solid #ccc;
58
59 .section-header
60 {
61 font-size: 18px;
62 //text-align: center;
63 border-bottom: 1px solid #ccc;
64 padding: 6px 0;
65 margin: 0 0 15px 0;
66 }
67 }
68
69 .explainer {
70 font-size: 14px;
71 color: #555;
72 max-width: 800px;
73 margin: 20px 0;
74 .underline {
75 text-decoration-style: dotted;
76 text-decoration-line: underline;
77 }
78
79
80 }
81
82 .image_chooser.wrapper
83 {
84 min-height: 350px;
85
86
87 .image_previews
88 {
89 margin: 15px 0;
90 position: relative;
91
92 .image_placeholder
93 {
94 position: relative;
95 display: inline-block;
96 margin-right: 25px;
97 margin-bottom: 10px;
98 border: 1px solid #ddd;
99 vertical-align: top;
100 box-sizing: border-box;
101 background: #fff;
102 max-width: calc(50% - 25px);
103
104 img { object-fit: contain; }
105
106 .upload-title
107 {
108 text-align: center;
109 position: absolute;
110 padding-top: 8px;
111 line-height: 20px;
112 font-weight: 700;
113 width: 100%;
114 left: 0;
115 top: 0;
116 }
117 #upload-file {
118 display: none;
119 }
120 .textlayer
121 {
122 font-size: 25px;
123 line-height: 25px;
124 opacity: 0.7;
125 position: absolute;
126 color: #ccc;
127 left: 48%;
128 top: 50%;
129 transform: translate(-50%, -50%);
130 border: 1px dashed #eee;
131 background-color: #333;
132 padding: 8px;
133 z-index: 5;
134 //max-width: 100%;
135 }
136 .dashicons
137 {
138 font-size: 60px;
139 position: absolute;
140 top: 50%;
141 margin-top: -30px;
142 left: 50%;
143 margin-left: -30px;
144 opacity: 0.5;
145
146 }
147 .image_size
148 {
149 text-align: center;
150 position: absolute;
151 bottom: -25px;
152 width: 100%;
153 }
154 .drag-and-drop-title
155 {
156 text-align: center;
157 font-weight: 400;
158 font-size: 14px;
159 width: 100%;
160 height: 100%;
161 position: absolute;
162 cursor: pointer;
163 top: 0;
164 left: 0;
165 z-index: 1;
166 span {
167 position: absolute;
168 // left: 46%;
169 top: 50%;
170 transform: translate(-50%,-50%);
171 padding: 5px 10px;
172 width: 100%;
173
174 }
175 }
176 &.is_image
177 {
178 .dashicons::before, .dashicons { display: none }
179
180
181 }
182 &.not_image
183 {
184 img { display: none; }
185 .textlayer { display: none; }
186 &.is_document{
187 .textlayer {
188 font-size: 18px;
189 line-height: 20px;
190 display: block;
191
192 }
193 }
194 } // not_image
195 } // image_placeholder
196 } // image_previews
197 } // wrapper
198
199 .form-error, .form-warning
200 {
201 background: #fff;
202 padding: 8px;
203 border-left: 4px solid #ff0000;
204 // display: inline-block;
205 margin: 10px 0;
206 display: none;
207 p {
208 margin: 0;
209 font-size: 12px;
210 font-weight: 700;
211 }
212
213 }
214 .form-warning
215 {
216 border-left: 4px solid #ffb900;
217 }
218
219 .option-flex-wrapper
220 {
221 display: flex;
222 }
223
224 .replace_type.wrapper
225 {
226 flex: 1;
227 border: 1px solid #ccc;
228 margin: 15px 0;
229 .option
230 {
231 position: relative;
232 z-index: 1;
233 &.disabled
234 {
235 // color: #eee;
236 }
237 label
238 {
239 font-size: 1.2em;
240 }
241 .nofeature-notice
242 {
243 border: 1px solid #ccc;
244 padding: 8px;
245 margin: 0;
246 position: absolute;
247 left: 0;
248 right: 0;
249 top: 0;
250 bottom: 0;
251 opacity: 0.8;
252 z-index: 9;
253 background: #444;
254 p {
255 text-align: center;
256 color: #fff;
257 margin: 15px 0;
258 }
259 }
260 }
261
262 }
263
264 .options.wrapper
265 {
266 flex: 1;
267 border: 1px solid #ccc;
268 padding: 15px;
269 margin: 15px 0 15px 35px;
270 .custom_date
271 {
272 .emr_datepicker {
273 width: 150px;
274 }
275 .emr_hour, .emr_minute
276 {
277 width: 45px;
278 }
279 }
280 .replace_custom_date
281 {
282 margin-left: 5px;
283 &:hover {
284 text-decoration: underline;
285 cursor: pointer
286 }
287 }
288 ul
289 {
290 li
291 {
292 input
293 {
294 margin-right: 8px;
295 }
296 }
297 }
298 .option
299 {
300 label { vertical-align: top; }
301 }
302 .small
303 {
304 font-size: 10px;
305 vertical-align: top;
306 margin-left: 8px;
307 }
308 .custom_date
309 {
310 margin: 8px 0 0 25px;
311 visibility: hidden;
312 opacity: 0;
313 span.field-title {
314 display: inline-block;
315 margin-bottom: 4px;
316 color: #444;
317 //margin-left: 8px;
318 font-size: 12px;
319 width: 100%;
320 text-align: left;
321 vertical-align: middle;
322 line-height: 26px;
323 &::before
324 {
325 font-size: 20px;
326 vertical-align: top;
327 margin-right: 4px;
328 }
329 }
330 } // custom_date
331 .location_option
332 {
333 display: none;
334 margin-top: 12px;
335 label
336 {
337 vertical-align: baseline;
338 margin-right: 8px;
339 }
340 }
341 }
342
343 .form_controls.wrapper
344 {
345 clear: both;
346 margin: 8px 0 15px 0;
347 border: 0;
348 padding: 0;
349 .button
350 {
351 padding-left: 20px;
352 padding-right: 20px;
353 }
354 }
355
356 .shortpixel.notice
357 {
358 padding: 12px;
359 }
360
361 // *** UPSELL SNIPPET
362 .shortpixel-offer
363 {
364 background: #fff;
365 width: 250px;
366 min-height: 270px;
367 border: 1px solid #ccc;
368 padding: 10px;
369 //margin: 0 0 10px 25px;
370 margin-bottom: 10px;
371 float: right;
372 clear: both;
373 background-color: #dcfdff;
374 h3 {
375 color: #00d0e5;
376 font-size: 18px;
377 text-align: center;
378 margin: 0;
379 line-height: 1.3em;
380 }
381 h4 {
382 // margin: 0;
383 font-size: 16px;
384 text-align: center;
385 }
386 .red { color: #ff0000; }
387 .cyan { color: #00d0e5; }
388 .grey { color: grey; }
389 .ucase { text-transform: uppercase; }
390 a {
391 text-decoration: none
392 }
393 .button-wrapper
394 {
395 text-align: center;
396 margin-top: 35px;
397 a {
398 background-color: #ff0000;
399 color: #fff;
400 display: inline-block;
401 padding: 8px;
402 text-decoration: none;
403 font-weight: 700;
404 font-size: 20px;
405 text-transform: uppercase;
406 }
407 }
408 .hidden { display: none !important; }
409
410 .img-wrapper
411 {
412 text-align: center;
413 margin: 0 0 25px 0;
414 img { max-width: 140px; max-height: 140px; margin: 0; }
415 }
416
417
418 &.spio // shortpixel
419 {
420 }
421 &.site-speed // Site speed
422 {
423 }
424 &.envira-shortpixel-install // envira
425 {
426 background: #fff;
427 }
428 &.theme-offer
429 {
430 background: #fff;
431 img {
432 max-width: 100%;
433 }
434 }
435 }
436
437 @media( max-width: 1200px)
438 {
439 .image_previews
440 {
441 text-align: center;
442 }
443 .option-flex-wrapper {
444 flex-direction: column;
445 .options.wrapper
446 { margin-left: 0;}
447 }
448 }
449 @media (max-width: 960px)
450 {
451 .upsell-wrapper { display: none; }
452 }
453 @media (max-width: 450px )
454 {
455 .replace_custom_date_wrapper {
456 display: block;
457 margin-top: 15px;
458 font-size: 16px;
459
460 }
461 .location_option {
462 label {
463 margin: 25px 0;
464 display: inline-block;
465 }
466
467 }
468 }
469 } // emr_upload_form
1
2 /* Styling for the edit attachment screen */
3 #emr-replace-box, #emr-showthumbs-box
4 {
5 .previewwrapper
6 {
7 display: inline-block;
8 position: relative;
9 clear: both;
10 margin: 3px 0;
11
12 img { max-width: 100%; }
13 span.label
14 {
15 font-size: 14px;
16 color: #fff;
17 position: absolute;
18 line-height: 16px;
19 margin-top: -8px;
20 top: 50%;
21 left: 0; right: 0;
22 background: rgba(0,0,0,0.5);
23 text-align: center;
24 padding: 4px 0;
25
26
27 }
28
29 }
30
31 }
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 }
1 <?php
2 if ( ! defined( 'ABSPATH' ) )
3 exit; // Exit if accessed directly.
4
5 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
6 use EnableMediaReplace\Notices\NoticeController as Notices;
7
8 /* Simple class for updating thumbnails.
9 */
10 class ThumbnailUpdater
11 {
12 protected $attach_id;
13 protected $oldMeta = array();
14 protected $newMeta = array();
15
16 protected $convertArray = array();
17 protected $relPath;
18
19 protected $post_table;
20
21 public function __construct($id)
22 {
23 $this->attach_id = intval($id);
24
25 global $wpdb;
26 $table_name = $wpdb->prefix . "posts";
27 // $postmeta_table_name = $wpdb->prefix . "postmeta";
28
29 $this->post_table = $table_name;
30 }
31
32 public function setOldMetadata($metadata)
33 {
34 if (isset($metadata['sizes']))
35 $this->oldMeta = $metadata;
36 }
37
38 public function setNewMetadata($metadata)
39 {
40 if (isset($metadata['sizes']))
41 $this->newMeta = $metadata;
42
43
44 // extract month prefix to prevent overwriting wrong images.
45 $file = $metadata['file'];
46 $pos = strrpos($metadata['file'], '/');
47 $month_path = substr($file, 0, $pos);
48 $this->relPath = trailingslashit($month_path);
49 }
50
51
52 public function updateThumbnails()
53 {
54 if (count($this->oldMeta) == 0 || count($this->newMeta) == 0)
55 return false;
56
57 $convertArray = array();
58 foreach($this->oldMeta['sizes'] as $sizeName => $data)
59 {
60 if (isset($this->newMeta['sizes'][$sizeName]))
61 {
62
63 //in some rare cases 'file' is missing
64 $oldFile = isset($data['file']) ? $data['file'] : null;
65 if(is_array($oldFile)) { $oldFile = $oldFile[0];} // HelpScout case 709692915
66 if(empty($oldFile)) {
67 return false; //make sure we don't replace in this case as we will break the URLs for all the images in the folder.
68 }
69 $newFile = $this->newMeta['sizes'][$sizeName]['file'];
70
71 // if images are not same size.
72 if ($oldFile != $newFile)
73 {
74 $this->convertArray[] = array('imageFrom' => $this->relPath . $oldFile, 'imageTo' => $this->relPath . $newFile );
75 }
76
77 }
78 else {
79 $this->findNearestSize($data, $sizeName);
80 }
81
82 }
83 $this->updateDatabase();
84
85 }
86
87 protected function updateDatabase()
88 {
89 global $wpdb;
90 $sql = "UPDATE " . $this->post_table . " set post_content = REPLACE(post_content, %s, %s)";
91
92 Log::addDebug('Thumbnail Updater - Converting Thumbnails for sizes', $this->convertArray);
93 foreach($this->convertArray as $convert_item)
94 {
95 $from = $convert_item['imageFrom'];
96 $to = $convert_item['imageTo'];
97
98 $replace_sql = $wpdb->prepare($sql, $from, $to );
99 $wpdb->query($replace_sql);
100 }
101 }
102
103 /** FindNearestsize
104 * This works on the assumption that when the exact image size name is not available, find the nearest width with the smallest possible difference to impact the site the least.
105 */
106 protected function findNearestSize($oldData, $sizeName)
107 {
108 $old_width = $oldData['width']; // the width from size not in new image
109 $new_width = $this->newMeta['width']; // default check - new width on image
110
111 $diff = abs($old_width - $new_width);
112 $closest_file = str_replace($this->relPath, '', $this->newMeta['file']);
113
114 foreach($this->newMeta['sizes'] as $sizeName => $data)
115 {
116 $thisdiff = abs($old_width - $data['width']);
117
118 if ( $thisdiff < $diff )
119 {
120 $closest_file = $data['file'];
121 if(is_array($closest_file)) { $closest_file = $closest_file[0];} // HelpScout case 709692915
122 if(!empty($closest_file)) {
123 $diff = $thisdiff;
124 $found_metasize = true;
125 }
126 }
127 }
128
129 if(empty($closest_file)) return;
130 $oldFile = $oldData['file'];
131 if(is_array($oldFile)) { $oldFile = $oldFile[0];} // HelpScout case 709692915
132 if(empty($oldFile)) {
133 return; //make sure we don't replace in this case as we will break the URLs for all the images in the folder.
134 }
135 $this->convertArray[] = array('imageFrom' => $this->relPath . $oldFile, 'imageTo' => $this->relPath . $closest_file);
136
137 }
138
139
140 }
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 use \EnableMediaReplace\Controller\ReplaceController as ReplaceController;
8
9 if (! defined('ABSPATH')) {
10 exit; // Exit if accessed directly.
11 }
12
13 $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : null;
14 if (is_null($key) || strlen($key) == 0)
15 {
16 wp_die(esc_html__('Error while sending form (no key). Please try again.', 'enable-media-replace'));
17 }
18
19 $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; // sanitize, post_id.
20 if (is_null($post_id)) {
21 wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));
22 }
23
24 $attachment = get_post($post_id);
25
26 if (! emr()->checkImagePermission($attachment)) {
27 wp_die(esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace'));
28 }
29
30 $uiHelper = emr()->uiHelper();
31
32 $replaceController = new ReplaceController($post_id);
33
34 //$replacer->setMode(\EnableMediaReplace\Replacer::MODE_REPLACE);
35
36 //$datetime = current_time('mysql');
37 //$replacer->setTimeMode( \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED, $datetime);
38
39 $api = new Api();
40 $result = $api->handleDownload($key);
41
42 if (! $result->success)
43 {
44 die($result->message);
45 }
46
47 // When are 1-1 replacing.
48 $source = $replacer->getSourceFile();
49
50 $redirect_error = $uiHelper->getFailedRedirect($post_id);
51 $redirect_success = $uiHelper->getSuccesRedirect($post_id);
52
53 if (! file_exists($result->image))
54 {
55 Log::addError('Download File not here', $result->image);
56 exit(__('Temp file does not exist', 'enable-media-replace'));
57 }
58
59
60 $params = array(
61 'replace_type' => \EnableMediaReplace\Replacer::MODE_REPLACE,
62 'timestamp_replace' => \EnableMediaReplace\Replacer::TIME_UPDATEMODIFIED,
63 'new_date' => current_time('mysql'),
64 'updateFile' => $result->image,
65
66 );
67 $replaceController->setupParams($params);
68
69
70 try {
71 $result = $replaceController->run();
72 } catch (\RunTimeException $e) {
73 print_r($e->getMessage());
74 Log::addError($e->getMessage());
75 die;
76
77 }
78
79 if (is_null($result)) {
80 wp_safe_redirect($redirect_error);
81 exit();
82 }
83
84 $noticeController = Notices::getInstance();
85 $notice = Notices::addSuccess('<p>' . __('File successfully replaced', 'enable-media-replace') . '</p>');
86 $notice->is_removable = false;
87 $noticeController->update();
88
89 wp_redirect($redirect_success);
90 exit();
1 <?php
2 namespace EnableMediaReplace;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 //use \EnableMediaReplace\UIHelper;
9 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
10
11
12 ?>
13
14 <div class='enable-media-replace emr-screen error-screen'>
15 <h3 class='title'><?php _e('Enable Media Replace', 'enable-media-replace'); ?></h3>
16
17 <div class='content'>
18 <h1><?php _e('An error occured', 'enable-media-replace'); ?></h1>
19 <p class="error-message"> <?php echo $view->errorMessage; ?> </p>
20
21 <?php if (property_exists($view, 'errorDescription'))
22 {
23 echo '<p class="description">' . $view->errorDescription . '</p>';
24 } ?>
25
26 <p><?php printf(esc_html__('You can return to %s previous page %s','enable-media-replace'),
27 '<a href="javascript:history.back()">', '</a>'); ?></p>
28
29
30 <p><?php printf(esc_html__('If you need help, please see the plugin %sdocumentation%s. It contains clear solutions to most of the problems you may encounter when using our plugin.', 'enable-media-replace'), '<a href="https://shortpixel.com/knowledge-base/category/308-enable-media-replace" target="_blank">', '</a>'); ?></p>
31 </div>
32 </div> <!--- screen -->
33
34
35 <?php
36 require_once ABSPATH . 'wp-admin/admin-footer.php';
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 = $view->attachment->ID;
15 //$attachment_id = intval($_GET['attachment_id']);
16 //$attachment = get_post($attachment_id);
17
18 //$replacer = new Replacer($attachment_id);
19 //$file = $replacer->getSourceFile();
20
21 $uiHelper->setPreviewSizes();
22 $uiHelper->setSourceSizes($attachment_id);
23
24 $base_image = $uiHelper->getPreviewImage($attachment_id, $view->sourceFile );
25 $replace_image = $uiHelper->getPreviewImage(-1, $view->sourceFile, 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 $replace_url = add_query_arg(array(
36 'page' => 'enable-media-replace/enable-media-replace.php',
37 'action' => 'media_replace',
38 'attachment_id' => $attachment_id,
39 ), admin_url("upload.php"));
40
41 ?>
42
43 <div class="wrap emr_upload_form" id="remove-background-form">
44
45 <form id="emr_replace_form" enctype="multipart/form-data" method="POST" action="<?php
46 echo $formurl; ?>" >
47
48 <?php wp_nonce_field('media_remove_background', 'emr_nonce'); ?>
49
50
51 <input type="hidden" name="ID" value="<?php echo intval($attachment_id); ?>" />
52 <input type='hidden' name='key' value='' />
53
54 <div class="editor-wrapper" >
55 <section class='image_chooser wrapper'>
56 <div class='section-header'> <?php esc_html_e( 'Remove Media Background', 'enable-media-replace' ); ?></div>
57 <div class='image_previews'>
58 <?php echo $base_image; ?>
59 <?php echo $replace_image ?>
60
61 </div>
62
63 <div class='bad-button'>
64 <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>
65
66 </div>
67
68 </section>
69
70 <p><a href="<?php echo esc_attr(wp_nonce_url($replace_url, 'media_replace')); ?>">
71 <?php esc_html_e('Replace this image with another one instead!', 'enable-media-replace'); ?>
72 </a></p>
73 <div class="option-flex-wrapper">
74 <section class="replace_type wrapper">
75 <div class="section-header"><?php esc_html_e('Background Removal Options', 'enable-media-replace'); ?></div>
76 <div class="option replace ">
77 <p>
78 <?php esc_html_e('If a CDN is used, remember to clear the cache for this image!', 'enable-media-replace'); ?>
79 </p>
80 <label for="transparent_background">
81 <input id="transparent_background" type="radio" name="background_type" value="transparent" <?php checked('transparent', $view->settings['bg_type']); ?> >
82 <?php esc_html_e('Transparent/white background', 'enable-media-replace'); ?>
83 </label>
84 <p class="howto">
85 <?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'); ?>
86 </p>
87 </div>
88 <div class="option searchreplace">
89 <label for="solid_background">
90 <input id="solid_background" type="radio" name="background_type" value="solid" <?php checked('solid', $view->settings['bg_type']); ?>>
91 <?php esc_html_e('Solid background', 'enable-media-replace'); ?>
92 </label>
93 <p class="howto">
94 <?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'); ?>
95 </p>
96 <div id="solid_selecter" style="display:none;">
97 <label for="bg_display_picker">
98 <p><?php esc_html_e('Background Color:','enable-media-replace'); ?> <strong>
99 <span style="text-transform: uppercase;" id="color_range">
100 <?php echo esc_attr($view->settings['bg_color']); ?></span>
101 </strong>
102 </p>
103 <input type="color" value="<?php echo esc_attr($view->settings['bg_color']); ?>" name="bg_display_picker" id="bg_display_picker" />
104 <input type="hidden" value="<?php echo esc_attr($view->settings['bg_color']); ?>" name="bg_color" id="bg_color" />
105 </label>
106 <hr>
107 <label for="bg_transparency">
108 <p><?php esc_html_e('Opacity:', 'enable-media-replace'); ?>
109 <strong>
110 <span id="transparency_range"><?php echo esc_attr($view->settings['bg_transparency']); ?></span>%</strong>
111 </p>
112 <input type="range" min="0" max="100" value="<?php echo esc_attr($view->settings['bg_transparency']); ?>" id="bg_transparency" />
113 </label>
114 </div>
115 </div>
116 </section>
117
118
119 </div>
120 <button type="button" class="button button-primary" id="remove_background_button"><?php esc_html_e('Preview', 'enable-media-replace'); ?></button>
121 <button type="submit" class="button button-primary" id="replace_image_button" disabled><?php esc_html_e('Replace', 'enable-media-replace'); ?></button>
122 <a class="button" href="javascript:history.back()"><?php esc_html_e('Cancel', 'enable-media-replace'); ?></a>
123 </div> <!--- editor wrapper -->
124 <?php include_once( 'upsell.php' ); ?>
125 </form>
126 </div>
1 <?php
2 namespace EnableMediaReplace;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 //use \EnableMediaReplace\UIHelper;
9 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
10 use EnableMediaReplace\Notices\NoticeController as Notices;
11 use EnableMediaReplace\Controller\ReplaceController as ReplaceController;
12
13
14 /**
15 * Uploadscreen for selecting and uploading new media file
16 *
17 * @author Måns Jonasson <http://www.mansjonasson.se>
18 * @copyright Måns Jonasson 13 sep 2010
19 * @version $Revision: 2303 $ | $Date: 2010-09-13 11:12:35 +0200 (ma, 13 sep 2010) $
20 * @package wordpress
21 * @subpackage enable-media-replace
22 *
23 */
24 if ( ! defined( 'ABSPATH' ) )
25 exit; // Exit if accessed directly.
26
27 if (!current_user_can('upload_files'))
28 wp_die( esc_html__('You do not have permission to upload files.', 'enable-media-replace') );
29
30
31 $attachment_id = $view->attachment->ID;
32 $settings = $view->settings;
33 $sourceFile = $view->sourceFile;
34
35 $uiHelper = emr()->uiHelper();
36
37 ?>
38
39 <div class="wrap emr_upload_form">
40
41 <div class='emr_drop_area' id='emr-drop-area'><h3><?php _e('Drop File', 'enable-media-replace'); ?></h3></div>
42
43 <h1><?php echo esc_html__("Replace Media Upload", "enable-media-replace"); ?></h1>
44
45 <?php
46
47 $formurl = $uiHelper->getFormUrl($attachment_id);
48 //$formurl = wp_nonce_url( $url, "media_replace_upload" );
49
50 if (FORCE_SSL_ADMIN) {
51 $formurl = str_replace("http:", "https:", $formurl);
52 }
53 ?>
54
55 <form enctype="multipart/form-data" method="POST" action="<?php echo $formurl; ?>">
56 <?php wp_nonce_field('media_replace_upload', 'emr_nonce'); ?>
57
58 <div class='editor-wrapper'>
59 <section class='image_chooser wrapper'>
60 <div class='section-header'> <?php _e('Select Replacement Media', 'enable-media-replace'); ?></div>
61
62 <!--
63 <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"), $sourceFile->getFileName() ); ?></strong></div>
64 -->
65
66 <input type="hidden" name="ID" value="<?php echo $attachment_id ?>" />
67
68 <p class='explainer'>
69 <?php printf(esc_html__(' You are about to replace %s in your media library. This will be %spermanent%s. %s You can click on the new image panel and select a file from your computer. You can also drag and drop a file into this window', 'enable-media-replace'), '<b class="underline" title="' . $sourceFile->getFullPath() . '">' . $sourceFile->getFileName() . '</b>', '<b>','</b>', '<br>' );
70 ?>
71 </p>
72
73 <p><?php printf(__('Maximum file size: <strong>%s</strong>','enable-media-replace'), size_format(wp_max_upload_size() ) ) ?></p>
74 <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>
75 </div>
76
77 <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>'); ?>
78
79 </p></div>
80
81 <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>
82
83 <div class='image_previews'>
84
85 <?php
86
87 if (wp_attachment_is('image', $attachment_id) || $view->sourceMime == 'application/pdf')
88 {
89 echo $uiHelper->getPreviewImage($attachment_id, $sourceFile);
90 echo $uiHelper->getPreviewImage(-1, $sourceFile, array('is_upload' => true));
91 }
92 else {
93
94 if (strlen($sourceFile->getFullPath()) == 0) // check if image in error state.
95 {
96 echo $uiHelper->getPreviewError(-1);
97 echo $uiHelper->getPreviewImage(-1, $sourceFile, array('is_upload' => true));
98 }
99 else {
100 echo $uiHelper->getPreviewFile($attachment_id, $sourceFile);
101 echo $uiHelper->getPreviewFile(-1, $sourceFile, array('is_upload' => true));
102 }
103
104 }
105 ?>
106 </div>
107 <?php
108 $url = admin_url("upload.php");
109 $url = add_query_arg(array(
110 'page' => 'enable-media-replace/enable-media-replace.php',
111 'action' => 'emr_prepare_remove',
112 'attachment_id' => $attachment_id,
113 ), $url);
114 ?>
115
116 <p>&nbsp;</p>
117 <?php if ($uiHelper->isBackgroundRemovable($view->attachment)): ?>
118 <div>
119
120 <a href="<?php echo wp_nonce_url( $url , 'emr_prepare_remove' ); ?>">
121 <?php _e('New! Click here to remove the background of this image!', 'enable-media-replace'); ?></a>
122 <br>
123 <br>
124 <input type="checkbox" id="remove_after_progress" name="remove_after_progress" value="<?php echo $attachment_id;?>">
125 <label for="remove_after_progress"><?php _e('Remove the background after replacing this image!' ,'enable-media-replace'); ?> </label>
126 </div>
127 <?php endif; ?>
128 </section>
129
130 <div class='option-flex-wrapper'>
131 <section class='replace_type wrapper'>
132 <div class='section-header'> <?php _e('Replacement Options', 'enable-media-replace'); ?></div>
133
134 <?php
135 // these are also used in externals, for checks.
136 do_action( 'emr_before_replace_type_options' ); ?>
137
138
139 <?php $enabled_search = apply_filters( 'emr_display_replace_type_options', true );
140 $search_disabled = (! $enabled_search) ? 'disabled' : '';
141 ?>
142 <div class='option replace <?php echo $search_disabled ?>'>
143 <label for="replace_type_1" ><input <?php checked('replace', $settings['replace_type']) ?> id="replace_type_1" type="radio" name="replace_type" value="replace" <?php echo $search_disabled ?> > <?php echo esc_html__("Just replace the file", "enable-media-replace"); ?>
144 </label>
145
146 <p class="howto">
147 <?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"), $sourceFile->getExtension(), $sourceFile->getFileName() ); ?>
148 </p>
149
150 <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'); ?>
151 </p>
152
153 <?php do_action('emr_after_search_type_options'); ?>
154 </div>
155
156 <?php $enabled_replacesearch = apply_filters( 'emr_enable_replace_and_search', true );
157 $searchreplace_disabled = (! $enabled_replacesearch) ? 'disabled' : '';
158 ?>
159
160 <div class="option searchreplace <?php echo $searchreplace_disabled ?>">
161 <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"); ?>
162 </label>
163
164 <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"), $sourceFile->getFileName() ); ?></p>
165
166 <!-- <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> -->
167
168 <?php do_action('emr_after_replace_type_options'); ?>
169 </div>
170
171 </section>
172 <section class='options wrapper'>
173 <div class='section-header'> <?php _e('Options', 'enable-media-replace'); ?></div>
174 <div class='option timestamp'>
175 <?php
176 $attachment_current_date = date_i18n('d/M/Y H:i', strtotime($view->attachment->post_date) );
177 $attachment_now_date = date_i18n('d/M/Y H:i' );
178
179 $time = current_time('mysql');
180 $date = $nowDate = new \dateTime($time); // default to now.
181 $attachmentDate = new \dateTime($view->attachment->post_date);
182
183
184 if ($settings['timestamp_replace'] == ReplaceController::TIME_CUSTOM)
185 {
186 $date = new \dateTime($settings['custom_date']);
187 }
188 ?>
189 <p><?php _e('When replacing the media, do you want to:', 'enable-media-replace'); ?></p>
190 <ul>
191 <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>
192 <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>
193 <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>
194 </ul>
195 <div class='custom_date'>
196
197 <span class='field-title dashicons dashicons-calendar'><?php _e('Custom Date', 'enable-media-replace'); ?></span>
198 <input type='text' name="custom_date" value="<?php echo $date->format(get_option('date_format')); ?>" id='emr_datepicker'
199 class='emr_datepicker' />
200
201 @ <input type='text' name="custom_hour" class='emr_hour' id='emr_hour' value="<?php echo $date->format('H') ?>" /> &nbsp;
202 <input type="text" name="custom_minute" class='emr_minute' id='emr_minute' value="<?php echo $date->format('i'); ?>" />
203 <input type="hidden" name="custom_date_formatted" value="<?php echo $date->format('Y-m-d'); ?>" />
204
205 <span class="replace_custom_date_wrapper">
206 <?php
207 printf('<a class="replace_custom_date" data-date="%s" data-hour="%s" data-min="%s" data-format="%s">%s</a>', $nowDate->format(get_option('date_format')), $nowDate->format('H'), $nowDate->format('i'), $nowDate->format('Y-m-d'), __('Now', 'enable-media-replace'));
208 echo " ";
209 printf('<a class="replace_custom_date" data-date="%s" data-hour="%s" data-min="%s" data-format="%s">%s</a>', $attachmentDate->format(get_option('date_format')), $attachmentDate->format('H'), $attachmentDate->format('i'), $attachmentDate->format('Y-m-d'), __('Original', 'enable-media-replace'));
210 ?>
211 </span>
212 </div>
213 <?php if ($subdir = $uiHelper->getRelPathNow()):
214
215 if ($settings['new_location'] !== false)
216 $subdir = $settings['new_location_dir'];
217 ?>
218
219 <!--
220 <div class='title_option'>
221 <input type="text" name="new_title" value="">
222 </div>
223 -->
224 <div class='location_option'>
225 <?php
226 if (true === $view->is_movable): ?>
227 <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>
228 <br>
229 <?php echo $view->custom_basedir ?> <input type="text" name="location_dir" value="<?php echo $subdir ?>" />
230 <?php
231 else:
232 echo __('File is offloaded and can\'t be moved to other directory', 'enable-media-replace');
233 endif;
234 ?>
235 </div>
236 <?php endif; ?>
237
238 </div>
239
240 </section>
241 </div>
242 <section class='form_controls wrapper'>
243 <input id="submit" type="submit" class="button button-primary" disabled="disabled" value="<?php echo esc_attr__("Upload", "enable-media-replace"); ?>" />
244 <a href="#" class="button" onclick="history.back();"><?php echo esc_html__("Cancel", "enable-media-replace"); ?></a>
245 </section>
246 </div>
247
248 <?php include_once('upsell.php'); ?>
249
250
251
252 </form>
253 </div>
1 <?php
2 namespace EnableMediaReplace;
3
4 if (! defined('ABSPATH')) {
5 exit; // Exit if accessed directly.
6 }
7
8 //use \EnableMediaReplace\UIHelper;
9 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
10
11
12 ?>
13
14 <div class='enable-media-replace emr-screen success-screen'>
15 <h3 class='title'><?php _e('Enable Media Replace', 'enable-media-replace'); ?></h3>
16 <div class='content'>
17 <h1><?php _e('Your image has been replaced!', 'enable-media-replace'); ?></h1>
18
19 <p><?php _e('Your image has been successfully replaced!', 'enable-media-replace'); ?></p>
20
21 <p><?php _e('Did you know that you can also optimize the images on your website to make them load faster?', 'enable-media-replace'); ?></p>
22
23 <p><?php printf(esc_html__('Try the %sShortPixel Image Optimizer%s plugin!', 'enable-media-replace'), '<a href="https://wordpress.org/plugins/shortpixel-image-optimiser/" target="_blank">', '</a>'); ?></p>
24
25 <p><?php _e('You will be redirect to the image screen in a few seconds.', 'enable-media-replace');
26 printf(esc_html__('( %s ) or %s click here to continue %s', 'enable-media-replace'), '<span id="redirect_counter"></span>',
27 '<a id="redirect_url" href="' . esc_url( $view->postUrl ) . '">', '</a>');
28 ?>
29
30 </p>
31
32 </div>
33
34 </div>
35
36 <?php
37 require_once ABSPATH . 'wp-admin/admin-footer.php';
1 <?php
2 namespace EnableMediaReplace;
3
4 //use \EnableMediaReplace\UIHelper;
5 use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
6 use EnableMediaReplace\Notices\NoticeController as Notices;
7
8 if (! apply_filters('emr/upsell', current_user_can('install_plugins')))
9 {
10 return;
11 }
12
13 #wp_nonce_field('enable-media-replace');
14 $plugins = get_plugins();
15
16 $spio_installed = isset($plugins['shortpixel-image-optimiser/wp-shortpixel.php']);
17 $spio_active = is_plugin_active('shortpixel-image-optimiser/wp-shortpixel.php');
18
19
20 $spai_installed = isset($plugins['shortpixel-adaptive-images/short-pixel-ai.php']);
21 $spai_active = is_plugin_active('shortpixel-adaptive-images/short-pixel-ai.php');
22
23 $envira_installed = isset($plugins['envira-gallery-lite/envira-gallery-lite.php']);
24 $envira_active = is_plugin_active('envira-gallery-lite/envira-gallery-lite.php');
25 $envira_pro_active = is_plugin_active('envira-gallery/envira-gallery.php');
26
27
28 ?>
29
30 <input type="hidden" id='upsell-nonce' value="<?php echo wp_create_nonce( 'emr-plugin-install' ); ?>" />
31 <input type="hidden" id='upsell-nonce-activate' value="<?php echo wp_create_nonce( 'emr-plugin-activate' ); ?>" />
32 <section class='upsell-wrapper'>
33
34 <!--- SHORTPIXEL -->
35 <?php if(! $spio_active): ?>
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"); ?>
43 </h4>
44 <h3 class="red ucase"><?php _e('Is your website slow?', 'enable-media-replace'); ?></h3>
45 <br>
46 <h3 class="cyan ucase"><?php printf(__('Optimize all images %s automatically', 'enable-media-replace'), '<br>'); ?></h3>
47 <p class='button-wrapper '>
48 <?php
49 $install_class = (! $spio_installed) ? '' : 'hidden';
50 $activate_class = ($spio_installed && ! $spio_active) ? '' : 'hidden';
51 ?>
52 <a class="emr-installer <?php echo $install_class ?>" data-action="install" data-plugin="spio" href="javascript:void(0)">
53 <?php _e('INSTALL NOW', 'enable-media-replace') ?>
54 </a>
55
56 <a class='emr-activate <?php echo $activate_class ?>' data-action="activate" data-plugin="spio" href="javascript:void(0)">
57 <?php _e('ACTIVATE', 'enable-media-replace') ?>
58 </a>
59
60 <h4 class='emr-activate-done hidden' data-plugin='spio'><?php _e('Shortpixel activated!', 'enable-media-replace'); ?></h4>
61 </p>
62
63 </div>
64 <?php endif; ?>
65 <!--- // SHORTPIXEL -->
66
67 <!--- SHORTPIXEL AI -->
68 <?php if(! $spai_active): ?>
69
70 <div class='shortpixel-offer spai'>
71 <div class='img-wrapper'>
72 <img width="40" height="40" src="<?php echo esc_url(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"); ?>
76 </h4>
77
78
79 <h3 class="cyan ucase"><?php printf(__('Start Serving %s Optimized, %s Nextgen images %s From a global CDN', 'enable-media-replace'), '<br>', '<br>', '<br>'); ?></h3>
80 <h3 class="red ucase"><?php _e('In Minutes', 'enable-media-replace'); ?></h3>
81 <p class='button-wrapper '>
82 <?php
83 $install_class = (! $spai_installed) ? '' : 'hidden';
84 $activate_class = ($spai_installed && ! $spai_active) ? '' : 'hidden';
85 ?>
86 <a class="emr-installer <?php echo $install_class ?>" data-action="install" data-plugin="spai" href="javascript:void(0)">
87 <?php _e('INSTALL NOW', 'enable-media-replace') ?>
88 </a>
89
90 <a class='emr-activate <?php echo $activate_class ?>' data-action="activate" data-plugin="spai" href="javascript:void(0)">
91 <?php _e('ACTIVATE', 'enable-media-replace') ?>
92 </a>
93
94 <h4 class='emr-activate-done hidden' data-plugin='spai'><?php _e('Shortpixel Adaptive Images activated!', 'enable-media-replace'); ?></h4>
95 </p>
96
97 </div>
98 <?php endif; ?>
99 <!--- // SHORTPIXEL AI -->
100
101 <!--- Shortpixel THEME -->
102 <div class='shortpixel-offer theme-offer'>
103 <p><a href="https://wordpress.org/themes/superb-pixels/" target="_blank"><img src="<?php echo esc_url(emr()->getPluginURL('img/sp-banner-theme.jpg')); ?>" alt='ShortPixel Theme' ></a></p>
104
105 </div>
106 <!--- // Shortpixel THEME -->
107
108 <!--- WPSO -->
109 <?php /*
110 <div class='shortpixel-offer site-speed'>
111 <p class='img-wrapper'><img width="40" height="40" src="<?php echo emr()->getPluginURL('img/sp-logo-wink.svg'); ?>" alt='ShortPixel'></p>
112 <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>
113
114 <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>
115 </div>
116 */ ?>
117 <!--- // WPSO -->
118
119
120
121 <!--- ENVIRA temprary deactivated
122 <?php if (! $envira_pro_active): ?>
123 <div class='envira-shortpixel-install shortpixel-offer'>
124
125 <p class='img-wrapper'><img src="<?php echo emr()->getPluginURL('img/envira-logo.png'); ?>" alt='Envira Gallery'></p>
126 <p><?php esc_html_e('Create beautiful, fast-loading photo & video galleries for your site in minutes.', 'enable-media-replace' ); ?></p>
127
128 <?php
129 $install_class = (! $envira_installed) ? '' : 'hidden';
130 $activate_class = ($envira_installed && ! $envira_active) ? '' : 'hidden';
131 ?>
132 <?php if (! $envira_active) { ?>
133 <p class='button-wrapper envira-emr-button-wrap'>
134
135 <a class="emr-installer button button-envira-emr emr-install-envira <?php echo $install_class ?>" data-action="install" data-plugin="envira" href='javascript:void(0)'><?php _e('Install now', 'enable-media-replace') ?></a>
136
137 <a class="emr-activate button button-envira-emr emr-activate-envira <?php echo $activate_class ?>" href='javascript:void(0)' data-action="activate" data-plugin="envira" ><?php _e('Activate', 'enable-media-replace') ?></a>
138
139 <h4 class='emr-activate-done hidden' data-plugin='envira'><?php _e('Envira Gallery activated!', 'enable-media-replace'); ?></h4>
140
141 </p>
142
143 <?php } else {
144 if ( is_plugin_active( 'envira-gallery-lite/envira-gallery-lite.php' ) ) {
145 ?>
146 <p class='button-wrapper envira-emr-button-wrap'><a class="button button-envira-emr" href='https://enviragallery.com/pricing' target="_blank"><?php _e('Get Pro', 'enable-media-replace') ?></a></p>
147
148 <?php } else { ?>
149
150 <?php }
151 } ?>
152 </div>
153 <style>
154 .envira-emr-button-wrap {
155 text-align: center;
156 }
157 .button-envira-emr {
158 background-color: #7cc048 !important;
159 border: none !important;
160 color: rgb(255,255,255) !important;
161 font-size: 21px !important;
162 }
163 .button-envira-emr:hover {
164 background-color: #95dc5e !important;
165 }
166 </style>
167 <?php endif; // envira ?>
168 -->
169
170 </section>