Enable Media Replace - Version 3.3.2

Version Description

Release date: 17th July 2019 * Check if medium size !> 400px, display that one, otherwise smallest. * Fixed: Links not updated when using Advanced Custom Fields * Fixed: Fails silently when file is too big for upload * When source file does not exist, show placeholder instead of failed image load * Fixed: Fatal error when replacing images * Fixed: Not the right time zone on replace * Fixed Beaver Builder incompatibility by not allowing replace with rename. * Fixed: Cannot replace non default Wordpress file types, even those allowed to upload [ Media Library Assistant compat ] * Fixed: error when trying to remove a file that doesn't exist - because the files are actually on another server

Download this release

Release Info

Developer ShortPixel
Plugin Icon 128x128 Enable Media Replace
Version 3.3.2
Comparing to
See all releases

Code changes from version 3.3.1 to 3.3.2

build/shortpixel/PackageLoader.php ADDED
@@ -0,0 +1,73 @@
1
+ <?php
2
+ namespace EnableMediaReplace\Build;
3
+
4
+ class PackageLoader
5
+ {
6
+ public $dir;
7
+
8
+ public function getComposerFile()
9
+ {
10
+ return json_decode(file_get_contents($this->dir."/composer.json"), 1);
11
+ }
12
+
13
+ public function load($dir)
14
+ {
15
+ $this->dir = $dir;
16
+ $composer = $this->getComposerFile();
17
+ if(isset($composer["autoload"]["psr-4"])){
18
+ $this->loadPSR4($composer['autoload']['psr-4']);
19
+ }
20
+ if(isset($composer["autoload"]["psr-0"])){
21
+ $this->loadPSR0($composer['autoload']['psr-0']);
22
+ }
23
+ if(isset($composer["autoload"]["files"])){
24
+ $this->loadFiles($composer["autoload"]["files"]);
25
+ }
26
+ }
27
+
28
+ public function loadFiles($files){
29
+ foreach($files as $file){
30
+ $fullpath = $this->dir."/".$file;
31
+ if(file_exists($fullpath)){
32
+ include_once($fullpath);
33
+ }
34
+ }
35
+ }
36
+
37
+ public function loadPSR4($namespaces)
38
+ {
39
+ $this->loadPSR($namespaces, true);
40
+ }
41
+
42
+ public function loadPSR0($namespaces)
43
+ {
44
+ $this->loadPSR($namespaces, false);
45
+ }
46
+
47
+ public function loadPSR($namespaces, $psr4)
48
+ {
49
+ $dir = $this->dir;
50
+ // Foreach namespace specified in the composer, load the given classes
51
+ foreach ($namespaces as $namespace => $classpaths) {
52
+ if (!is_array($classpaths)) {
53
+ $classpaths = array($classpaths);
54
+ }
55
+ spl_autoload_register(function ($classname) use ($namespace, $classpaths, $dir, $psr4) {
56
+ // Check if the namespace matches the class we are looking for
57
+ if (preg_match("#^".preg_quote($namespace)."#", $classname)) {
58
+ // Remove the namespace from the file path since it's psr4
59
+ if ($psr4) {
60
+ $classname = str_replace($namespace, "", $classname);
61
+ }
62
+ $filename = preg_replace("#\\\\#", "/", $classname).".php";
63
+ foreach ($classpaths as $classpath) {
64
+ $fullpath = $this->dir."/".$classpath."/$filename";
65
+ if (file_exists($fullpath)) {
66
+ include_once $fullpath;
67
+ }
68
+ }
69
+ }
70
+ });
71
+ }
72
+ }
73
+ }
build/shortpixel/autoload.php ADDED
@@ -0,0 +1,5 @@
1
+ <?php
2
+ require_once "PackageLoader.php";
3
+ $loader = new EnableMediaReplace\Build\PackageLoader();
4
+ $loader->load(__DIR__);
5
+
build/shortpixel/composer.json ADDED
@@ -0,0 +1 @@
1
+ {"name":"EnableMediaReplace\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"EnableMediaReplace\\ShortPixelLogger\\":"log\/src\/","EnableMediaReplace\\Notices\\":"notices\/src\/"}}}
build/shortpixel/log/composer.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "shortpixel/log",
3
+ "description": "ShortPixel Logging",
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\\ShortPixelLogger\\" : "src/" }
17
+ }
18
+ }
build/shortpixel/log/src/DebugItem.php ADDED
@@ -0,0 +1,136 @@
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
45
+ {
46
+ foreach($data as $index => $item)
47
+ {
48
+ if (is_object($item) || is_array($item))
49
+ {
50
+ $this->data[] = print_r($item, true);
51
+ }
52
+ }
53
+ }
54
+ } // if
55
+ elseif (! is_array($data)) // this leaves out empty default arrays
56
+ {
57
+ $this->data[] = print_r($data, true);
58
+ }
59
+ }
60
+
61
+ public function getData()
62
+ {
63
+ return array('time' => $this->time, 'level' => $this->level, 'message' => $this->message, 'data' => $this->data, 'caller' => $this->caller);
64
+ }
65
+
66
+ /** Test Data Array for possible values
67
+ *
68
+ * Data can be a collection of several debug vars, a single var, or just an normal array. Test if array has single types,
69
+ * which is a sign the array is not a collection.
70
+ */
71
+ protected function getDataType($data)
72
+ {
73
+ $single_type = array('integer', 'boolean', 'string');
74
+ if (in_array(gettype(reset($data)), $single_type))
75
+ {
76
+ return 1;
77
+ }
78
+ else
79
+ {
80
+ return 2;
81
+ }
82
+ }
83
+
84
+ public function getForFormat()
85
+ {
86
+ $data = $this->getData();
87
+ switch($this->level)
88
+ {
89
+ case self::LEVEL_ERROR:
90
+ $level = 'ERR';
91
+ $color = "\033[31m";
92
+ break;
93
+ case self::LEVEL_WARN:
94
+ $level = 'WRN';
95
+ $color = "\033[33m";
96
+ break;
97
+ case self::LEVEL_INFO:
98
+ $level = 'INF';
99
+ $color = "\033[37m";
100
+ break;
101
+ case self::LEVEL_DEBUG:
102
+ $level = 'DBG';
103
+ $color = "\033[37m";
104
+ break;
105
+
106
+ }
107
+ $color_end = "\033[0m";
108
+
109
+ $data['color'] = $color;
110
+ $data['color_end'] = $color_end;
111
+ $data['level'] = $level;
112
+
113
+ return $data;
114
+
115
+ //return array('time' => $this->time, 'level' => $level, 'message' => $this->message, 'data' => $this->data, 'color' => $color, 'color_end' => $color_end, 'caller' => $this->caller);
116
+
117
+ }
118
+
119
+ protected function setCaller()
120
+ {
121
+ $debug=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,5);
122
+ $i = 4;
123
+ if (isset($debug[$i]))
124
+ {
125
+ $info = $debug[$i];
126
+ $line = isset($info['line']) ? $info['line'] : 'Line unknown';
127
+ $file = isset($info['file']) ? basename($info['file']) : 'File not set';
128
+
129
+ $this->caller = array('line' => $line, 'file' => $file, 'function' => $info['function']);
130
+ }
131
+
132
+
133
+ }
134
+
135
+
136
+ }
build/shortpixel/log/src/ShortPixelLogger.php ADDED
@@ -0,0 +1,322 @@
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
+ /* protected $hooks = array(
28
+ 'shortpixel_image_exists' => array('numargs' => 3),
29
+ 'shortpixel_webp_image_base' => array('numargs' => 2),
30
+ 'shortpixel_image_urls' => array('numargs' => 2),
31
+ ); // @todo monitor hooks, but this should be more dynamic. Do when moving to module via config.
32
+ */
33
+
34
+ // utility
35
+ private $namespace;
36
+ private $view;
37
+
38
+ protected $template = 'view-debug-box';
39
+
40
+ /** Debugger constructor
41
+ * 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
42
+ * 2) Put SHORTPIXEL_DEBUG in the request. Either true or number.
43
+ */
44
+ public function __construct()
45
+ {
46
+ $this->start_time = microtime(true);
47
+ $this->logLevel = DebugItem::LEVEL_WARN;
48
+
49
+ $ns = __NAMESPACE__;
50
+ $this->namespace = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
51
+
52
+ if ($this->logPath === false)
53
+ {
54
+ $upload_dir = wp_upload_dir(null,false,false);
55
+ $this->logPath = $upload_dir['basedir'] . '/' . $this->namespace . ".log";
56
+ }
57
+
58
+ if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) // manual takes precedence over constants
59
+ {
60
+ $this->is_manual_request = true;
61
+ $this->is_active = true;
62
+
63
+ if ($_REQUEST['SHORTPIXEL_DEBUG'] === 'true')
64
+ {
65
+ $this->logLevel = DebugItem::LEVEL_INFO;
66
+ }
67
+ else {
68
+ $this->logLevel = intval($_REQUEST['SHORTPIXEL_DEBUG']);
69
+ }
70
+
71
+ }
72
+ else if ( (defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG > 0) )
73
+ {
74
+ $this->is_active = true;
75
+ if (SHORTPIXEL_DEBUG === true)
76
+ $this->logLevel = DebugItem::LEVEL_INFO;
77
+ else {
78
+ $this->logLevel = intval(SHORTPIXEL_DEBUG);
79
+ }
80
+ }
81
+
82
+ if (defined('SHORTPIXEL_DEBUG_TARGET') && SHORTPIXEL_DEBUG_TARGET || $this->is_manual_request)
83
+ {
84
+ //$this->logPath = SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log";
85
+ //$this->logMode = defined('SHORTPIXEL_LOG_OVERWRITE') ? 0 : FILE_APPEND;
86
+ if (defined('SHORTPIXEL_LOG_OVERWRITE')) // if overwrite, do this on init once.
87
+ file_put_contents($this->logPath,'-- Log Reset -- ' .PHP_EOL);
88
+
89
+ }
90
+
91
+ /* On Early init, this function might not exist, then queue it when needed */
92
+ if (! function_exists('wp_get_current_user'))
93
+ add_action('plugins_loaded', array($this, 'initView'));
94
+ else
95
+ $this->initView();
96
+
97
+
98
+ if ($this->is_active && count($this->hooks) > 0)
99
+ $this->monitorHooks();
100
+ }
101
+
102
+ /** Init the view when needed. Private function ( public because of WP_HOOK )
103
+ * Never call directly */
104
+ public function initView()
105
+ {
106
+ $user_is_administrator = (current_user_can('manage_options')) ? true : false;
107
+
108
+ if ($this->is_active && $this->is_manual_request && $user_is_administrator )
109
+ {
110
+ $content_url = content_url();
111
+ $logPath = $this->logPath;
112
+ $pathpos = strpos($logPath, 'wp-content') + strlen('wp-content');
113
+ $logPart = substr($logPath, $pathpos);
114
+ $logLink = $content_url . $logPart;
115
+
116
+ $this->view = new \stdClass;
117
+ $this->view->logLink = $logLink;
118
+ add_action('admin_footer', array($this, 'loadView'));
119
+ }
120
+ }
121
+
122
+ public static function getInstance()
123
+ {
124
+ if ( self::$instance === null)
125
+ {
126
+ self::$instance = new ShortPixelLogger();
127
+ }
128
+ return self::$instance;
129
+ }
130
+
131
+ public function setLogPath($logPath)
132
+ {
133
+ $this->logPath = $logPath;
134
+ }
135
+ protected static function addLog($message, $level, $data = array())
136
+ {
137
+ $log = self::getInstance();
138
+
139
+ // don't log anything too low.
140
+ if ($log->logLevel < $level)
141
+ {
142
+ return;
143
+ }
144
+
145
+ $arg = array();
146
+ $args['level'] = $level;
147
+ $args['data'] = $data;
148
+
149
+ $newItem = new DebugItem($message, $args);
150
+ $log->items[] = $newItem;
151
+
152
+ if ($log->is_active)
153
+ {
154
+ $log->write($newItem);
155
+ }
156
+ }
157
+
158
+ /** Writes to log File. */
159
+ protected function write($debugItem, $mode = 'file')
160
+ {
161
+ $items = $debugItem->getForFormat();
162
+ $items['time_passed'] = round ( ($items['time'] - $this->start_time), 5);
163
+ $items['time'] = date('Y-m-d H:i:s', $items['time'] );
164
+
165
+ if ( ($items['caller']) && is_array($items['caller']) && count($items['caller']) > 0)
166
+ {
167
+ $caller = $items['caller'];
168
+ $items['caller'] = $caller['file'] . ' in ' . $caller['function'] . '(' . $caller['line'] . ')';
169
+ }
170
+
171
+ $line = $this->formatLine($items);
172
+
173
+ if ($this->logPath)
174
+ {
175
+ file_put_contents($this->logPath,$line, FILE_APPEND);
176
+ }
177
+ else {
178
+ error_log($line);
179
+ }
180
+ }
181
+
182
+ protected function formatLine($args = array() )
183
+ {
184
+ $line= $this->format;
185
+ foreach($args as $key => $value)
186
+ {
187
+ if (! is_array($value) && ! is_object($value))
188
+ $line = str_replace('%%' . $key . '%%', $value, $line);
189
+ }
190
+
191
+ $line .= PHP_EOL;
192
+
193
+ if (isset($args['data']))
194
+ {
195
+ $data = array_filter($args['data']);
196
+ if (count($data) > 0)
197
+ {
198
+ foreach($data as $item)
199
+ {
200
+ $line .= $item . PHP_EOL;
201
+ }
202
+ }
203
+ }
204
+
205
+ return $line;
206
+ }
207
+
208
+ protected function setLogLevel($level)
209
+ {
210
+ $this->logLevel = $level;
211
+ }
212
+
213
+ protected function getEnv($name)
214
+ {
215
+ if (isset($this->{$name}))
216
+ {
217
+ return $this->{$name};
218
+ }
219
+ else {
220
+ return false;
221
+ }
222
+ }
223
+
224
+ public static function addError($message, $args = array())
225
+ {
226
+ $level = DebugItem::LEVEL_ERROR;
227
+ static::addLog($message, $level, $args);
228
+ }
229
+ public static function addWarn($message, $args = array())
230
+ {
231
+ $level = DebugItem::LEVEL_WARN;
232
+ static::addLog($message, $level, $args);
233
+ }
234
+ public static function addInfo($message, $args = array())
235
+ {
236
+ $level = DebugItem::LEVEL_INFO;
237
+ static::addLog($message, $level, $args);
238
+ }
239
+ public static function addDebug($message, $args = array())
240
+ {
241
+ $level = DebugItem::LEVEL_DEBUG;
242
+ static::addLog($message, $level, $args);
243
+ }
244
+
245
+ public static function logLevel($level)
246
+ {
247
+ $log = self::getInstance();
248
+ static::addInfo('Changing Log level' . $level);
249
+ $log->setLogLevel($level);
250
+ }
251
+
252
+ public static function getLogLevel()
253
+ {
254
+ $log = self::getInstance();
255
+ return $log->getEnv('logLevel');
256
+ }
257
+
258
+ public static function isManualDebug()
259
+ {
260
+ $log = self::getInstance();
261
+ return $log->getEnv('is_manual_request');
262
+ }
263
+
264
+ public static function getLogPath()
265
+ {
266
+ $log = self::getInstance();
267
+ return $log->getEnv('logPath');
268
+ }
269
+
270
+ /** Function to test if the debugger is active
271
+ * @return boolean true when active.
272
+ */
273
+ public static function debugIsActive()
274
+ {
275
+ $log = self::getInstance();
276
+ return $log->getEnv('is_active');
277
+ }
278
+
279
+ protected function monitorHooks()
280
+ {
281
+
282
+ foreach($this->hooks as $hook => $data)
283
+ {
284
+ $numargs = isset($data['numargs']) ? $data['numargs'] : 1;
285
+ $prio = isset($data['priority']) ? $data['priority'] : 10;
286
+
287
+ add_filter($hook, function($value) use ($hook) {
288
+ $args = func_get_args();
289
+ return $this->logHook($hook, $value, $args); }, $prio, $numargs);
290
+ }
291
+ }
292
+
293
+ public function logHook($hook, $value, $args)
294
+ {
295
+ array_shift($args);
296
+ self::addInfo('[Hook] - ' . $hook . ' with ' . var_export($value,true), $args);
297
+ return $value;
298
+ }
299
+
300
+ public function loadView()
301
+ {
302
+ // load either param or class template.
303
+ $template = $this->template;
304
+
305
+ $view = $this->view;
306
+ $view->namespace = $this->namespace;
307
+ $controller = $this;
308
+
309
+ $template_path = __DIR__ . '/' . $this->template . '.php';
310
+ if (file_exists($template_path))
311
+ {
312
+
313
+ include($template_path);
314
+ }
315
+ else {
316
+ self::addError("View $template could not be found in " . $template_path,
317
+ array('class' => get_class($this), 'req' => $_REQUEST));
318
+ }
319
+ }
320
+
321
+
322
+ } // class debugController
build/shortpixel/log/src/view-debug-box.php ADDED
@@ -0,0 +1,57 @@
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 $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>
build/shortpixel/notices/composer.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "shortpixel/notices",
3
+ "description": "ShortPixel WordPress Notice System",
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
+ "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
+ }
build/shortpixel/notices/src/NoticeController.php ADDED
@@ -0,0 +1,194 @@
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
+ public $notice_count = 0;
12
+
13
+ protected $has_stored = false;
14
+
15
+ protected $notice_option = ''; // The wp_options name for notices here.
16
+
17
+ /** For backward compat. Never call constructor directly. */
18
+ public function __construct()
19
+ {
20
+ // $this->loadModel('notice');
21
+ $ns = __NAMESPACE__;
22
+ $ns = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
23
+ $this->notice_option = $ns . '-notices';
24
+
25
+ $this->loadNotices();
26
+ //$this->loadConfig();
27
+ }
28
+
29
+ /** Load Notices Config File, if any
30
+ *
31
+ * [ Future Use ]
32
+ */
33
+ public function loadConfig()
34
+ {
35
+ if (file_exists('../notice_config.json'))
36
+ {
37
+ $config = file_get_contents('../notice_config.json');
38
+ $json_config = json_decode($config);
39
+ }
40
+ }
41
+
42
+ public function loadIcons($icons)
43
+ {
44
+ foreach($icons as $name => $icon)
45
+ NoticeModel::setIcon($name, $icon);
46
+ }
47
+
48
+
49
+ protected function loadNotices()
50
+ {
51
+ $notices = get_option($this->notice_option, false);
52
+ $cnotice = (is_array($notices)) ? count($notices) : 0;
53
+ Log::addDebug('Notice Control - #num notices' . $cnotice);
54
+ if ($notices !== false)
55
+ {
56
+ self::$notices = $notices;
57
+ $this->has_stored = true;
58
+ }
59
+ else {
60
+ self::$notices = array();
61
+ $this->has_stored = false;
62
+ }
63
+ $this->countNotices();
64
+ }
65
+
66
+
67
+ public function addNotice($message, $code)
68
+ {
69
+ $notice = new NoticeModel($message, $code);
70
+ self::$notices[] = $notice;
71
+ $this->countNotices();
72
+ Log::addDebug('Adding notice - ', $notice);
73
+ $this->update();
74
+ return $notice;
75
+ }
76
+
77
+ /** Update the notices to store, check what to remove, returns count. */
78
+ public function update()
79
+ {
80
+ if (! is_array(self::$notices) || count(self::$notices) == 0)
81
+ {
82
+ if ($this->has_stored)
83
+ delete_option($this->notice_option);
84
+
85
+ return 0;
86
+ }
87
+
88
+ $new_notices = array();
89
+ foreach(self::$notices as $item)
90
+ {
91
+ if (! $item->isDone() )
92
+ {
93
+ $new_notices[] = $item;
94
+ }
95
+ }
96
+
97
+ update_option($this->notice_option, $new_notices);
98
+ self::$notices = $new_notices;
99
+
100
+ return $this->countNotices();
101
+ }
102
+
103
+ public function countNotices()
104
+ {
105
+ $this->notice_count = count(self::$notices);
106
+ return $this->notice_count;
107
+ }
108
+
109
+
110
+ public function getNotices()
111
+ {
112
+ return self::$notices;
113
+ }
114
+
115
+ public static function getInstance()
116
+ {
117
+ if ( self::$instance === null)
118
+ {
119
+ self::$instance = new NoticeController();
120
+ }
121
+
122
+ return self::$instance;
123
+ }
124
+
125
+ /** Adds a notice, quick and fast method
126
+ * @param String $message The Message you want to notify
127
+ * @param int $code A value of messageType as defined in model
128
+ * @returm Object Instance of noticeModel
129
+ */
130
+
131
+ public static function addNormal($message)
132
+ {
133
+ $noticeController = self::getInstance();
134
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_NORMAL);
135
+ return $notice;
136
+
137
+ }
138
+
139
+ public static function addError($message)
140
+ {
141
+ $noticeController = self::getInstance();
142
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_ERROR);
143
+ return $notice;
144
+
145
+ }
146
+
147
+ public static function addWarning($message)
148
+ {
149
+ $noticeController = self::getInstance();
150
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_WARNING);
151
+ return $notice;
152
+
153
+ }
154
+
155
+ public static function addSuccess($message)
156
+ {
157
+ $noticeController = self::getInstance();
158
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_SUCCESS);
159
+ return $notice;
160
+
161
+ }
162
+
163
+ public function admin_notices()
164
+ {
165
+ if ($this->countNotices() > 0)
166
+ {
167
+ if (! self::$cssHookLoaded)
168
+ {
169
+ add_action('admin_print_footer_scripts', array($this, 'printNoticeStyle'));
170
+ self::$cssHookLoaded = true;
171
+ }
172
+ foreach($this->getNotices() as $notice)
173
+ {
174
+ echo $notice->getForDisplay();
175
+ }
176
+ }
177
+ $this->update(); // puts views, and updates
178
+ }
179
+
180
+ public function printNoticeStyle()
181
+ {
182
+ if (file_exists(__DIR__ . '/css/notices.css'))
183
+ {
184
+ echo '<style>' . file_get_contents(__DIR__ . '/css/notices.css') . '</style>';
185
+ }
186
+ else {
187
+ Log::addDebug('Notices : css/notices.css could not be loaded');
188
+ }
189
+ }
190
+
191
+
192
+
193
+
194
+ }
build/shortpixel/notices/src/NoticeModel.php ADDED
@@ -0,0 +1,116 @@
1
+ <?php
2
+ namespace EnableMediaReplace\Notices;
3
+
4
+ class NoticeModel //extends ShortPixelModel
5
+ {
6
+ protected $message;
7
+ public $code;
8
+
9
+ protected $viewed = false;
10
+ public $is_persistent = false; // This is a fatal issue, display until something was fixed.
11
+ public $is_removable = true; // if removable, display a notice dialog with red X or so.
12
+ public $messageType = self::NOTICE_NORMAL;
13
+
14
+ public static $icons = array();
15
+
16
+ const NOTICE_NORMAL = 1;
17
+ const NOTICE_ERROR = 2;
18
+ const NOTICE_SUCCESS = 3;
19
+ const NOTICE_WARNING = 4;
20
+
21
+
22
+ public function __construct($message, $messageType = self::NOTICE_NORMAL)
23
+ {
24
+ $this->message = $message;
25
+ $this->messageType = $messageType;
26
+
27
+ }
28
+
29
+ public function isDone()
30
+ {
31
+ if ($this->viewed && ! $this->is_persistent)
32
+ return true;
33
+ else
34
+ return false;
35
+
36
+ }
37
+
38
+ public static function setIcon($notice_type, $icon)
39
+ {
40
+ switch($notice_type)
41
+ {
42
+ case 'error':
43
+ $type = self::NOTICE_ERROR;
44
+ break;
45
+ case 'success':
46
+ $type = self::NOTICE_SUCCESS;
47
+ break;
48
+ case 'warning':
49
+ $type = self::NOTICE_WARNING;
50
+ break;
51
+ case 'normal':
52
+ default:
53
+ $type = self::NOTICE_NORMAL;
54
+ break;
55
+ }
56
+ self::$icons[$type] = $icon;
57
+ }
58
+
59
+ public function getForDisplay()
60
+ {
61
+ $this->viewed = true;
62
+ $class = 'shortpixel notice ';
63
+
64
+ $icon = '';
65
+
66
+ switch($this->messageType)
67
+ {
68
+ case self::NOTICE_ERROR:
69
+ $class .= 'notice-error ';
70
+ $icon = isset(self::$icons[self::NOTICE_ERROR]) ? self::$icons[self::NOTICE_ERROR] : '';
71
+ //$icon = 'scared';
72
+ break;
73
+ case self::NOTICE_SUCCESS:
74
+ $class .= 'notice-success ';
75
+ $icon = isset(self::$icons[self::NOTICE_SUCCESS]) ? self::$icons[self::NOTICE_SUCCESS] : '';
76
+ break;
77
+ case self::NOTICE_WARNING:
78
+ $class .= 'notice-warning ';
79
+ $icon = isset(self::$icons[self::NOTICE_WARNING]) ? self::$icons[self::NOTICE_WARNING] : '';
80
+ break;
81
+ case self::NOTICE_NORMAL:
82
+ $class .= 'notice-info ';
83
+ $icon = isset(self::$icons[self::NOTICE_NORMAL]) ? self::$icons[self::NOTICE_NORMAL] : '';
84
+ break;
85
+ default:
86
+ $class .= 'notice-info ';
87
+ $icon = '';
88
+ break;
89
+ }
90
+
91
+ /*$image = '<img src="' . plugins_url('/shortpixel-image-optimiser/res/img/robo-' . $icon . '.png') . '"
92
+ srcset="' . plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '.png' ) . ' 1x, ' . plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '@2x.png') . ' 2x" class="short-pixel-notice-icon">';
93
+ */
94
+
95
+ if ($this->is_removable)
96
+ {
97
+ $class .= 'is-dismissible ';
98
+ }
99
+
100
+ if ($this->is_persistent)
101
+ {
102
+ $class .= '';
103
+ }
104
+
105
+ return "<div class='$class'>" . $icon . "<p>" . $this->message . "</p></div>";
106
+
107
+ }
108
+
109
+
110
+
111
+ // @todo Transient save, since that is used in some parts.
112
+ // save
113
+ // load
114
+
115
+
116
+ }
build/shortpixel/notices/src/css/notices.css ADDED
@@ -0,0 +1,21 @@
1
+ .shortpixel.notice {
2
+ padding: 8px; }
3
+ .shortpixel.notice img {
4
+ display: inline-block;
5
+ margin: 0 25px 0 0;
6
+ max-height: 50px; }
7
+ .shortpixel.notice .notice-dismiss {
8
+ margin-top: 10px; }
9
+
10
+ /* In-view notice ( not on top, between the options ) - styled after WP notice */
11
+ .view-notice {
12
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
13
+ border: 4px solid #fff;
14
+ padding: 1px 12px; }
15
+ .view-notice p {
16
+ margin: 1em 0 !important; }
17
+ .view-notice.warning {
18
+ border-left-color: #ffb900; }
19
+
20
+ .view-notice-row {
21
+ display: none; }
build/shortpixel/notices/src/css/notices.scss ADDED
@@ -0,0 +1,39 @@
1
+
2
+ .shortpixel.notice
3
+ {
4
+ //padding: 18px;
5
+ //min-height: 50px;
6
+ padding: 8px;
7
+ img
8
+ {
9
+ display:inline-block;
10
+ margin: 0 25px 0 0;
11
+ max-height: 50px;
12
+ }
13
+ .notice-dismiss
14
+ {
15
+ margin-top: 10px;
16
+ }
17
+ }
18
+
19
+ /* In-view notice ( not on top, between the options ) - styled after WP notice */
20
+ .view-notice
21
+ {
22
+
23
+ box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
24
+ border: 4px solid #fff;
25
+
26
+ padding: 1px 12px;
27
+ p {
28
+ margin: 1em 0 !important;
29
+ }
30
+ &.warning
31
+ {
32
+ border-left-color: #ffb900;
33
+ }
34
+ }
35
+
36
+ .view-notice-row
37
+ {
38
+ display: none;
39
+ }
classes/emr-plugin.php CHANGED
@@ -1,19 +1,29 @@
1
<?php
2
namespace EnableMediaReplace;
3
4
// Does what a plugin does.
5
class EnableMediaReplacePlugin
6
{
7
8
protected $plugin_path;
9
10
public function __construct()
11
{
12
$this->plugin_actions(); // init
13
14
15
}
16
17
public function plugin_actions()
18
{
19
$this->plugin_path = plugin_dir_path(EMR_ROOT_FILE);
@@ -51,11 +61,21 @@ class EnableMediaReplacePlugin
51
52
/**
53
* Initialize this plugin. Called by 'admin_init' hook.
54
- * Only languages files needs loading during init.
55
*/
56
public function init()
57
{
58
load_plugin_textdomain( 'enable-media-replace', false, basename(dirname(EMR_ROOT_FILE) ) . '/languages' );
59
}
60
61
/** Load EMR views based on request */
@@ -109,7 +129,16 @@ class EnableMediaReplacePlugin
109
}
110
111
wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), false, true );
112
- wp_localize_script('emr_admin', 'emr_options', array('dateFormat' => $this->convertdate(get_option( 'date_format' ))));
113
114
}
115
@@ -170,7 +199,7 @@ class EnableMediaReplacePlugin
170
$form_fields["enable-media-replace"] = array(
171
"label" => esc_html__("Replace media", "enable-media-replace"),
172
"input" => "html",
173
- "html" => "<p><a class='button-secondary'$link>" . esc_html__("Upload a new file", "enable-media-replace") . "</a></p>", "helps" => esc_html__("To replace the current file, click the link and upload a replacement.", "enable-media-replace")
174
);
175
176
return $form_fields;
@@ -180,7 +209,6 @@ class EnableMediaReplacePlugin
180
* @param array $mime_types
181
* @return array
182
*/
183
-
184
public function add_mime_types($mime_types)
185
{
186
$mime_types['dat'] = 'text/plain'; // Adding .dat extension
@@ -202,7 +230,7 @@ class EnableMediaReplacePlugin
202
} */
203
$link = "href=\"$editurl\"";
204
205
- $newaction['adddata'] = '<a ' . $link . ' aria-label="' . esc_html__("Replace media", "enable-media-replace") . '" rel="permalink">' . esc_html__("Replace media", "enable-media-replace") . '</a>';
206
return array_merge($actions,$newaction);
207
}
208
1
<?php
2
namespace EnableMediaReplace;
3
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use EnableMediaReplace\Notices\NoticeController as Notices;
5
6
// Does what a plugin does.
7
class EnableMediaReplacePlugin
8
{
9
10
protected $plugin_path;
11
+ private static $instance;
12
13
public function __construct()
14
{
15
$this->plugin_actions(); // init
16
+ }
17
18
+ public static function get()
19
+ {
20
+ if (is_null(self::$instance))
21
+ self::$instance = new EnableMediaReplacePlugin();
22
23
+ return self::$instance;
24
}
25
26
+
27
public function plugin_actions()
28
{
29
$this->plugin_path = plugin_dir_path(EMR_ROOT_FILE);
61
62
/**
63
* Initialize this plugin. Called by 'admin_init' hook.
64
+ *
65
*/
66
public function init()
67
{
68
load_plugin_textdomain( 'enable-media-replace', false, basename(dirname(EMR_ROOT_FILE) ) . '/languages' );
69
+
70
+ // Load Submodules
71
+ Log::addDebug('Plugin Init');
72
+ $notices = Notices::getInstance();
73
+
74
+ // Enqueue notices
75
+ add_action('admin_notices', array($notices, 'admin_notices')); // previous page / init time
76
+ add_action('admin_footer', array($notices, 'admin_notices')); // fresh notices between init - end
77
+
78
+ new Externals();
79
}
80
81
/** Load EMR views based on request */
129
}
130
131
wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), false, true );
132
+ $emr_options = array(
133
+ 'dateFormat' => $this->convertdate(get_option( 'date_format' )),
134
+ 'maxfilesize' => wp_max_upload_size(),
135
+
136
+ );
137
+
138
+ if (Log::debugIsActive())
139
+ $emr_options['is_debug'] = true;
140
+
141
+ wp_localize_script('emr_admin', 'emr_options', $emr_options);
142
143
}
144
199
$form_fields["enable-media-replace"] = array(
200
"label" => esc_html__("Replace media", "enable-media-replace"),
201
"input" => "html",
202
+ "html" => "<p><a class='button-secondary' $link>" . esc_html__("Upload a new file", "enable-media-replace") . "</a></p>", "helps" => esc_html__("To replace the current file, click the link and upload a replacement.", "enable-media-replace")
203
);
204
205
return $form_fields;
209
* @param array $mime_types
210
* @return array
211
*/
212
public function add_mime_types($mime_types)
213
{
214
$mime_types['dat'] = 'text/plain'; // Adding .dat extension
230
} */
231
$link = "href=\"$editurl\"";
232
233
+ $newaction['adddata'] = '<a ' . $link . ' aria-label="' . esc_attr__("Replace media", "enable-media-replace") . '" rel="permalink">' . esc_html__("Replace media", "enable-media-replace") . '</a>';
234
return array_merge($actions,$newaction);
235
}
236
classes/externals.php ADDED
@@ -0,0 +1,64 @@
1
+ <?php
2
+ namespace EnableMediaReplace;
3
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use EnableMediaReplace\Notices\NoticeController as Notices;
5
+
6
+
7
+ class Externals
8
+ {
9
+ protected $replaceType = null;
10
+ protected $replaceSearchType = null;
11
+
12
+ protected $messages = array();
13
+
14
+
15
+ public function __construct()
16
+ {
17
+ add_filter('emr_display_replace_type_options', array($this, 'get_replace_type'));
18
+ add_filter('emr_enable_replace_and_search', array($this, 'get_replacesearch_type'));
19
+
20
+ add_action('emr_after_replace_type_options', array($this, 'get_messages'));
21
+
22
+
23
+ $this->check();
24
+ }
25
+
26
+ protected function check()
27
+ {
28
+ if (class_exists('FLBuilder'))
29
+ {
30
+ $this->replaceSearchType = false;
31
+ $this->messages[] = __('Replace and Search feature is not compatible with Beaver Builder.', 'enable-media-replace');
32
+ }
33
+ }
34
+
35
+ public function get_replace_type($bool)
36
+ {
37
+ if ($this->replaceType === null)
38
+ return $bool;
39
+
40
+ return $this->replaceType;
41
+ }
42
+
43
+ public function get_replacesearch_type($bool)
44
+ {
45
+ if ($this->replaceSearchType === null)
46
+ return $bool;
47
+
48
+ return $this->replaceSearchType;
49
+ }
50
+
51
+ public function get_messages()
52
+ {
53
+ foreach($this->messages as $message)
54
+ {
55
+ echo '<span class="nofeature-notice"><p>'. $message . '</p></span>';
56
+ }
57
+
58
+ }
59
+
60
+
61
+
62
+
63
+
64
+ }
classes/file.php CHANGED
@@ -1,6 +1,9 @@
1
<?php
2
namespace EnableMediaReplace;
3
4
class emrFile
5
{
6
@@ -36,7 +39,15 @@ class emrFile
36
// This will *not* be checked, is not meant for permission of validation!
37
$this->fileMime = (isset($filedata['type'])) ? $filedata['type'] : false;
38
39
- // echo "<PRE>"; var_dump($this); echo "</PRE><BR>";
40
}
41
42
public function getFullFilePath()
@@ -64,11 +75,21 @@ class emrFile
64
return $this->fileName;
65
}
66
67
public function getFileMime()
68
{
69
return $this->fileMime;
70
}
71
72
73
}
74
1
<?php
2
namespace EnableMediaReplace;
3
4
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
+ use EnableMediaReplace\Notices\NoticeController as Notices;
6
+
7
class emrFile
8
{
9
39
// This will *not* be checked, is not meant for permission of validation!
40
$this->fileMime = (isset($filedata['type'])) ? $filedata['type'] : false;
41
42
+ }
43
+
44
+ public function checkAndCreateFolder()
45
+ {
46
+ $path = $this->getFilePath();
47
+ if (! is_dir($path) && ! file_exists($path))
48
+ {
49
+ return wp_mkdir_p($path);
50
+ }
51
}
52
53
public function getFullFilePath()
75
return $this->fileName;
76
}
77
78
+ public function getFileExtension()
79
+ {
80
+ return $this->extension;
81
+ }
82
+
83
public function getFileMime()
84
{
85
return $this->fileMime;
86
}
87
88
+ public function exists()
89
+ {
90
+ return $this->exists;
91
+ }
92
+
93
94
}
95
classes/replacer.php CHANGED
@@ -1,6 +1,8 @@
1
<?php
2
namespace EnableMediaReplace;
3
use \EnableMediaReplace\emrFile as File;
4
5
class Replacer
6
{
@@ -39,6 +41,7 @@ class Replacer
39
$source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
40
41
$this->sourceFile = new File($source_file);
42
$this->source_post = get_post($post_id);
43
$this->source_is_image = wp_attachment_is('image', $this->source_post);
44
$this->source_metadata = wp_get_attachment_metadata( $post_id );
@@ -65,6 +68,7 @@ class Replacer
65
/** Replace the sourceFile with a target
66
* @param $file String Full Path to the Replacement File. This will usually be an uploaded file in /tmp/
67
* @param $fileName String The fileName of the uploaded file. This will be used if sourcefile is not to be overwritten.
68
*/
69
public function replaceWith($file, $fileName)
70
{
@@ -78,18 +82,25 @@ class Replacer
78
79
if (is_null($targetFile))
80
{
81
- _e('Target File could not be set. The source file might not be there. In case of search and replace, a filter might prevent this', "enable-media-replace");
82
- exit;
83
}
84
85
/* @todo See if wp_handle_sideload / wp_handle_upload can be more securely used for this */
86
$result_moved = move_uploaded_file($file,$targetFile);
87
88
if (false === $result_moved)
89
{
90
- printf( esc_html__('The uploaded file could not be moved to %1$s , most likely because it could not remove the old images (file permissions) or the upload failed.', "enable-media-replace"), $targetFile );
91
- exit;
92
}
93
$this->targetFile = new File($targetFile);
94
95
if ($this->sourceFile->getPermissions() > 0)
@@ -114,6 +125,7 @@ class Replacer
114
{
115
update_attached_file($this->post_id, $filtered['file'] );
116
$this->targetFile = new File($filtered['file']); // handle as a new file
117
}
118
119
$metadata = wp_generate_attachment_metadata( $this->post_id, $this->targetFile->getFullFilePath() );
@@ -134,6 +146,8 @@ class Replacer
134
// update post doesn't update GUID on updates.
135
$wpdb->update( $wpdb->posts, array( 'guid' => $this->target_url), array('ID' => $this->post_id) );
136
//enable-media-replace-upload-done
137
if (is_wp_error($post_id))
138
{
139
$errors = $post_id->get_error_messages();
@@ -147,8 +161,10 @@ class Replacer
147
148
if(wp_attachment_is_image($this->post_id))
149
{
150
- $this->ThumbnailUpdater->setNewMetadata($metadata);
151
- $this->ThumbnailUpdater->updateThumbnails();
152
}
153
154
// if all set and done, update the date.
@@ -168,7 +184,8 @@ class Replacer
168
169
protected function getNewTitle()
170
{
171
- $title = $this->targetFile->getFileName();
172
$meta = $this->target_metadata;
173
174
if (isset($meta['image_meta']))
@@ -194,7 +211,7 @@ class Replacer
194
$targetPath = null;
195
if ($this->replaceMode == self::MODE_REPLACE)
196
{
197
- $targetPath = $this->sourceFile->getFullFilePath(); // overwrite source
198
}
199
elseif ($this->replaceMode == self::MODE_SEARCHREPLACE)
200
{
@@ -202,9 +219,26 @@ class Replacer
202
$unique = wp_unique_filename($path, $this->targetName);
203
204
$new_filename = apply_filters( 'emr_unique_filename', $unique, $path, $this->post_id );
205
- $targetPath = trailingslashit($path) . $new_filename;
206
}
207
- return $targetPath;
208
}
209
210
/** Tries to remove all of the old image, without touching the metadata in database
@@ -212,9 +246,10 @@ class Replacer
212
*/
213
protected function removeCurrent()
214
{
215
- $meta = wp_get_attachment_metadata( $this->post_id );
216
$backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
217
- $result = wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $this->sourceFile->getFullFilePath() );
218
}
219
220
/** Handle new dates for the replacement */
@@ -255,7 +290,14 @@ class Replacer
255
global $wpdb;
256
257
// Search-and-replace filename in post database
258
- $current_base_url = emr_get_match_url( $this->source_url );
259
260
/* Search and replace in WP_POSTS */
261
$posts_sql = $wpdb->remove_placeholder_escape($wpdb->prepare(
@@ -277,8 +319,9 @@ class Replacer
277
$replace_urls = emr_get_file_urls( $this->target_url, $this->target_metadata );
278
$replace_urls = array_values(emr_normalize_file_urls( $search_urls, $replace_urls ));
279
280
- if ( ! empty( $rs ) ) {
281
282
foreach ( $rs AS $rows ) {
283
$number_of_updates = $number_of_updates + 1;
284
// replace old URLs with new URLs.
@@ -292,6 +335,9 @@ class Replacer
292
// echo "$sql <BR>";
293
$wpdb->query( $sql );
294
}
295
foreach ($rsmeta as $row)
296
{
297
$number_of_updates++;
@@ -301,8 +347,8 @@ class Replacer
301
$sql = $wpdb->prepare('UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d', $content, $row['meta_id'] );
302
$wpdb->query($sql);
303
}
304
305
- }
306
307
} // doSearchReplace
308
1
<?php
2
namespace EnableMediaReplace;
3
use \EnableMediaReplace\emrFile as File;
4
+ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
5
+ use EnableMediaReplace\Notices\NoticeController as Notices;
6
7
class Replacer
8
{
41
$source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
42
43
$this->sourceFile = new File($source_file);
44
+
45
$this->source_post = get_post($post_id);
46
$this->source_is_image = wp_attachment_is('image', $this->source_post);
47
$this->source_metadata = wp_get_attachment_metadata( $post_id );
68
/** Replace the sourceFile with a target
69
* @param $file String Full Path to the Replacement File. This will usually be an uploaded file in /tmp/
70
* @param $fileName String The fileName of the uploaded file. This will be used if sourcefile is not to be overwritten.
71
+ * @throws RunTimeException Can throw exception if something went wrong with the files.
72
*/
73
public function replaceWith($file, $fileName)
74
{
82
83
if (is_null($targetFile))
84
{
85
+ $ex = __('Target File could not be set. The source file might not be there. In case of search and replace, a filter might prevent this', "enable-media-replace");
86
+ throw new \RuntimeException($ex);
87
}
88
89
+ $targetFileObj = new File($targetFile);
90
+ $result = $targetFileObj->checkAndCreateFolder();
91
+ if ($result === false)
92
+ Log::addError('Directory creation for targetFile failed');
93
+
94
/* @todo See if wp_handle_sideload / wp_handle_upload can be more securely used for this */
95
$result_moved = move_uploaded_file($file,$targetFile);
96
97
if (false === $result_moved)
98
{
99
+ $ex = sprintf( esc_html__('The uploaded file could not be moved to %1$s. This is most likely an issue with permissions, or upload failed.', "enable-media-replace"), $targetFile );
100
+ throw new \RuntimeException($ex);
101
}
102
+
103
+ // init targetFile.
104
$this->targetFile = new File($targetFile);
105
106
if ($this->sourceFile->getPermissions() > 0)
125
{
126
update_attached_file($this->post_id, $filtered['file'] );
127
$this->targetFile = new File($filtered['file']); // handle as a new file
128
+ Log::addInfo('WP_Handle_upload filter returned different file', $filtered);
129
}
130
131
$metadata = wp_generate_attachment_metadata( $this->post_id, $this->targetFile->getFullFilePath() );
146
// update post doesn't update GUID on updates.
147
$wpdb->update( $wpdb->posts, array( 'guid' => $this->target_url), array('ID' => $this->post_id) );
148
//enable-media-replace-upload-done
149
+
150
+ // @todo Replace this one with proper Notices:addError;
151
if (is_wp_error($post_id))
152
{
153
$errors = $post_id->get_error_messages();
161
162
if(wp_attachment_is_image($this->post_id))
163
{
164
+ $this->ThumbnailUpdater->setNewMetadata($this->target_metadata);
165
+ $result = $this->ThumbnailUpdater->updateThumbnails();
166
+ if (false === $result)
167
+ Log::addWarn('Thumbnail Updater returned false');
168
}
169
170
// if all set and done, update the date.
184
185
protected function getNewTitle()
186
{
187
+ // get basename without extension
188
+ $title = basename($this->targetFile->getFileName(), '.' . $this->targetFile->getFileExtension());
189
$meta = $this->target_metadata;
190
191
if (isset($meta['image_meta']))
211
$targetPath = null;
212
if ($this->replaceMode == self::MODE_REPLACE)
213
{
214
+ $targetFile = $this->sourceFile->getFullFilePath(); // overwrite source
215
}
216
elseif ($this->replaceMode == self::MODE_SEARCHREPLACE)
217
{
219
$unique = wp_unique_filename($path, $this->targetName);
220
221
$new_filename = apply_filters( 'emr_unique_filename', $unique, $path, $this->post_id );
222
+ $targetFile = trailingslashit($path) . $new_filename;
223
}
224
+ if (is_dir($targetFile)) // this indicates an error with the source.
225
+ {
226
+ Log::addWarn('TargetFile is directory ' . $targetFile );
227
+ $upload_dir = wp_upload_dir();
228
+ if (isset($upload_dir['path']))
229
+ {
230
+ $targetFile = trailingslashit($upload_dir['path']) . wp_unique_filename($targetFile, $this->targetName);
231
+ }
232
+ else {
233
+ $err = 'EMR could not establish a proper destination for replacement';
234
+ Log::addError($err);
235
+ throw new \RuntimeException($err);
236
+ exit($err); // fallback
237
+
238
+ }
239
+ }
240
+
241
+ return $targetFile;
242
}
243
244
/** Tries to remove all of the old image, without touching the metadata in database
246
*/
247
protected function removeCurrent()
248
{
249
+ $meta = \wp_get_attachment_metadata( $this->post_id );
250
$backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
251
+ $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $this->sourceFile->getFullFilePath() );
252
+
253
}
254
255
/** Handle new dates for the replacement */
290
global $wpdb;
291
292
// Search-and-replace filename in post database
293
+ $current_base_url = emr_get_match_url( $this->source_url);
294
+
295
+ /** Fail-safe if base_url is a whole directory, don't go search/replace */
296
+ if (is_dir($current_base_url))
297
+ {
298
+ Log::addError('Search Replace tried to replace to directory - ' . $current_base_url);
299
+ exit('Fail Safe :: Source Location seems to be a directory.');
300
+ }
301
302
/* Search and replace in WP_POSTS */
303
$posts_sql = $wpdb->remove_placeholder_escape($wpdb->prepare(
319
$replace_urls = emr_get_file_urls( $this->target_url, $this->target_metadata );
320
$replace_urls = array_values(emr_normalize_file_urls( $search_urls, $replace_urls ));
321
322
+ Log::addDebug('Replacing references', array($search_urls, $replace_urls));
323
324
+ if ( ! empty( $rs ) ) {
325
foreach ( $rs AS $rows ) {
326
$number_of_updates = $number_of_updates + 1;
327
// replace old URLs with new URLs.
335
// echo "$sql <BR>";
336
$wpdb->query( $sql );
337
}
338
+ }
339
+ if (! empty($rsmeta))
340
+ {
341
foreach ($rsmeta as $row)
342
{
343
$number_of_updates++;
347
$sql = $wpdb->prepare('UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d', $content, $row['meta_id'] );
348
$wpdb->query($sql);
349
}
350
+ }
351
352
353
} // doSearchReplace
354
classes/uihelper.php ADDED
@@ -0,0 +1,297 @@
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_height = 500;
14
+ protected $preview_max_width = 400;
15
+
16
+ protected $full_width = 0;
17
+ protected $full_height = 0;
18
+
19
+ public function __construct()
20
+ {
21
+
22
+ }
23
+
24
+
25
+ public function getFormUrl($attach_id)
26
+ {
27
+ $url = admin_url('upload.php');
28
+ $url = add_query_arg(array(
29
+ 'page' => 'enable-media-replace/enable-media-replace.php',
30
+ 'noheader' => true,
31
+ 'action' => 'media_replace_upload',
32
+ 'attachment_id' => $attach_id,
33
+ ));
34
+
35
+ if (isset($_REQUEST['SHORTPIXEL_DEBUG']))
36
+ {
37
+ $spdebug = $_REQUEST['SHORTPIXEL_DEBUG'];
38
+ if (is_numeric($spdebug))
39
+ $spdebug = intval($spdebug);
40
+ else {
41
+ $spdebug = sanitize_text_field($spdebug);
42
+ }
43
+
44
+ $url = add_query_arg('SHORTPIXEL_DEBUG', $spdebug, $url);
45
+ }
46
+
47
+ return $url;
48
+
49
+ }
50
+
51
+ public function getSuccesRedirect($post_id)
52
+ {
53
+ $url = admin_url('post.php');
54
+ $url = add_query_arg(array('action' => 'edit', 'post' => $post_id), $url);
55
+
56
+ $url = apply_filters('emr_returnurl', $url);
57
+ Log::addDebug('Success URL- ' . $url);
58
+
59
+ return $url;
60
+
61
+ }
62
+
63
+ public function getFailedRedirect($attach_id)
64
+ {
65
+ $url = admin_url('upload.php');
66
+ $url = add_query_arg(array(
67
+ 'page' => 'enable-media-replace/enable-media-replace.php',
68
+ 'action' => 'media_replace',
69
+ 'attachment_id' => $attach_id,
70
+ '_wpnonce' => wp_create_nonce('media_replace'),
71
+ ), $url
72
+ );
73
+
74
+ $url = apply_filters('emr_returnurl_failed', $url);
75
+ Log::addDebug('Failed URL- ' . $url);
76
+ return $url;
77
+ }
78
+
79
+
80
+
81
+ public function setPreviewSizes()
82
+ {
83
+ list($this->preview_size, $this->preview_width, $this->preview_height) = $this->findImageSizeByMax($this->preview_max_width);
84
+ }
85
+
86
+ public function setSourceSizes($attach_id)
87
+ {
88
+ $data = wp_get_attachment_image_src($attach_id, 'full');
89
+ if (is_array($data))
90
+ {
91
+ $this->full_width = $data[1];
92
+ $this->full_height = $data[2];
93
+ }
94
+ }
95
+
96
+ // Returns Preview Image HTML Output.
97
+ public function getPreviewImage($attach_id)
98
+ {
99
+ $data = false;
100
+
101
+ if ($attach_id > 0)
102
+ {
103
+ $data = wp_get_attachment_image_src($attach_id, $this->preview_size);
104
+ $file = get_attached_file($attach_id);
105
+ Log::addDebug('Attached File ' . $file, $data);
106
+
107
+ }
108
+
109
+ $mime_type = get_post_mime_type($attach_id);
110
+
111
+ if (! is_array($data) || ! file_exists($file) )
112
+ {
113
+ // if attachid higher than zero ( exists ) but not the image, fail, that's an error state.
114
+ $icon = ($attach_id < 0) ? '' : 'dashicons-no';
115
+ $is_document = false;
116
+
117
+ $args = array(
118
+ 'width' => $this->preview_width,
119
+ 'height' => $this->preview_height,
120
+ 'is_image' => false,
121
+ 'is_document' => $is_document,
122
+ 'icon' => $icon,
123
+ );
124
+
125
+ // failed, it might be this server doens't support PDF thumbnails. Fallback to File preview.
126
+ if ($mime_type == 'application/pdf')
127
+ {
128
+ return $this->getPreviewFile($attach_id);
129
+ }
130
+
131
+ return $this->getPlaceHolder($args);
132
+ }
133
+
134
+ $url = $data[0];
135
+ $width = $data[1];
136
+ $height = $data[2];
137
+ // preview width, if source if found, should be set to source.
138
+ $this->preview_width = $width;
139
+ $this->preview_height = $height;
140
+
141
+ if ($width > $this->preview_max_width)
142
+ $width = $this->preview_max_width;
143
+ if ($height > $this->preview_max_height)
144
+ $height = $this->preview_max_height;
145
+
146
+ $image = "<img src='$url' width='$width' height='$height' class='image' style='max-width:100%; max-height: 100%;' />";
147
+
148
+ $args = array(
149
+ 'width' => $width,
150
+ 'height' => $height,
151
+ 'image' => $image,
152
+ 'mime_type' => $mime_type,
153
+ );
154
+ $output = $this->getPlaceHolder($args);
155
+ return $output;
156
+ }
157
+
158
+ public function getPreviewError($attach_id)
159
+ {
160
+ $args = array(
161
+ 'width' => $this->preview_width,
162
+ 'height' => $this->preview_height,
163
+ 'icon' => 'dashicons-no',
164
+ 'is_image' => false,
165
+ );
166
+ $output = $this->getPlaceHolder($args);
167
+ return $output;
168
+ }
169
+
170
+ public function getPreviewFile($attach_id)
171
+ {
172
+ if ($attach_id > 0)
173
+ {
174
+ $filepath = get_attached_file($attach_id);
175
+ $filename = basename($filepath);
176
+ }
177
+ else {
178
+ $filename = false;
179
+ }
180
+
181
+ $mime_type = get_post_mime_type($attach_id);
182
+
183
+ $args = array(
184
+ 'width' => 300,
185
+ 'height' => 300,
186
+ 'is_image' => false,
187
+ 'is_document' => true,
188
+ 'layer' => $filename,
189
+ 'mime_type' => $mime_type,
190
+ );
191
+ $output = $this->getPlaceHolder($args);
192
+ return $output;
193
+ }
194
+
195
+ public function findImageSizeByMax($maxwidth)
196
+ {
197
+ $image_sizes = $this->get_image_sizes();
198
+
199
+ $match_width = 0;
200
+ $match_height = 0;
201
+ $match = '';
202
+
203
+ foreach($image_sizes as $sizeName => $sizeItem)
204
+ {
205
+
206
+ $width = $sizeItem['width'];
207
+ if ($width > $match_width && $width <= $maxwidth)
208
+ {
209
+ $match = $sizeName;
210
+ $match_width = $width;
211
+ $match_height = $sizeItem['height'];
212
+ }
213
+ }
214
+ return array($match, $match_width, $match_height);
215
+ }
216
+
217
+ public function getPlaceHolder($args)
218
+ {
219
+ $defaults = array(
220
+ 'width' => 150,
221
+ 'height' => 150,
222
+ 'image' => '',
223
+ 'icon' => 'dashicons-media-document',
224
+ 'layer' => $this->full_width . ' x ' . $this->full_height,
225
+ 'is_image' => true,
226
+ 'is_document' => false,
227
+ 'mime_type' => false,
228
+ );
229
+
230
+ $args = wp_parse_args($args, $defaults);
231
+ $w = $args['width'];
232
+ $h = $args['height'];
233
+
234
+ if ($w < 150) // minimum
235
+ $w = 150;
236
+ if ($h < 150)
237
+ $h = 150;
238
+
239
+ $icon = $args['icon'];
240
+
241
+ if ($args['is_image'])
242
+ {
243
+ $placeholder_class = 'is_image';
244
+ }
245
+ else {
246
+ $placeholder_class = 'not_image';
247
+ }
248
+
249
+ if ($args['is_document'])
250
+ {
251
+ $placeholder_class .= ' is_document';
252
+ }
253
+
254
+ $filetype = '';
255
+ if ($args['mime_type'])
256
+ {
257
+ $filetype = 'data-filetype="' . $args['mime_type'] . '"';
258
+ }
259
+
260
+
261
+ $output = "<div class='image_placeholder $placeholder_class' $filetype style='width:" . $w . "px; height:". $h ."px'> ";
262
+ $output .= $args['image'];
263
+ $output .= "<div class='dashicons $icon'>&nbsp;</div>";
264
+ $output .= "<span class='textlayer'>" . $args['layer'] . "</span>";
265
+ $output .= "</div>";
266
+
267
+ return $output;
268
+ }
269
+
270
+ /**
271
+ * Get size information for all currently-registered image sizes.
272
+ * Directly stolen from - https://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
273
+ * @global $_wp_additional_image_sizes
274
+ * @uses get_intermediate_image_sizes()
275
+ * @return array $sizes Data for all currently-registered image sizes.
276
+ */
277
+ private function get_image_sizes() {
278
+ global $_wp_additional_image_sizes;
279
+
280
+ $sizes = array();
281
+
282
+ foreach ( get_intermediate_image_sizes() as $_size ) {
283
+ if ( in_array( $_size, array('thumbnail', 'medium', 'medium_large', 'large') ) ) {
284
+ $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" );
285
+ $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" );
286
+ } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
287
+ $sizes[ $_size ] = array(
288
+ 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
289
+ 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
290
+ );
291
+ }
292
+ }
293
+
294
+ return $sizes;
295
+ }
296
+
297
+ } // class
composer.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "repositories": [
3
+ {
4
+ "packagist.org": false,
5
+ "type": "path",
6
+ "url": "../modules/*",
7
+ "options": {
8
+ "symlink": true
9
+ }
10
+ }
11
+ ],
12
+ "require": {
13
+ "shortpixel/notices":"@dev",
14
+ "shortpixel/build" : "@dev"
15
+ },
16
+
17
+ "scripts": {
18
+ "post-update-cmd" : "\\ShortPixel\\Build\\Build::BuildIt",
19
+ "buildSP" : "\\ShortPixel\\Build\\Build::BuildIt"
20
+ },
21
+ "extra": {
22
+ "targetNamespace" : "EnableMediaReplace"
23
+ }
24
+
25
+ }
css/admin.css CHANGED
@@ -772,14 +772,80 @@
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;
772
min-height: 350px; }
773
.emr_upload_form .image_chooser.wrapper .image_previews {
774
margin: 15px 0; }
775
+ .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder {
776
+ position: relative;
777
+ display: inline-block;
778
+ margin-right: 25px;
779
+ border: 1px solid #ddd;
780
+ vertical-align: top;
781
+ max-height: 500px; }
782
+ .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .textlayer {
783
+ font-size: 25px;
784
+ line-height: 25px;
785
+ opacity: 0.7;
786
+ position: absolute;
787
+ color: #ccc;
788
+ left: 48%;
789
+ top: 50%;
790
+ transform: translate(-50%, -50%);
791
+ border: 1px dashed #eee;
792
+ background-color: #333;
793
+ padding: 8px; }
794
+ .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder .dashicons {
795
+ font-size: 60px;
796
+ position: absolute;
797
+ top: 50%;
798
+ margin-top: -30px;
799
+ left: 50%;
800
+ margin-left: -30px;
801
+ opacity: 0.5; }
802
+ .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 {
803
+ display: none; }
804
+ .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.not_image img {
805
+ display: none; }
806
+ .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.not_image .textlayer {
807
+ display: none; }
808
+ .emr_upload_form .image_chooser.wrapper .image_previews .image_placeholder.not_image.is_document .textlayer {
809
+ font-size: 18px;
810
+ line-height: 20px;
811
+ display: block; }
812
+ .emr_upload_form .form-error, .emr_upload_form .form-warning {
813
+ background: #fff;
814
+ padding: 8px;
815
+ border-left: 4px solid #ff0000;
816
+ margin: 10px 0;
817
+ display: none; }
818
+ .emr_upload_form .form-error p, .emr_upload_form .form-warning p {
819
+ margin: 0; }
820
+ .emr_upload_form .form-warning {
821
+ border-left: 4px solid #ffb900; }
822
.emr_upload_form .option-flex-wrapper {
823
display: flex; }
824
.emr_upload_form .replace_type.wrapper {
825
flex: 1;
826
border: 1px solid #ccc;
827
margin: 15px 0; }
828
+ .emr_upload_form .replace_type.wrapper .option {
829
+ position: relative;
830
+ z-index: 1; }
831
+ .emr_upload_form .replace_type.wrapper .option label {
832
+ font-size: 1.2em; }
833
+ .emr_upload_form .replace_type.wrapper .option .nofeature-notice {
834
+ border: 1px solid #ccc;
835
+ padding: 8px;
836
+ margin: 0;
837
+ position: absolute;
838
+ left: 0;
839
+ right: 0;
840
+ top: 0;
841
+ bottom: 0;
842
+ opacity: 0.8;
843
+ z-index: 9;
844
+ background: #444; }
845
+ .emr_upload_form .replace_type.wrapper .option .nofeature-notice p {
846
+ text-align: center;
847
+ color: #fff;
848
+ margin: 15px 0; }
849
.emr_upload_form .options.wrapper {
850
flex: 1;
851
border: 1px solid #ccc;
enable-media-replace.php CHANGED
@@ -3,7 +3,7 @@
3
Plugin Name: Enable Media Replace
4
Plugin URI: https://wordpress.org/plugins/enable-media-replace/
5
Description: Enable replacing media files by uploading a new file in the "Edit Media" section of the WordPress Media Library.
6
- Version: 3.3.1
7
Author: ShortPixel
8
Author URI: https://shortpixel.com
9
Text Domain: enable-media-replace
@@ -24,6 +24,8 @@ http://www.gnu.org/licenses/gpl.html
24
*
25
*/
26
27
if ( ! defined( 'ABSPATH' ) ) {
28
exit; // Exit if accessed directly.
29
}
@@ -40,10 +42,13 @@ if(!defined("SHORTPIXEL_AFFILIATE_CODE")) {
40
define("SHORTPIXEL_AFFILIATE_CODE", 'VKG6LYN28044');
41
}
42
43
require_once('classes/replacer.php');
44
require_once('classes/file.php');
45
require_once('classes/cache.php');
46
require_once('classes/emr-plugin.php');
47
require_once('thumbnail_updater.php');
48
49
- $emr_plugin = new \EnableMediaReplace\EnableMediaReplacePlugin();
3
Plugin Name: Enable Media Replace
4
Plugin URI: https://wordpress.org/plugins/enable-media-replace/
5
Description: Enable replacing media files by uploading a new file in the "Edit Media" section of the WordPress Media Library.
6
+ Version: 3.3.2
7
Author: ShortPixel
8
Author URI: https://shortpixel.com
9
Text Domain: enable-media-replace
24
*
25
*/
26
27
+ namespace EnableMediaReplace;
28
+
29
if ( ! defined( 'ABSPATH' ) ) {
30
exit; // Exit if accessed directly.
31
}
42
define("SHORTPIXEL_AFFILIATE_CODE", 'VKG6LYN28044');
43
}
44
45
+ require_once('build/shortpixel/autoload.php');
46
require_once('classes/replacer.php');
47
+ require_once('classes/uihelper.php');
48
require_once('classes/file.php');
49
require_once('classes/cache.php');
50
require_once('classes/emr-plugin.php');
51
+ require_once('classes/externals.php');
52
require_once('thumbnail_updater.php');
53
54
+ $emr_plugin = EnableMediaReplacePlugin::get();
js/emr_admin.js CHANGED
<
@@ -3,12 +3,39 @@ jQuery(document).ready(function($)
3
// interface for emr.
4
var emrIf = new function ()
5
{
6
7
this.init = function()
8
{
9
$('input[name="timestamp_replace"]').on('change', $.proxy(this.checkCustomDate, this));
10
this.checkCustomDate();
11
this.loadDatePicker();
12
},
13
this.loadDatePicker = function()
14
{
@@ -28,7 +55,6 @@ jQuery(document).ready(function($)
28
},
29
this.checkCustomDate = function()
30
{
31
- console.log('check');
32
if ($('input[name="timestamp_replace"]:checked').val() == 3)
33
this.showCustomDate();
34
else
@@ -46,26 +72,148 @@ jQuery(document).ready(function($)
46
$('.custom_date').css('visibility', 'hidden');
47
});
48
}
49
- } // emrIf
50
51
- /*emrIf.
52
53
- $('input[name="timestamp_replace"]').on('change',function(e)
54
- {
55
- var target = e.target;
56
- var value = $(e.target).val();
57
- if (value == 3) // custom date
58
{
59
- $('.custom_date').css('visibility', 'visible').fadeTo(100, 1);
60
}
61
- else {
62
- $('.custom_date').fadeTo(100,0,
63
- function ()
64
- {
65
- $('.custom_date').css('visibility', 'hidden');
66
- });
67
}
68
- });*/