Enable Media Replace - Version 3.3.8

Version Description

Release date: 18th February 2020 * Fixes for WP 5.3+ -scaled images system. Will now replace those as well. * Updating and replacing images and thumbnails should now work for serialized (and such) metadata. - For Beaver Builder * Add extra warning if mimetype is not allowed by WordPress for upload * Put Javascript version in wp_register_script * Hidden double 'replace media' * Replace Image label to Replace Media * Removed constant S3_UPLOADS_AUTOENABLE * Improved detection of SVG image sizes * Fixed - Logger doesn't call wp_upload_dir when not debugging * Extra - Javascript tries to resume after external errors. * Language 0 new strings added, 2 updated, 0 fuzzied, and 0 obsoleted

Download this release

Release Info

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

Code changes from version 3.3.7 to 3.3.8

build/shortpixel/log/composer.json CHANGED
@@ -1,7 +1,7 @@
1
{
2
"name": "shortpixel/log",
3
"description": "ShortPixel Logging",
4
- "version": "1.1.2",
5
"type": "library",
6
"license": "MIT",
7
"authors": [
1
{
2
"name": "shortpixel/log",
3
"description": "ShortPixel Logging",
4
+ "version": "1.1.3",
5
"type": "library",
6
"license": "MIT",
7
"authors": [
build/shortpixel/log/src/ShortPixelLogger.php CHANGED
@@ -49,12 +49,6 @@ namespace EnableMediaReplace\ShortPixelLogger;
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 = $this->setLogPath($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;
@@ -88,12 +82,14 @@ namespace EnableMediaReplace\ShortPixelLogger;
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();
@@ -132,26 +128,33 @@ namespace EnableMediaReplace\ShortPixelLogger;
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
@@ -225,22 +228,26 @@ namespace EnableMediaReplace\ShortPixelLogger;
225
public static function addError($message, $args = array())
226
{
227
$level = DebugItem::LEVEL_ERROR;
228
- static::addLog($message, $level, $args);
229
}
230
public static function addWarn($message, $args = array())
231
{
232
$level = DebugItem::LEVEL_WARN;
233
- static::addLog($message, $level, $args);
234
}
235
public static function addInfo($message, $args = array())
236
{
237
$level = DebugItem::LEVEL_INFO;
238
- static::addLog($message, $level, $args);
239
}
240
public static function addDebug($message, $args = array())
241
{
242
$level = DebugItem::LEVEL_DEBUG;
243
- static::addLog($message, $level, $args);
244
}
245
246
public static function logLevel($level)
49
$ns = __NAMESPACE__;
50
$this->namespace = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
51
52
if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) // manual takes precedence over constants
53
{
54
$this->is_manual_request = true;
82
83
}
84
85
+ if ($this->is_active)
86
+ {
87
+ /* On Early init, this function might not exist, then queue it when needed */
88
+ if (! function_exists('wp_get_current_user'))
89
+ add_action('init', array($this, 'initView'));
90
+ else
91
+ $this->initView();
92
+ }
93
94
if ($this->is_active && count($this->hooks) > 0)
95
$this->monitorHooks();
128
{
129
$this->logPath = $logPath;
130
}
131
+ protected function addLog($message, $level, $data = array())
132
{
133
+ // $log = self::getInstance();
134
135
+ // don't log anything too low or when not active.
136
+ if ($this->logLevel < $level || ! $this->is_active)
137
{
138
return;
139
}
140
141
+ // Check where to log to.
142
+ if ($this->logPath === false)
143
+ {
144
+ $upload_dir = wp_upload_dir(null,false,false);
145
+ $this->logPath = $this->setLogPath($upload_dir['basedir'] . '/' . $this->namespace . ".log");
146
+ }
147
+
148
$arg = array();
149
$args['level'] = $level;
150
$args['data'] = $data;
151
152
$newItem = new DebugItem($message, $args);
153
+ $this->items[] = $newItem;
154
155
+ if ($this->is_active)
156
{
157
+ $this->write($newItem);
158
}
159
}
160
228
public static function addError($message, $args = array())
229
{
230
$level = DebugItem::LEVEL_ERROR;
231
+ $log = self::getInstance();
232
+ $log->addLog($message, $level, $args);
233
}
234
public static function addWarn($message, $args = array())
235
{
236
$level = DebugItem::LEVEL_WARN;
237
+ $log = self::getInstance();
238
+ $log->addLog($message, $level, $args);
239
}
240
public static function addInfo($message, $args = array())
241
{
242
$level = DebugItem::LEVEL_INFO;
243
+ $log = self::getInstance();
244
+ $log->addLog($message, $level, $args);
245
}
246
public static function addDebug($message, $args = array())
247
{
248
$level = DebugItem::LEVEL_DEBUG;
249
+ $log = self::getInstance();
250
+ $log->addLog($message, $level, $args);
251
}
252
253
public static function logLevel($level)
build/shortpixel/notices/composer.json CHANGED
@@ -1,7 +1,7 @@
1
{
2
"name": "shortpixel/notices",
3
"description": "ShortPixel WordPress Notice System",
4
- "version": "1.2",
5
"type": "library",
6
"license": "MIT",
7
"authors": [
1
{
2
"name": "shortpixel/notices",
3
"description": "ShortPixel WordPress Notice System",
4
+ "version": "1.3",
5
"type": "library",
6
"license": "MIT",
7
"authors": [
build/shortpixel/notices/src/NoticeController.php CHANGED
@@ -8,6 +8,8 @@ class NoticeController //extends ShortPixelController
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;
@@ -22,16 +24,29 @@ class NoticeController //extends ShortPixelController
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');
@@ -50,8 +65,10 @@ class NoticeController //extends ShortPixelController
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;
@@ -64,7 +81,7 @@ class NoticeController //extends ShortPixelController
64
}
65
66
67
- public function addNotice($message, $code, $unique)
68
{
69
$notice = new NoticeModel($message, $code);
70
@@ -73,7 +90,7 @@ class NoticeController //extends ShortPixelController
73
foreach(self::$notices as $nitem)
74
{
75
if ($nitem->message == $notice->message && $nitem->code == $notice->code) // same message.
76
- return false;
77
}
78
}
79
self::$notices[] = $notice;
@@ -118,17 +135,97 @@ class NoticeController //extends ShortPixelController
118
119
public function getNotices()
120
{
121
- return self::$notices;
122
}
123
124
- public static function getInstance()
125
{
126
- if ( self::$instance === null)
127
- {
128
- self::$instance = new NoticeController();
129
- }
130
131
- return self::$instance;
132
}
133
134
/** Adds a notice, quick and fast method
@@ -170,6 +267,30 @@ class NoticeController //extends ShortPixelController
170
171
}
172
173
public function admin_notices()
174
{
175
if ($this->countNotices() > 0)
@@ -179,7 +300,7 @@ class NoticeController //extends ShortPixelController
179
add_action('admin_print_footer_scripts', array($this, 'printNoticeStyle'));
180
self::$cssHookLoaded = true;
181
}
182
- foreach($this->getNotices() as $notice)
183
{
184
echo $notice->getForDisplay();
185
}
@@ -187,6 +308,7 @@ class NoticeController //extends ShortPixelController
187
$this->update(); // puts views, and updates
188
}
189
190
public function printNoticeStyle()
191
{
192
if (file_exists(__DIR__ . '/css/notices.css'))
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;
24
$ns = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
25
$this->notice_option = $ns . '-notices';
26
27
+ add_action('wp_ajax_' . $this->notice_option, array($this, 'ajax_action'));
28
+
29
$this->loadNotices();
30
//$this->loadConfig();
31
}
32
33
+ public static function getInstance()
34
+ {
35
+ if ( self::$instance === null)
36
+ {
37
+ self::$instance = new NoticeController();
38
+ }
39
+
40
+ return self::$instance;
41
+ }
42
+
43
/** Load Notices Config File, if any
44
*
45
* [ Future Use ]
46
*/
47
public function loadConfig()
48
{
49
+ return;
50
if (file_exists('../notice_config.json'))
51
{
52
$config = file_get_contents('../notice_config.json');
65
{
66
$notices = get_option($this->notice_option, false);
67
$cnotice = (is_array($notices)) ? count($notices) : 0;
68
+ if ($cnotice > 0)
69
+ Log::addDebug('Notice Control - #num notices' . $cnotice);
70
+
71
+ if ($notices !== false && is_array($notices))
72
{
73
self::$notices = $notices;
74
$this->has_stored = true;
81
}
82
83
84
+ protected function addNotice($message, $code, $unique)
85
{
86
$notice = new NoticeModel($message, $code);
87
90
foreach(self::$notices as $nitem)
91
{
92
if ($nitem->message == $notice->message && $nitem->code == $notice->code) // same message.
93
+ return $notice; // return the notice with the same message.
94
}
95
}
96
self::$notices[] = $notice;
135
136
public function getNotices()
137
{
138
+ return self::$notices;
139
}
140
141
+ public function getNoticesForDisplay()
142
{
143
+ $newNotices = array();
144
+ foreach(self::$notices as $notice)
145
+ {
146
+ if ($notice->isDismissed()) // dismissed never displays.
147
+ continue;
148
149
+ if ($notice->isPersistent())
150
+ {
151
+ $id = $notice->getID();
152
+ if (! is_null($id) && ! in_array($id, $this->notice_displayed))
153
+ {
154
+ $notice->notice_action = $this->notice_option;
155
+ $newNotices[] = $notice;
156
+ $this->notice_displayed[] = $id;
157
+ }
158
+
159
+ }
160
+ else
161
+ $newNotices[] = $notice;
162
+
163
+
164
+ }
165
+ return $newNotices;
166
+ }
167
+
168
+
169
+ public function getNoticeByID($id)
170
+ {
171
+ foreach(self::$notices as $notice)
172
+ {
173
+ if ($notice->getID() == $id)
174
+ return $notice;
175
+ }
176
+
177
+ return false;
178
+ }
179
+
180
+ public static function removeNoticeByID($id)
181
+ {
182
+ $noticeController = self::getInstance();
183
+
184
+ for($i = 0; $i < count(self::$notices); $i++)
185
+ {
186
+ $item = self::$notices[$i];
187
+ if ($item->getID() == $id)
188
+ {
189
+ Log::addDebug('Removing notice with ID ' . $id);
190
+ unset(self::$notices[$i]);
191
+ }
192
+ //if ($notice_item )
193
+ }
194
+ $noticeController->update();
195
+ }
196
+
197
+ public function ajax_action()
198
+ {
199
+ $response = array('result' => false, 'reason' => '');
200
+
201
+ if ( wp_verify_nonce( $_POST['nonce'], 'dismiss') )
202
+ {
203
+ if ($_POST['plugin_action'] == 'dismiss')
204
+ {
205
+ $id = sanitize_text_field($_POST['id']);
206
+ $notice = $this->getNoticeByID($id);
207
+
208
+ if($notice)
209
+ {
210
+ $notice->dismiss();
211
+ $this->update();
212
+ $response['result'] = true;
213
+ }
214
+ else
215
+ {
216
+ Log::addError('Notice not found when dismissing -> ' . $id, self::$notices);
217
+ $response['result'] = ' Notice ' . $id . ' not found. ';
218
+ }
219
+
220
+ }
221
+
222
+ }
223
+ else
224
+ {
225
+ Log::addError('Wrong Nonce when dismissed notice. ');
226
+ $response['reason'] = 'wrong nonce';
227
+ }
228
+ wp_send_json($response);
229
}
230
231
/** Adds a notice, quick and fast method
267
268
}
269
270
+ public static function makePersistent($notice, $key, $suppress = -1)
271
+ {
272
+ $noticeController = self::getInstance();
273
+ $existing = $noticeController->getNoticeByID($key);
274
+
275
+ // if this key already exists, don't allow the new notice to be entered into the array. Remove it since it's already created.
276
+ if ($existing)
277
+ {
278
+ for($i = 0; $i < count(self::$notices); $i++)
279
+ {
280
+ $item = self::$notices[$i];
281
+ if ($item->message == $notice->message && $item->getID() == null)
282
+ unset(self::$notices[$i]);
283
+ //if ($notice_item )
284
+ }
285
+ }
286
+ else
287
+ {
288
+ $notice->setPersistent($key, $suppress); // set this notice persistent.
289
+ }
290
+
291
+ $noticeController->update();
292
+ }
293
+
294
public function admin_notices()
295
{
296
if ($this->countNotices() > 0)
300
add_action('admin_print_footer_scripts', array($this, 'printNoticeStyle'));
301
self::$cssHookLoaded = true;
302
}
303
+ foreach($this->getNoticesForDisplay() as $notice)
304
{
305
echo $notice->getForDisplay();
306
}
308
$this->update(); // puts views, and updates
309
}
310
311
+
312
public function printNoticeStyle()
313
{
314
if (file_exists(__DIR__ . '/css/notices.css'))
build/shortpixel/notices/src/NoticeModel.php CHANGED
@@ -3,14 +3,20 @@ namespace EnableMediaReplace\Notices;
3
4
class NoticeModel //extends ShortPixelModel
5
{
6
- public $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;
@@ -18,7 +24,7 @@ class NoticeModel //extends ShortPixelModel
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;
@@ -28,11 +34,53 @@ class NoticeModel //extends ShortPixelModel
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)
@@ -96,12 +144,37 @@ class NoticeModel //extends ShortPixelModel
96
97
if ($this->is_persistent)
98
{
99
- $class .= '';
100
}
101
102
- return "<div class='$class'>" . $icon . "<p>" . $this->message . "</p></div>";
103
104
}
105
106
107
}
3
4
class NoticeModel //extends ShortPixelModel
5
{
6
+ public $message; // The message we want to convey.
7
public $code;
8
9
+ private $id = null; // used for persistent messages.
10
+ protected $viewed = false; // was this notice viewed?
11
+ protected $is_persistent = false; // This is a fatal issue, display until something was fixed.
12
+ protected $is_dismissed = false; // for persistent notices,
13
+ protected $suppress_until = null;
14
+ protected $suppress_period = -1;
15
public $is_removable = true; // if removable, display a notice dialog with red X or so.
16
public $messageType = self::NOTICE_NORMAL;
17
18
+ public $notice_action; // empty unless for display. Ajax action to talk back to controller.
19
+
20
public static $icons = array();
21
22
const NOTICE_NORMAL = 1;
24
const NOTICE_SUCCESS = 3;
25
const NOTICE_WARNING = 4;
26
27
+ /** Use this model in conjunction with NoticeController, do not call directly */
28
public function __construct($message, $messageType = self::NOTICE_NORMAL)
29
{
30
$this->message = $message;
34
35
public function isDone()
36
{
37
+ // check suppressed
38
+ if ($this->is_dismissed && ! is_null($this->suppress_until))
39
+ {
40
+ if (time() >= $this->suppress_until)
41
+ {
42
+ //Log::addDebug('')
43
+ $this->is_persistent = false; // unpersist, so it will be cleaned and dropped.
44
+
45
+ }
46
+ }
47
+
48
if ($this->viewed && ! $this->is_persistent)
49
return true;
50
else
51
return false;
52
+ }
53
+
54
+ public function getID()
55
+ {
56
+ return $this->id;
57
+ }
58
+
59
+ public function isPersistent()
60
+ {
61
+ return $this->is_persistent;
62
+ }
63
+
64
+ public function isDismissed()
65
+ {
66
+ return $this->is_dismissed;
67
+ }
68
69
+ public function dismiss()
70
+ {
71
+ $this->is_dismissed = true;
72
+ $this->suppress_until = time() + $this->suppress_period;
73
+ }
74
+
75
+ /** Set a notice persistent. Meaning it shows every page load until dismissed.
76
+ * @param $key Unique Key of this message. Required
77
+ * @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
78
+ */
79
+ public function setPersistent($key, $suppress = -1)
80
+ {
81
+ $this->id = $key;
82
+ $this->is_persistent = true;
83
+ $this->suppress_period = $suppress;
84
}
85
86
public static function setIcon($notice_type, $icon)
144
145
if ($this->is_persistent)
146
{
147
+ $class .= 'is-persistent ';
148
}
149
150
+ $id = ! is_null($this->id) ? 'id="' . $this->id . '"' : '';
151
+
152
+ $output = "<div $id class='$class'><span class='icon'> " . $icon . "</span> <span class='content'>" . $this->message . "</span></div>";
153
+ if ($this->is_persistent && $this->is_removable)
154
+ {
155
+ $output .= "<script type='text/javascript'>\n" . $this->getDismissJS() . "\n</script>";
156
+ }
157
+ return $output;
158
159
}
160
161
+ private function getDismissJS()
162
+ {
163
+ $url = wp_json_encode(admin_url('admin-ajax.php'));
164
+ // $action = 'dismiss';
165
+ $nonce = wp_create_nonce('dismiss');
166
+
167
+ $data = wp_json_encode(array('action' => $this->notice_action, 'plugin_action' => 'dismiss', 'nonce' => $nonce, 'id' => $this->id, 'time' => $this->suppress_period));
168
+
169
+ // $data_string = "{action:'$this->notice_action'}";
170
+
171
+ $js = "jQuery(document).on('click','#$this->id button',
172
+ function() {
173
+ var data = $data;
174
+ var url = $url;
175
+ jQuery.post(url, data); }
176
+ );";
177
+ return "\n jQuery(document).ready(function(){ \n" . $js . "\n});";
178
+ }
179
180
}
classes/emr-plugin.php CHANGED
@@ -21,10 +21,12 @@ class EnableMediaReplacePlugin
21
self::$instance = new EnableMediaReplacePlugin();
22
23
$log = Log::getInstance();
24
- $uploaddir =wp_upload_dir();
25
- if (isset($uploaddir['basedir']))
26
- $log->setLogPath($uploaddir['basedir'] . "/emr_log");
27
-
28
return self::$instance;
29
}
30
@@ -51,7 +53,7 @@ class EnableMediaReplacePlugin
51
add_action('wp_ajax_emr_dismiss_notices', array($this,'dismiss_notices'));
52
53
// editors
54
- add_action( 'add_meta_boxes', function () { add_meta_box('emr-eplace-box', __('Replace Image', 'enable-media-replace'), array($this, 'replace_meta_box'), 'attachment', 'side', 'low'); } );
55
add_filter('attachment_fields_to_edit', array($this, 'attachment_editor'), 10, 2);
56
57
// shortcode
@@ -63,10 +65,11 @@ class EnableMediaReplacePlugin
63
add_filter('wp_get_attachment_image_src',array($this, 'attempt_uncache_image'), 10, 4);
64
65
// adds a metabox to list thumbnails. This is a cache reset hidden as feature.
66
- add_action( 'add_meta_boxes', function () { add_meta_box('emr-replace-box', __('Replaced Thumbnails Preview', 'enable-media-replace'), array($this, 'show_thumbs_box'), 'attachment', 'side', 'low'); } );
67
- add_filter('postbox_classes_attachment_emr-replace-box', function($classes) { $classes[] = 'closed'; return $classes; });
68
}
69
70
}
71
72
/**
@@ -149,10 +152,13 @@ class EnableMediaReplacePlugin
149
150
wp_register_style('emr_edit-attachment', plugins_url('css/edit_attachment.css', EMR_ROOT_FILE));
151
152
- wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), false, true );
153
$emr_options = array(
154
'dateFormat' => $this->convertdate(get_option( 'date_format' )),
155
'maxfilesize' => wp_max_upload_size(),
156
157
);
158
@@ -203,6 +209,7 @@ class EnableMediaReplacePlugin
203
204
public function replace_meta_box($post)
205
{
206
$url = $this->getMediaReplaceURL($post->ID);
207
208
$action = "media_replace";
@@ -229,6 +236,20 @@ class EnableMediaReplacePlugin
229
return false;
230
}
231
232
foreach($meta['sizes'] as $size => $data)
233
{
234
$display_size = ucfirst(str_replace("_", " ", $size));
@@ -239,6 +260,10 @@ class EnableMediaReplacePlugin
239
240
public function attachment_editor($form_fields, $post)
241
{
242
$url = $this->getMediaReplaceURL($post->ID);
243
$action = "media_replace";
244
$editurl = wp_nonce_url( $url, $action );
@@ -249,6 +274,7 @@ class EnableMediaReplacePlugin
249
"input" => "html",
250
"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")
251
);
252
return $form_fields;
253
}
254
21
self::$instance = new EnableMediaReplacePlugin();
22
23
$log = Log::getInstance();
24
+ if (Log::debugIsActive())
25
+ {
26
+ $uploaddir = wp_upload_dir(null, false, false);
27
+ if (isset($uploaddir['basedir']))
28
+ $log->setLogPath($uploaddir['basedir'] . "/emr_log");
29
+ }
30
return self::$instance;
31
}
32
53
add_action('wp_ajax_emr_dismiss_notices', array($this,'dismiss_notices'));
54
55
// editors
56
+ add_action( 'add_meta_boxes', function () { add_meta_box('emr-replace-box', __('Replace Media', 'enable-media-replace'), array($this, 'replace_meta_box'), 'attachment', 'side', 'low'); } );
57
add_filter('attachment_fields_to_edit', array($this, 'attachment_editor'), 10, 2);
58
59
// shortcode
65
add_filter('wp_get_attachment_image_src',array($this, 'attempt_uncache_image'), 10, 4);
66
67
// adds a metabox to list thumbnails. This is a cache reset hidden as feature.
68
+ add_action( 'add_meta_boxes', function () { add_meta_box('emr-showthumbs-box', __('Replaced Thumbnails Preview', 'enable-media-replace'), array($this, 'show_thumbs_box'), 'attachment', 'side', 'low'); } );
69
+ add_filter('postbox_classes_attachment_emr-showthumbs-box', function($classes) { $classes[] = 'closed'; return $classes; });
70
}
71
72
+
73
}
74
75
/**
152
153
wp_register_style('emr_edit-attachment', plugins_url('css/edit_attachment.css', EMR_ROOT_FILE));
154
155
+ $mimes = array_values(get_allowed_mime_types());
156
+
157
+ wp_register_script('emr_admin', plugins_url('js/emr_admin.js', EMR_ROOT_FILE), array('jquery'), EMR_VERSION, true );
158
$emr_options = array(
159
'dateFormat' => $this->convertdate(get_option( 'date_format' )),
160
'maxfilesize' => wp_max_upload_size(),
161
+ 'allowed_mime' => $mimes,
162
163
);
164
209
210
public function replace_meta_box($post)
211
{
212
+
213
$url = $this->getMediaReplaceURL($post->ID);
214
215
$action = "media_replace";
236
return false;
237
}
238
239
+ if (function_exists('wp_get_original_image_url')) // indicating WP 5.3+
240
+ {
241
+ $source_url = wp_get_original_image_url($post->ID);
242
+ // oldway will give -scaled in case of scaling.
243
+ $source_url_oldway = wp_get_attachment_url($post->ID);
244
+
245
+ if ($source_url !== $source_url_oldway)
246
+ {
247
+ echo "<div class='original previewwrapper'><img src='" . $source_url_oldway . "'><span class='label'>" . __('Original') . "</span></div>";
248
+ }
249
+
250
+ }
251
+
252
+
253
foreach($meta['sizes'] as $size => $data)
254
{
255
$display_size = ucfirst(str_replace("_", " ", $size));
260
261
public function attachment_editor($form_fields, $post)
262
{
263
+ $screen = get_current_screen();
264
+ if(! is_null($screen) && $screen->id == 'attachment') // hide on edit attachment screen.
265
+ return $form_fields;
266
+
267
$url = $this->getMediaReplaceURL($post->ID);
268
$action = "media_replace";
269
$editurl = wp_nonce_url( $url, $action );
274
"input" => "html",
275
"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")
276
);
277
+
278
return $form_fields;
279
}
280
classes/externals.php CHANGED
@@ -25,11 +25,11 @@ class Externals
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)
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)
classes/functions.php CHANGED
@@ -57,7 +57,7 @@ function emr_remove_size_from_filename( $url, $remove_extension = false ) {
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);
61
$url = emr_remove_domain_from_filename($url);
62
return $url;
63
}
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
}
classes/replacer.php CHANGED
@@ -15,7 +15,7 @@ class Replacer
15
protected $source_metadata;
16
protected $source_url;
17
18
- // everything target is what will be.
19
protected $targetFile;
20
protected $targetName;
21
protected $target_metadata;
@@ -38,16 +38,28 @@ class Replacer
38
{
39
$this->post_id = $post_id;
40
41
- $source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
42
$this->sourceFile = new File($source_file);
43
44
$this->source_post = get_post($post_id);
45
$this->source_is_image = wp_attachment_is('image', $this->source_post);
46
$this->source_metadata = wp_get_attachment_metadata( $post_id );
47
- $this->source_url = wp_get_attachment_url($post_id);
48
49
- $this->ThumbnailUpdater = new \ThumbnailUpdater($post_id);
50
- $this->ThumbnailUpdater->setOldMetadata($this->source_metadata);
51
}
52
53
public function setMode($mode)
@@ -133,7 +145,6 @@ class Replacer
133
134
if ($this->replaceMode == self::MODE_SEARCHREPLACE)
135
{
136
-
137
// Write new image title.
138
$title = $this->getNewTitle();
139
$update_ar = array('ID' => $this->post_id);
@@ -155,17 +166,23 @@ class Replacer
155
echo $error;
156
}
157
}
158
- $this->doSearchReplace();
159
160
} // SEARCH REPLACE MODE
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.
171
// This must be done after wp_update_posts
@@ -207,6 +224,12 @@ class Replacer
207
return $title;
208
}
209
210
/** Returns a full target path to place to new file. Including the file name! **/
211
protected function getTargetFile()
212
{
@@ -250,7 +273,10 @@ class Replacer
250
{
251
$meta = \wp_get_attachment_metadata( $this->post_id );
252
$backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
253
- $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $this->sourceFile->getFullFilePath() );
254
255
}
256
@@ -278,65 +304,166 @@ class Replacer
278
279
wp_cache_delete($this->post_id, 'posts');
280
281
-
282
- /* $sql = $wpdb->prepare(
283
- "UPDATE $table_name SET post_date = '$post_date', post_date_gmt = '$post_date_gmt' WHERE ID = %d;",
284
- $ID
285
- );
286
- $wpdb->query($sql); */
287
}
288
289
290
- protected function doSearchReplace()
291
{
292
- global $wpdb;
293
294
// Search-and-replace filename in post database
295
- $current_base_url = emr_get_match_url( $this->source_url);
296
297
/** Fail-safe if base_url is a whole directory, don't go search/replace */
298
if (is_dir($current_base_url))
299
{
300
Log::addError('Search Replace tried to replace to directory - ' . $current_base_url);
301
- exit('Fail Safe :: Source Location seems to be a directory.');
302
}
303
304
/* Search and replace in WP_POSTS */
305
// Removed $wpdb->remove_placeholder_escape from here, not compatible with WP 4.8
306
$posts_sql = $wpdb->prepare(
307
"SELECT ID, post_content FROM $wpdb->posts WHERE post_status = 'publish' AND post_content LIKE %s;",
308
'%' . $current_base_url . '%');
309
310
- //INNER JOIN ' . $wpdb->posts . ' on ' . $wpdb->posts . '.ID = ' . $wpdb->postmeta . '.post_id
311
312
- $postmeta_sql = 'SELECT meta_id, post_id, meta_value FROM ' . $wpdb->postmeta . '
313
- WHERE post_id in (SELECT ID from '. $wpdb->posts . ' where post_status = "publish") AND meta_value like %s ';
314
$postmeta_sql = $wpdb->prepare($postmeta_sql, '%' . $current_base_url . '%');
315
316
$rsmeta = $wpdb->get_results($postmeta_sql, ARRAY_A);
317
$rs = $wpdb->get_results( $posts_sql, ARRAY_A );
318
319
$number_of_updates = 0;
320
321
- $search_urls = emr_get_file_urls( $this->source_url, $this->source_metadata );
322
- $replace_urls = emr_get_file_urls( $this->target_url, $this->target_metadata );
323
- $replace_urls = array_values(emr_normalize_file_urls( $search_urls, $replace_urls ));
324
325
- Log::addDebug('Replacing references', array($search_urls, $replace_urls));
326
327
if ( ! empty( $rs ) ) {
328
foreach ( $rs AS $rows ) {
329
$number_of_updates = $number_of_updates + 1;
330
// replace old URLs with new URLs.
331
$post_content = $rows["post_content"];
332
- $post_content = str_replace( $search_urls, $replace_urls, $post_content );
333
-
334
- $sql = $wpdb->prepare(
335
- "UPDATE $wpdb->posts SET post_content = %s WHERE ID = %d;",
336
- array($post_content, $rows["ID"])
337
- );
338
- // echo "$sql <BR>";
339
- $wpdb->query( $sql );
340
}
341
}
342
if (! empty($rsmeta))
@@ -345,14 +472,161 @@ class Replacer
345
{
346
$number_of_updates++;
347
$content = $row['meta_value'];
348
- $content = str_replace($search_urls, $replace_urls, $content);
349
350
- $sql = $wpdb->prepare('UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d', $content, $row['meta_id'] );
351
- $wpdb->query($sql);
352
}
353
}
354
355
356
} // doSearchReplace
357
358
} // class
15
protected $source_metadata;
16
protected $source_url;
17
18
+ // everything target is what will be. This is set when the image is replace, the result. Used for replacing.
19
protected $targetFile;
20
protected $targetName;
21
protected $target_metadata;
38
{
39
$this->post_id = $post_id;
40
41
+ if (function_exists('wp_get_original_image_path')) // WP 5.3+
42
+ {
43
+ $source_file = wp_get_original_image_path($post_id);
44
+ if ($source_file === false) // if it's not an image, returns false, use the old way.
45
+ $source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
46
+ }
47
+ else
48
+ $source_file = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
49
+
50
+ Log::addDebug('SourceFile ' . $source_file);
51
$this->sourceFile = new File($source_file);
52
53
$this->source_post = get_post($post_id);
54
$this->source_is_image = wp_attachment_is('image', $this->source_post);
55
$this->source_metadata = wp_get_attachment_metadata( $post_id );
56
57
+ if (function_exists('wp_get_original_image_url')) // WP 5.3+
58
+ $this->source_url = wp_get_original_image_url($post_id);
59
+ else
60
+ $this->source_url = wp_get_attachment_url($post_id);
61
+ // $this->ThumbnailUpdater = new \ThumbnailUpdater($post_id);
62
+ //$this->ThumbnailUpdater->setOldMetadata($this->source_metadata);
63
}
64
65
public function setMode($mode)
145
146
if ($this->replaceMode == self::MODE_SEARCHREPLACE)
147
{
148
// Write new image title.
149
$title = $this->getNewTitle();
150
$update_ar = array('ID' => $this->post_id);
166
echo $error;
167
}
168
}
169
170
} // SEARCH REPLACE MODE
171
172
+ $args = array(
173
+ 'thumbnails_only' => ($this->replaceMode == self::MODE_SEARCHREPLACE) ? false : true,
174
+ );
175
+
176
+ // Search Replace will also update thumbnails.
177
+ $this->doSearchReplace($args);
178
+
179
+ /*if(wp_attachment_is_image($this->post_id))
180
{
181
$this->ThumbnailUpdater->setNewMetadata($this->target_metadata);
182
$result = $this->ThumbnailUpdater->updateThumbnails();
183
if (false === $result)
184
Log::addWarn('Thumbnail Updater returned false');
185
+ }*/
186
187
// if all set and done, update the date.
188
// This must be done after wp_update_posts
224
return $title;
225
}
226
227
+ /** Gets the source file after processing. Returns a file */
228
+ public function getSourceFile()
229
+ {
230
+ return $this->sourceFile;
231
+ }
232
+
233
/** Returns a full target path to place to new file. Including the file name! **/
234
protected function getTargetFile()
235
{
273
{
274
$meta = \wp_get_attachment_metadata( $this->post_id );
275
$backup_sizes = get_post_meta( $this->post_id, '_wp_attachment_backup_sizes', true );
276
+
277
+ // 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.
278
+ $file = get_attached_file($this->post_id);
279
+ $result = \wp_delete_attachment_files($this->post_id, $meta, $backup_sizes, $file );
280
281
}
282
304
305
wp_cache_delete($this->post_id, 'posts');
306
307
}
308
309
310
+ protected function doSearchReplace($args = array())
311
{
312
+ $defaults = array(
313
+ 'thumbnails_only' => false,
314
+ );
315
+
316
+ $args = wp_parse_args($args, $defaults);
317
+
318
+ global $wpdb;
319
320
// Search-and-replace filename in post database
321
+ // @todo Check this with scaled images.
322
+ $current_base_url = parse_url($this->source_url, PHP_URL_PATH);// emr_get_match_url( $this->source_url);
323
+ $current_base_url = str_replace('.' . pathinfo($current_base_url, PATHINFO_EXTENSION), '', $current_base_url);
324
325
/** Fail-safe if base_url is a whole directory, don't go search/replace */
326
if (is_dir($current_base_url))
327
{
328
Log::addError('Search Replace tried to replace to directory - ' . $current_base_url);
329
+ Notices::addError(__('Fail Safe :: Source Location seems to be a directory.', 'enable-media-replace'));
330
+ return;
331
+ }
332
+
333
+ //$search_files = $this->getFilesFromMetadata($this->source_metadata);
334
+ //$replace_files = $this->getFilesFromMetadata($this->target_metadata);
335
+ // $arr = $this->getRelativeURLS();
336
+
337
+ /*$search_urls = emr_get_file_urls( $this->source_url, $this->source_metadata );
338
+ $replace_urls = emr_get_file_urls( $this->target_url, $this->target_metadata );
339
+ $replace_urls = array_values(emr_normalize_file_urls( $search_urls, $replace_urls ));*/
340
+
341
+ // get relurls of both source and target.
342
+ $urls = $this->getRelativeURLS();
343
+
344
+
345
+ if ($args['thumbnails_only'])
346
+ {
347
+ foreach($urls as $side => $data)
348
+ {
349
+ if (isset($data['base']))
350
+ {
351
+ unset($urls[$side]['base']);
352
+ }
353
+ if (isset($data['file']))
354
+ {
355
+ unset($urls[$side]['file']);
356
+ }
357
+ }
358
+ }
359
+
360
+ $search_urls = $urls['source'];
361
+ $replace_urls = $urls['target'];
362
+
363
+ /* If the replacement is much larger than the source, there can be more thumbnails. This leads to disbalance in the search/replace arrays.
364
+ Remove those from the equation. If the size doesn't exist in the source, it shouldn't be in use either */
365
+ foreach($replace_urls as $size => $url)
366
+ {
367
+ if (! isset($search_urls[$size]))
368
+ {
369
+ Log::addDebug('Dropping size ' . $size . ' - not found in source urls');
370
+ unset($replace_urls[$size]);
371
+ }
372
+ }
373
+
374
+ /* If on the other hand, some sizes are available in source, but not in target, try to replace them with something closeby. */
375
+ foreach($search_urls as $size => $url)
376
+ {
377
+ if (! isset($replace_urls[$size]))
378
+ {
379
+ $closest = $this->findNearestSize($size);
380
+ if ($closest)
381
+ {
382
+ $sourceUrl = $search_urls[$size];
383
+ $baseurl = trailingslashit(str_replace(wp_basename($sourceUrl), '', $sourceUrl));
384
+ Log::addDebug('Nearest size of source ' . $size . ' for target is ' . $closest);
385
+ $replace_urls[$size] = $baseurl . $closest;
386
+ }
387
+ else
388
+ {
389
+ Log::addDebug('Unset size ' . $size . ' - no closest found in source');
390
+ }
391
+ }
392
+ }
393
+
394
+ /* If source and target are the same, remove them from replace. This happens when replacing a file with same name, and +/- same dimensions generated.
395
+
396
+ After previous loops, for every search there should be a replace size.
397
+ */
398
+ foreach($search_urls as $size => $url)
399
+ {
400
+ $replace_url = $replace_urls[$size];
401
+ if ($url == $replace_url) // if source and target as the same, no need for replacing.
402
+ {
403
+ unset($search_urls[$size]);
404
+ unset($replace_urls[$size]);
405
+ }
406
}
407
408
+ // 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.
409
+ if (count($search_urls) !== count($replace_urls))
410
+ {
411
+
412
+ Log::addError('Unbalanced Replace Arrays, aborting', array($search_urls, $replace_urls, count($search_urls), count($replace_urls) ));
413
+ Notices::addError(__('There was an issue with updating your image URLS: Search and replace have different amount of values. Aborting updating thumbnails', 'enable-media-replace'));
414
+ return;
415
+ }
416
+
417
+ Log::addDebug('Doing meta search and replace -', array($search_urls, $replace_urls) );
418
+ Log::addDebug('Searching with BaseuRL' . $current_base_url);
419
+
420
/* Search and replace in WP_POSTS */
421
// Removed $wpdb->remove_placeholder_escape from here, not compatible with WP 4.8
422
$posts_sql = $wpdb->prepare(
423
"SELECT ID, post_content FROM $wpdb->posts WHERE post_status = 'publish' AND post_content LIKE %s;",
424
'%' . $current_base_url . '%');
425
426
+ // json encodes it all differently. Catch json-like encoded urls
427
+ //$json_url = str_replace('/', '\/', ltrim($current_base_url, '/') );
428
429
+ $postmeta_sql = 'SELECT meta_id, post_id, meta_key, meta_value FROM ' . $wpdb->postmeta . '
430
+ WHERE post_id in (SELECT ID from '. $wpdb->posts . ' where post_status = "publish") AND meta_value like %s';
431
$postmeta_sql = $wpdb->prepare($postmeta_sql, '%' . $current_base_url . '%');
432
433
+ // 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.
434
+ // $postmeta_sql = str_replace('[JSON_URL]', $json_url, $postmeta_sql);
435
+
436
$rsmeta = $wpdb->get_results($postmeta_sql, ARRAY_A);
437
+
438
$rs = $wpdb->get_results( $posts_sql, ARRAY_A );
439
440
$number_of_updates = 0;
441
442
+ Log::addDebug('Queries', array($postmeta_sql, $posts_sql));
443
+ Log::addDebug('Queries found ' . count($rs) . ' post rows and ' . count($rsmeta) . ' meta rows');
444
445
446
if ( ! empty( $rs ) ) {
447
foreach ( $rs AS $rows ) {
448
$number_of_updates = $number_of_updates + 1;
449
// replace old URLs with new URLs.
450
$post_content = $rows["post_content"];
451
+ //$post_content = str_replace( $search_urls, $replace_urls, $post_content );
452
+
453
+ $post_id = $rows['ID'];
454
+ $post_ar = array('ID' => $post_id);
455
+ $post_ar['post_content'] = $this->replaceContent($post_content, $search_urls, $replace_urls);
456
+
457
+ if ($post_ar['post_content'] !== $post_content)
458
+ {
459
+ $result = wp_update_post($post_ar);
460
+ if (is_wp_error($result))
461
+ {
462
+ Notice::addError('Something went wrong while replacing' . $result->get_error_message() );
463
+ Log::addError('WP-Error during post update', $result);
464
+ }
465
+ }
466
+
467
}
468
}
469
if (! empty($rsmeta))
472
{
473
$number_of_updates++;
474
$content = $row['meta_value'];
475
+ $meta_key = $row['meta_key'];
476
+ $post_id = $row['post_id'];
477
+ $content = $this->replaceContent($content, $search_urls, $replace_urls); //str_replace($search_urls, $replace_urls, $content);
478
479
+ update_post_meta($post_id, $meta_key, $content);
480
+ // $sql = $wpdb->prepare('UPDATE ' . $wpdb->postmeta . ' SET meta_value = %s WHERE meta_id = %d', $content, $row['meta_id'] );
481
+ // $wpdb->query($sql);
482
}
483
}
484
485
486
} // doSearchReplace
487
488
+ private function replaceContent($content, $search, $replace)
489
+ {
490
+ //$is_serial = false;
491
+ $content = maybe_unserialize($content);
492
+ $isJson = $this->isJSON($content);
493
+
494
+ if ($isJson)
495
+ {
496
+ $content = json_decode($content);
497
+ }
498
+
499
+ if (is_string($content)) // let's check the normal one first.
500
+ {
501
+ return str_replace($search, $replace, $content);
502
+ }
503
+ elseif (is_wp_error($content)) // seen this.
504
+ {
505
+ return $content; // do nothing.
506
+ }
507
+ elseif (is_array($content) ) // array metadata and such.
508
+ {
509
+ foreach($content as $index => $value)
510
+ {
511
+ $content[$index] = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace);
512
+ }
513
+ return $content;
514
+ }
515
+ elseif(is_object($content)) // metadata objects, they exist.
516
+ {
517
+ foreach($content as $key => $value)
518
+ {
519
+ $content->{$key} = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace);
520
+ }
521
+ return $content;
522
+ }
523
+
524
+ if ($isJson) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically.
525
+ {
526
+ $content = json_encode($content);
527
+ }
528
+
529
+ return $content;
530
+ }
531
+
532
+ private function getFilesFromMetadata($meta)
533
+ {
534
+ $fileArray = array();
535
+ if (isset($meta['file']))
536
+ $fileArray['file'] = $meta['file'];
537
+
538
+ if (isset($meta['sizes']))
539
+ {
540
+ foreach($meta['sizes'] as $name => $data)
541
+ {
542
+ if (isset($data['file']))
543
+ {
544
+ $fileArray[$name] = $data['file'];
545
+ }
546
+ }
547
+ }
548
+ return $fileArray;
549
+ }
550
+
551
+ /* Check if given content is JSON format. */
552
+ private function isJSON($content)
553
+ {
554
+ $json = is_string($content) && json_decode($content);
555
+ return $json && $content != $json;
556
+ }
557
+
558
+ // Get REL Urls of both source and target.
559
+ private function getRelativeURLS()
560
+ {
561
+ $dataArray = array(
562
+ 'source' => array('url' => $this->source_url, 'metadata' => $this->getFilesFromMetadata($this->source_metadata) ),
563
+ 'target' => array('url' => $this->target_url, 'metadata' => $this->getFilesFromMetadata($this->target_metadata) ),
564
+ );
565
+
566
+ // Log::addDebug('Source Metadata', $this->source_metadata);
567
+ // Log::addDebug('Target Metadata', $this->target_metadata);
568
+
569
+ $result = array();
570
+
571
+ foreach($dataArray as $index => $item)
572
+ {
573
+ $result[$index] = array();
574
+ $metadata = $item['metadata'];
575
+
576
+ $baseurl = parse_url($item['url'], PHP_URL_PATH);
577
+ $result[$index]['base'] = $baseurl; // this is the relpath of the mainfile.
578
+ $baseurl = trailingslashit(str_replace( wp_basename($item['url']), '', $baseurl)); // get the relpath of main file.
579
+
580
+ foreach($metadata as $name => $filename)
581
+ {
582
+ $result[$index][$name] = $baseurl . wp_basename($filename); // filename can have a path like 19/08 etc.
583
+ }
584
+
585
+ }
586
+ // Log::addDebug('Relative URLS', $result);
587
+ return $result;
588
+ }
589
+
590
+
591
+ /** FindNearestsize
592
+ * 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.
593
+ */
594
+ private function findNearestSize($sizeName)
595
+ {
596
+
597
+ $old_width = $this->source_metadata['sizes'][$sizeName]['width']; // the width from size not in new image
598
+ $new_width = $this->target_metadata['width']; // default check - the width of the main image
599
+
600
+ $diff = abs($old_width - $new_width);
601
+ // $closest_file = str_replace($this->relPath, '', $this->newMeta['file']);
602
+ $closest_file = wp_basename($this->target_metadata['file']); // mainfile as default
603
+
604
+ foreach($this->target_metadata['sizes'] as $sizeName => $data)
605
+ {
606
+ $thisdiff = abs($old_width - $data['width']);
607
+
608
+ if ( $thisdiff < $diff )
609
+ {
610
+ $closest_file = $data['file'];
611
+ if(is_array($closest_file)) { $closest_file = $closest_file[0];} // HelpScout case 709692915
612
+ if(!empty($closest_file)) {
613
+ $diff = $thisdiff;
614
+ $found_metasize = true;
615
+ }
616
+ }
617
+ }
618
+
619
+
620
+ if(empty($closest_file)) return false;
621
+
622
+ return $closest_file;
623
+ //$oldFile = $oldData['file'];
624
+ //if(is_array($oldFile)) { $oldFile = $oldFile[0];} // HelpScout case 709692915
625
+ /*if(empty($oldFile)) {
626
+ return false; //make sure we don't replace in this case as we will break the URLs for all the images in the folder.
627
+ } */
628
+ // $this->convertArray[] = array('imageFrom' => $this->relPath . $oldFile, 'imageTo' => $this->relPath . $closest_file);
629
+
630
+ }
631
+
632
} // class
classes/uihelper.php CHANGED
@@ -64,7 +64,7 @@ class UIHelper
64
65
$url = add_query_arg('SHORTPIXEL_DEBUG', $spdebug, $url);
66
}
67
-
68
$url = apply_filters('emr_returnurl', $url);
69
Log::addDebug('Success URL- ' . $url);
70
@@ -97,12 +97,16 @@ class UIHelper
97
98
public function setSourceSizes($attach_id)
99
{
100
- $data = wp_get_attachment_image_src($attach_id, 'full');
101
if (is_array($data))
102
{
103
$this->full_width = $data[1];
104
$this->full_height = $data[2];
105
}
106
}
107
108
// Returns Preview Image HTML Output.
@@ -112,10 +116,11 @@ class UIHelper
112
113
if ($attach_id > 0)
114
{
115
- $data = wp_get_attachment_image_src($attach_id, $this->preview_size);
116
$file = get_attached_file($attach_id);
117
Log::addDebug('Attached File ' . $file, $data);
118
119
}
120
121
$mime_type = get_post_mime_type($attach_id);
@@ -146,10 +151,14 @@ class UIHelper
146
$url = $data[0];
147
$width = $data[1];
148
$height = $data[2];
149
// preview width, if source if found, should be set to source.
150
$this->preview_width = $width;
151
$this->preview_height = $height;
152
153
if ($width > $this->preview_max_width)
154
$width = $this->preview_max_width;
155
if ($height > $this->preview_max_height)
@@ -163,10 +172,43 @@ class UIHelper
163
'image' => $image,
164
'mime_type' => $mime_type,
165
);
166
$output = $this->getPlaceHolder($args);
167
return $output;
168
}
169
170
public function getPreviewError($attach_id)
171
{
172
$args = array(
64
65
$url = add_query_arg('SHORTPIXEL_DEBUG', $spdebug, $url);
66
}
67
+
68
$url = apply_filters('emr_returnurl', $url);
69
Log::addDebug('Success URL- ' . $url);
70
97
98
public function setSourceSizes($attach_id)
99
{
100
+ $data = $this->getImageSizes($attach_id, 'full'); // wp_get_attachment_image_src($attach_id, 'full');
101
+ $file = get_attached_file($attach_id);
102
+
103
if (is_array($data))
104
{
105
+
106
$this->full_width = $data[1];
107
$this->full_height = $data[2];
108
}
109
+
110
}
111
112
// Returns Preview Image HTML Output.
116
117
if ($attach_id > 0)
118
{
119
+ $data = $this->getImageSizes($attach_id, $this->preview_size); //wp_get_attachment_image_src($attach_id, $this->preview_size);
120
$file = get_attached_file($attach_id);
121
Log::addDebug('Attached File ' . $file, $data);
122
123
+
124
}
125
126
$mime_type = get_post_mime_type($attach_id);
151
$url = $data[0];
152
$width = $data[1];
153
$height = $data[2];
154
+
155
+ // SVG's without any helpers return around 0 for width / height. Fix preview.
156
+
157
// preview width, if source if found, should be set to source.
158
$this->preview_width = $width;
159
$this->preview_height = $height;
160
161
+
162
if ($width > $this->preview_max_width)
163
$width = $this->preview_max_width;
164
if ($height > $this->preview_max_height)
172
'image' => $image,
173
'mime_type' => $mime_type,
174
);
175
+
176
$output = $this->getPlaceHolder($args);
177
return $output;
178
}
179
180
+ protected function getImageSizes($attach_id, $size = 'thumbnail')
181
+ {
182
+ $data = wp_get_attachment_image_src($attach_id, $size);
183
+ $width = $data[1];
184
+ $mime_type = get_post_mime_type($attach_id);
185
+
186
+ if (strpos($mime_type, 'svg') !== false && $width <= 5)
187
+ {
188
+ $file = get_attached_file($attach_id);
189
+ $data = $this->fixSVGSize($data, $file);
190
+ }
191
+
192
+ return $data;
193
+ }
194
+
195
+ protected function fixSVGSize($data, $file)
196
+ {
197
+ if (! function_exists('simplexml_load_file'))
198
+ return $data;
199
+
200
+ $xml = simplexml_load_file($file);
201
+ if ($xml)
202
+ { // stolen from SVG Upload plugin
203
+ $attr = $xml->attributes();
204
+ $viewbox = explode(' ', $attr->viewBox);
205
+ $data[1] = isset($attr->width) && preg_match('/\d+/', $attr->width, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[2] : null);
206
+ $data[2] = isset($attr->height) && preg_match('/\d+/', $attr->height, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[3] : null);
207
+ }
208
+
209
+ return $data;
210
+ }
211
+
212
public function getPreviewError($attach_id)
213
{
214
$args = array(
css/edit_attachment.css CHANGED
@@ -1,12 +1,12 @@
1
/* Styling for the edit attachment screen */
2
- #emr-replace-box .previewwrapper {
3
display: inline-block;
4
position: relative;
5
clear: both;
6
margin: 3px 0; }
7
- #emr-replace-box .previewwrapper img {
8
max-width: 100%; }
9
- #emr-replace-box .previewwrapper span.label {
10
font-size: 14px;
11
color: #fff;
12
position: absolute;
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
+ #emr-replace-box .previewwrapper img, #emr-showthumbs-box .previewwrapper img {
8
max-width: 100%; }
9
+ #emr-replace-box .previewwrapper span.label, #emr-showthumbs-box .previewwrapper span.label {
10
font-size: 14px;
11
color: #fff;
12
position: absolute;
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.7
7
Author: ShortPixel
8
Author URI: https://shortpixel.com
9
Text Domain: enable-media-replace
@@ -25,14 +25,16 @@ http://www.gnu.org/licenses/gpl.html
25
*/
26
27
namespace EnableMediaReplace;
28
29
if ( ! defined( 'ABSPATH' ) ) {
30
exit; // Exit if accessed directly.
31
}
32
33
if(!defined("S3_UPLOADS_AUTOENABLE")) {
34
define('S3_UPLOADS_AUTOENABLE', true);
35
- }
36
37
if (! defined("EMR_ROOT_FILE")) {
38
define("EMR_ROOT_FILE", __FILE__);
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.8
7
Author: ShortPixel
8
Author URI: https://shortpixel.com
9
Text Domain: enable-media-replace
25
*/
26
27
namespace EnableMediaReplace;
28
+ define('EMR_VERSION', '3.3.8');
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__);
js/emr_admin.js CHANGED
@@ -1,7 +1,6 @@
1
- jQuery(document).ready(function($)
2
- {
3
// interface for emr.
4
- var emrIf = new function ()
5
{
6
var source_type;
7
var source_is_image;
@@ -119,8 +118,15 @@ jQuery(document).ready(function($)
119
120
img.setAttribute('style', 'max-width:100%; max-height: 100%;');
121
img.addEventListener("load", function () {
122
// $(preview).find('.textlayer').text(img.naturalWidth + ' x ' + img.naturalHeight );
123
- self.updateTextLayer(preview, img.naturalWidth + ' x ' + img.naturalHeight);
124
});
125
126
$(preview).prepend(img);
@@ -147,6 +153,14 @@ jQuery(document).ready(function($)
147
this.debug(target_type + ' not ' + source_type);
148
this.warningFileType();
149
}
150
},
151
// replace the text, check if text is there ( or hide ), and fix the layout.
152
this.updateTextLayer = function (preview, newtext)
@@ -209,12 +223,36 @@ jQuery(document).ready(function($)
209
{
210
$('.form-warning.filetype').fadeIn();
211
}
212
this.debug = function(message)
213
{
214
console.debug(message);
215
}
216
} // emrIf
217
218
- window.enableMediaReplace = emrIf;
219
window.enableMediaReplace.init();
220
});
1
+
2
// interface for emr.
3
+ var emrIf = function ($)
4
{
5
var source_type;
6
var source_is_image;
118
119
img.setAttribute('style', 'max-width:100%; max-height: 100%;');
120
img.addEventListener("load", function () {
121
+ // with formats like svg it can be rough.
122
+ var width = img.naturalWidth;
123
+ var height = img.naturalHeight;
124
+ if (width == 0)
125
+ width = img.width;
126
+ if (height == 0)
127
+ height = img.height;
128
// $(preview).find('.textlayer').text(img.naturalWidth + ' x ' + img.naturalHeight );
129
+ self.updateTextLayer(preview, width + ' x ' + height);
130
});
131
132
$(preview).prepend(img);
153
this.debug(target_type + ' not ' + source_type);
154
this.warningFileType();
155
}
156
+
157
+ if (emr_options.allowed_mime.indexOf(target_type) == -1)
158
+ {
159
+ this.debug(target_type + ' not ' + ' in allowed types ');
160
+ this.warningMimeType();
161
+ }
162
+ // this.debug(emr_options.allowed_mime);
163
+
164
},
165
// replace the text, check if text is there ( or hide ), and fix the layout.
166
this.updateTextLayer = function (preview, newtext)
223
{
224
$('.form-warning.filetype').fadeIn();
225
}
226
+ this.warningMimeType = function(fileItem)
227
+ {
228
+ $('.form-warning.mimetype').fadeIn();
229
+ }
230
this.debug = function(message)
231
{
232
console.debug(message);
233
}
234
} // emrIf
235
236
+ jQuery(document).ready(function($)
237
+ {
238
+ window.enableMediaReplace = new emrIf($);
239
window.enableMediaReplace.init();
240
});
241
+
242
+
243
+ function emrDelayedInit() {
244
+ console.log('Checking delayed init ');
245
+ if(typeof window.enableMediaReplace == "undefined") {
246
+ console.log(emrIf);
247
+ window.enableMediaReplace = new emrIf(jQuery);
248
+ window.enableMediaReplace.init();
249
+ }
250
+ else if (typeof window.enableMediaReplace !== 'undefined')
251
+ {
252
+ // All fine.
253
+ }
254
+ else { // Nothing yet, try again.
255
+ setTimeout(emrdelayedInit, 3000);
256
+ }
257
+ }
258
+ setTimeout(emrDelayedInit, 3000);
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
Contributors: ShortPixel
3
Donate link: https://www.paypal.me/resizeImage
4
Tags: replace, attachment, media, files, replace image, replace jpg, change media, replace media, image, file
5
- Requires at least: 4.2
6
Tested up to: 5.3
7
- Requires PHP: 5.4
8
Stable tag: trunk
9
10
Easily replace any attached image/file by simply uploading a new file in the Media Library edit view - a real time saver!
@@ -47,6 +47,22 @@ If you want more control over the format used to display the time, you can use t
47
48
== Changelog ==
49
50
= 3.3.7 =
51
52
Release date: 13th November 2019
2
Contributors: ShortPixel
3
Donate link: https://www.paypal.me/resizeImage
4
Tags: replace, attachment, media, files, replace image, replace jpg, change media, replace media, image, file
5
+ Requires at least: 4.9.7
6
Tested up to: 5.3
7
+ Requires PHP: 5.6
8
Stable tag: trunk
9
10
Easily replace any attached image/file by simply uploading a new file in the Media Library edit view - a real time saver!
47
48
== Changelog ==
49
50
+ = 3.3.8 =
51
+
52
+ Release date: 18th February 2020
53
+ * Fixes for WP 5.3+ -scaled images system. Will now replace those as well.
54
+ * Updating and replacing images and thumbnails should now work for serialized (and such) metadata.
55
+ - For Beaver Builder
56
+ * Add extra warning if mimetype is not allowed by WordPress for upload
57
+ * Put Javascript version in wp_register_script
58
+ * Hidden double 'replace media'
59
+ * Replace Image label to Replace Media
60
+ * Removed constant S3_UPLOADS_AUTOENABLE
61
+ * Improved detection of SVG image sizes
62
+ * Fixed - Logger doesn't call wp_upload_dir when not debugging
63
+ * Extra - Javascript tries to resume after external errors.
64
+ * Language – 0 new strings added, 2 updated, 0 fuzzied, and 0 obsoleted
65
+
66
= 3.3.7 =
67
68
Release date: 13th November 2019
scss/edit_attachment.scss CHANGED
@@ -1,6 +1,6 @@
1
2
/* Styling for the edit attachment screen */
3
- #emr-replace-box
4
{
5
.previewwrapper
6
{
@@ -23,6 +23,7 @@
23
text-align: center;
24
padding: 4px 0;
25
26
}
27
28
}
1
2
/* Styling for the edit attachment screen */
3
+ #emr-replace-box, #emr-showthumbs-box
4
{
5
.previewwrapper
6
{
23
text-align: center;
24
padding: 4px 0;
25
26
+
27
}
28
29
}
thumbnail_updater.php CHANGED
@@ -6,9 +6,6 @@ use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
6
use EnableMediaReplace\Notices\NoticeController as Notices;
7
8
/* Simple class for updating thumbnails.
9
- *
10
- *
11
- *
12
*/
13
class ThumbnailUpdater
14
{
6
use EnableMediaReplace\Notices\NoticeController as Notices;
7
8
/* Simple class for updating thumbnails.
9
*/
10
class ThumbnailUpdater
11
{
views/popup.php CHANGED
@@ -33,17 +33,24 @@ Log::addDebug('Load Popup Form View');
33
34
$attachment_id = intval($_GET['attachment_id']);
35
$attachment = get_post($attachment_id);
36
37
- $filepath = get_attached_file($attachment_id); // fullpath
38
$filetype = $attachment->post_mime_type;
39
$filename = basename($filepath);
40
- $source_mime = get_post_mime_type($attachment_id);
41
42
$uiHelper = new UIHelper();
43
$uiHelper->setPreviewSizes();
44
$uiHelper->setSourceSizes($attachment_id);
45
46
- Log::addDebug('Popup view Data', array('id' => $attachment_id, 'source_mime' => $source_mime, 'filepath' => $filepath));
47
48
?>
49
<style>
@@ -71,7 +78,7 @@ $url = $uiHelper->getFormUrl($attachment_id);
71
72
<form enctype="multipart/form-data" method="POST" action="<?php echo $formurl; ?>">
73
<section class='image_chooser wrapper'>
74
- <div class='section-header'> <?php _e('Choose Replacement Image', 'enable-replace-media'); ?></div>
75
76
<?php
77
#wp_nonce_field('enable-media-replace');
@@ -113,6 +120,7 @@ $url = $uiHelper->getFormUrl($attachment_id);
113
114
<div class='form-warning filetype'><p><?php printf(__('Replacement file is not the same filetype. This might cause unexpected issues')); ?></p></div>
115
116
117
<input type="file" name="userfile" id="userfile" />
118
<div class='image_previews'>
33
34
$attachment_id = intval($_GET['attachment_id']);
35
$attachment = get_post($attachment_id);
36
+ $replacer = new Replacer($attachment_id);
37
38
+ $file = $replacer->getSourceFile();
39
+ $filepath = $file->getFullFilePath();
40
+ $filename = $file->getFileName();
41
+ $filetype = $file->getFileExtension();
42
+ $source_mime = get_post_mime_type($attachment_id);
43
+
44
+ /*$filepath = get_attached_file($attachment_id); // fullpath
45
$filetype = $attachment->post_mime_type;
46
$filename = basename($filepath);
47
+ */
48
49
$uiHelper = new UIHelper();
50
$uiHelper->setPreviewSizes();
51
$uiHelper->setSourceSizes($attachment_id);
52
53
+ //Log::addDebug('Popup view Data', array('id' => $attachment_id, 'source_mime' => $source_mime, 'filepath' => $filepath));
54
55
?>
56
<style>
78
79
<form enctype="multipart/form-data" method="POST" action="<?php echo $formurl; ?>">
80
<section class='image_chooser wrapper'>
81
+ <div class='section-header'> <?php _e('Choose Replacement Media', 'enable-replace-media'); ?></div>
82
83
<?php
84
#wp_nonce_field('enable-media-replace');
120
121
<div class='form-warning filetype'><p><?php printf(__('Replacement file is not the same filetype. This might cause unexpected issues')); ?></p></div>
122
123
+ <div class='form-warning mimetype'><p><?php printf(__('Replacement file type doesn\'t seem to be allowed by WordPress. This might cause unexpected issues')); ?></p></div>
124
125
<input type="file" name="userfile" id="userfile" />
126
<div class='image_previews'>
views/upload.php CHANGED
@@ -21,26 +21,18 @@ global $wpdb;
21
$table_name = $wpdb->prefix . "posts";
22
$postmeta_table_name = $wpdb->prefix . "postmeta";
23
24
-
25
// Starts processing.
26
$uihelper = new UIHelper();
27
28
// Get old guid and filetype from DB
29
$post_id = intval($_POST['ID']); // sanitize, post_id.
30
- $replacer = new replacer($post_id);
31
32
// Massage a bunch of vars
33
$ID = intval($_POST["ID"]); // legacy
34
$replace_type = isset($_POST["replace_type"]) ? sanitize_text_field($_POST["replace_type"]) : false;
35
$timestamp_replace = intval($_POST['timestamp_replace']);
36
37
- $current_file = get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ));
38
- $current_path = substr($current_file, 0, (strrpos($current_file, "/")));
39
- $current_file = preg_replace("|(?<!:)/{2,}|", "/", $current_file); // @todo what does this mean?
40
- $current_filename = wp_basename($current_file);
41
- $current_metadata = wp_get_attachment_metadata( $post_id );
42
-
43
-
44
$redirect_error = $uihelper->getFailedRedirect($post_id);
45
$redirect_success = $uihelper->getSuccesRedirect($post_id);
46
@@ -106,7 +98,8 @@ if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) {
106
exit();
107
}
108
109
- if ($filedata["ext"] == "") {
110
111
Notices::addError(esc_html__("File type does not meet security guidelines. Try another.", 'enable-media-replace') );
112
wp_safe_redirect($redirect_error);
@@ -114,17 +107,10 @@ if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) {
114
}
115
116
// Here we have the uploaded file
117
-
118
- //$thumbUpdater = new ThumbnailUpdater($ID);
119
- //$thumbUpdater->setOldMetadata($current_metadata);
120
-
121
$new_filename = $_FILES["userfile"]["name"];
122
//$new_filesize = $_FILES["userfile"]["size"]; // Seems not to be in use.
123
$new_filetype = $filedata["type"];
124
125
- // save original file permissions
126
- //$original_file_perms = fileperms($current_file) & 0777;
127
-
128
// Gather all functions that both options do.
129
do_action('wp_handle_replace', array('post_id' => $post_id));
130
@@ -141,7 +127,6 @@ if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) {
141
$returnurl = admin_url("/post.php?post={$_POST["ID"]}&action=edit&message=1");
142
143
// Execute hook actions - thanks rubious for the suggestion!
144
- //if (isset($new_guid)) { do_action("enable-media-replace-upload-done", $new_guid, $current_guid); }
145
146
} else {
147
//TODO Better error handling when no file is selected.
21
$table_name = $wpdb->prefix . "posts";
22
$postmeta_table_name = $wpdb->prefix . "postmeta";
23
24
// Starts processing.
25
$uihelper = new UIHelper();
26
27
// Get old guid and filetype from DB
28
$post_id = intval($_POST['ID']); // sanitize, post_id.
29
+ $replacer = new Replacer($post_id);
30
31
// Massage a bunch of vars
32
$ID = intval($_POST["ID"]); // legacy
33
$replace_type = isset($_POST["replace_type"]) ? sanitize_text_field($_POST["replace_type"]) : false;
34
$timestamp_replace = intval($_POST['timestamp_replace']);
35
36
$redirect_error = $uihelper->getFailedRedirect($post_id);
37
$redirect_success = $uihelper->getSuccesRedirect($post_id);
38
98
exit();
99
}
100
101
+
102
+ if ($filedata["ext"] == false) {
103
104
Notices::addError(esc_html__("File type does not meet security guidelines. Try another.", 'enable-media-replace') );
105
wp_safe_redirect($redirect_error);
107
}
108
109
// Here we have the uploaded file
110
$new_filename = $_FILES["userfile"]["name"];
111
//$new_filesize = $_FILES["userfile"]["size"]; // Seems not to be in use.
112
$new_filetype = $filedata["type"];
113
114
// Gather all functions that both options do.
115
do_action('wp_handle_replace', array('post_id' => $post_id));
116
127
$returnurl = admin_url("/post.php?post={$_POST["ID"]}&action=edit&message=1");
128
129
// Execute hook actions - thanks rubious for the suggestion!
130
131
} else {
132
//TODO Better error handling when no file is selected.