Force Regenerate Thumbnails - Version 2.1.0

Version Description

  • added: PHP 8.0 compatibility
  • added: support generating thumbnails from original (pre-scaled), on by default
  • added: support for PDF thumbnail generation
  • added: ability to skip an image by regenerate_thumbs_skip_image filter
  • changed: escape all output, sanitize all input
  • changed: ensure all strings are i18n
  • changed: remove HTML from i18n strings
  • changed: improve path lookup function
  • fixed: call to set_time_limit() when it is not allowed
Download this release

Release Info

Developer nosilver4u
Plugin Icon 128x128 Force Regenerate Thumbnails
Version 2.1.0
Comparing to
See all releases

Code changes from version 2.0.6 to 2.1.0

assets/jquery-ui-1.10.1.custom.css ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! jQuery UI - v1.10.1 - 2013-03-14
2
+ * Copyright 2013 jQuery Foundation and other contributors Licensed MIT */
3
+ /* includes styling for progressbar and tooltip elements */
4
+ /* Layout helpers
5
+ ----------------------------------*/
6
+ .ui-helper-hidden {
7
+ display: none;
8
+ }
9
+ .ui-helper-hidden-accessible {
10
+ border: 0;
11
+ clip: rect(0 0 0 0);
12
+ height: 1px;
13
+ margin: -1px;
14
+ overflow: hidden;
15
+ padding: 0;
16
+ position: absolute;
17
+ width: 1px;
18
+ }
19
+ .ui-helper-reset {
20
+ margin: 0;
21
+ padding: 0;
22
+ border: 0;
23
+ outline: 0;
24
+ line-height: 1.3;
25
+ text-decoration: none;
26
+ font-size: 100%;
27
+ list-style: none;
28
+ }
29
+ .ui-helper-clearfix:before,
30
+ .ui-helper-clearfix:after {
31
+ content: "";
32
+ display: table;
33
+ border-collapse: collapse;
34
+ }
35
+ .ui-helper-clearfix:after {
36
+ clear: both;
37
+ }
38
+ .ui-helper-clearfix {
39
+ min-height: 0; /* support: IE7 */
40
+ }
41
+ .ui-helper-zfix {
42
+ width: 100%;
43
+ height: 100%;
44
+ top: 0;
45
+ left: 0;
46
+ position: absolute;
47
+ opacity: 0;
48
+ filter:Alpha(Opacity=0);
49
+ }
50
+ .ui-front {
51
+ z-index: 100;
52
+ }
53
+ /* Interaction Cues
54
+ ----------------------------------*/
55
+ .ui-state-disabled {
56
+ cursor: default !important;
57
+ }
58
+
59
+ /* Misc visuals
60
+ ----------------------------------*/
61
+ /* Corner radius */
62
+ .ui-corner-all,
63
+ .ui-corner-top,
64
+ .ui-corner-left,
65
+ .ui-corner-tl {
66
+ border-top-left-radius: 22px;
67
+ }
68
+ .ui-corner-all,
69
+ .ui-corner-top,
70
+ .ui-corner-right,
71
+ .ui-corner-tr {
72
+ border-top-right-radius: 22px;
73
+ }
74
+ .ui-corner-all,
75
+ .ui-corner-bottom,
76
+ .ui-corner-left,
77
+ .ui-corner-bl {
78
+ border-bottom-left-radius: 22px;
79
+ }
80
+ .ui-corner-all,
81
+ .ui-corner-bottom,
82
+ .ui-corner-right,
83
+ .ui-corner-br {
84
+ border-bottom-right-radius: 22px;
85
+ }
86
+ /* Overlays */
87
+ .ui-widget-overlay {
88
+ position: fixed;
89
+ top: 0;
90
+ left: 0;
91
+ width: 100%;
92
+ height: 100%;
93
+ }
94
+ .ui-progressbar {
95
+ height: 22px;
96
+ text-align: left;
97
+ overflow: hidden;
98
+ }
99
+ .ui-progressbar .ui-progressbar-value {
100
+ margin: 0px;
101
+ height: 100%;
102
+
103
+ }
104
+ .ui-accordion .ui-accordion-header {
105
+ display: block;
106
+ cursor: pointer;
107
+ position: relative;
108
+ margin-top: 2px;
109
+ padding: .5em .5em .5em .7em;
110
+ min-height: 0; /* support: IE7 */
111
+ }
112
+ .ui-accordion .ui-accordion-icons {
113
+ padding-left: 2.2em;
114
+ }
115
+ .ui-accordion .ui-accordion-noicons {
116
+ padding-left: .7em;
117
+ }
118
+ .ui-accordion .ui-accordion-icons .ui-accordion-icons {
119
+ padding-left: 2.2em;
120
+ }
121
+ .ui-accordion .ui-accordion-header .ui-accordion-header-icon {
122
+ position: absolute;
123
+ left: .5em;
124
+ top: 50%;
125
+ margin-top: -8px;
126
+ }
127
+ .ui-accordion .ui-accordion-content {
128
+ padding: 1em 2.2em;
129
+ border-top: 0;
130
+ overflow: auto;
131
+ border-top-left-radius: 0px;
132
+ border-top-right-radius: 0px;
133
+ }
134
+ .ui-slider {
135
+ position: relative;
136
+ text-align: left;
137
+ }
138
+ .ui-slider .ui-slider-handle {
139
+ position: absolute;
140
+ z-index: 2;
141
+ width: 1.2em;
142
+ height: 1.2em;
143
+ cursor: default;
144
+ }
145
+ .ui-slider .ui-slider-range {
146
+ position: absolute;
147
+ z-index: 1;
148
+ font-size: .7em;
149
+ display: block;
150
+ border: 0;
151
+ background-position: 0 0;
152
+ }
153
+
154
+ /* For IE8 - See #6727 */
155
+ .ui-slider.ui-state-disabled .ui-slider-handle,
156
+ .ui-slider.ui-state-disabled .ui-slider-range {
157
+ filter: inherit;
158
+ }
159
+
160
+ .ui-slider-horizontal {
161
+ height: .8em;
162
+ }
163
+ .ui-slider-horizontal .ui-slider-handle {
164
+ top: -.3em;
165
+ margin-left: -.6em;
166
+ }
167
+ .ui-slider-horizontal .ui-slider-range {
168
+ top: 0;
169
+ height: 100%;
170
+ }
171
+ .ui-slider-horizontal .ui-slider-range-min {
172
+ left: 0;
173
+ }
174
+ .ui-slider-horizontal .ui-slider-range-max {
175
+ right: 0;
176
+ }
177
+
178
+ .ui-slider-vertical {
179
+ width: .8em;
180
+ height: 100px;
181
+ }
182
+ .ui-slider-vertical .ui-slider-handle {
183
+ left: -.3em;
184
+ margin-left: 0;
185
+ margin-bottom: -.6em;
186
+ }
187
+ .ui-slider-vertical .ui-slider-range {
188
+ left: 0;
189
+ width: 100%;
190
+ }
191
+ .ui-slider-vertical .ui-slider-range-min {
192
+ bottom: 0;
193
+ }
194
+ .ui-slider-vertical .ui-slider-range-max {
195
+ top: 0;
196
+ }
197
+ /* Component containers
198
+ ----------------------------------*/
199
+ .ui-widget-content {
200
+ background: none repeat scroll 0% 0% #dddddd;
201
+ -webkit-border-radius: 22px;
202
+ border-radius: 22px;
203
+ -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
204
+ box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
205
+ }
206
+ .ui-widget-content a {
207
+ color: #222222;
208
+ }
209
+ .ui-widget-header {
210
+ z-index: 9;
211
+ /* background-color: #00a32a;
212
+ background-color: #04a4cc;
213
+ background-color: #67c93e; */
214
+ background-color: #0073aa;
215
+ -webkit-border-radius: 22px;
216
+ border-radius: 22px;
217
+ }
218
+ .ui-widget-header a {
219
+ color: #ffffff;
220
+ }
221
+ /* Interaction states
222
+ ----------------------------------*/
223
+ .ui-state-default,
224
+ .ui-widget-content .ui-state-default,
225
+ .ui-widget-header .ui-state-default {
226
+ border: 1px solid #d3d3d3;
227
+ background: #e6e6e6 50% 50% repeat-x;
228
+ font-weight: normal;
229
+ color: #555555;
230
+ }
231
+ .ui-state-default a,
232
+ .ui-state-default a:link,
233
+ .ui-state-default a:visited {
234
+ color: #555555;
235
+ text-decoration: none;
236
+ }
237
+ .ui-state-hover,
238
+ .ui-widget-content .ui-state-hover,
239
+ .ui-widget-header .ui-state-hover,
240
+ .ui-state-focus,
241
+ .ui-widget-content .ui-state-focus,
242
+ .ui-widget-header .ui-state-focus {
243
+ border: 1px solid #999999;
244
+ background: #dadada 50% 50% repeat-x;
245
+ font-weight: normal;
246
+ color: #212121;
247
+ }
248
+ .ui-state-hover a,
249
+ .ui-state-hover a:hover,
250
+ .ui-state-hover a:link,
251
+ .ui-state-hover a:visited {
252
+ color: #212121;
253
+ text-decoration: none;
254
+ }
255
+ .ui-state-active,
256
+ .ui-widget-content .ui-state-active,
257
+ .ui-widget-header .ui-state-active {
258
+ border: 1px solid #aaaaaa;
259
+ background: #ffffff 50% 50% repeat-x;
260
+ font-weight: normal;
261
+ color: #212121;
262
+ }
263
+ .ui-state-active a,
264
+ .ui-state-active a:link,
265
+ .ui-state-active a:visited {
266
+ color: #212121;
267
+ text-decoration: none;
268
+ }
269
+
270
+ /* Interaction Cues
271
+ ----------------------------------*/
272
+ .ui-state-highlight,
273
+ .ui-widget-content .ui-state-highlight,
274
+ .ui-widget-header .ui-state-highlight {
275
+ border: 1px solid #fcefa1;
276
+ color: #363636;
277
+ }
278
+ .ui-state-highlight a,
279
+ .ui-widget-content .ui-state-highlight a,
280
+ .ui-widget-header .ui-state-highlight a {
281
+ color: #363636;
282
+ }
283
+ .ui-state-error,
284
+ .ui-widget-content .ui-state-error,
285
+ .ui-widget-header .ui-state-error {
286
+ border: 1px solid #cd0a0a;
287
+ color: #cd0a0a;
288
+ }
289
+ .ui-state-error a,
290
+ .ui-widget-content .ui-state-error a,
291
+ .ui-widget-header .ui-state-error a {
292
+ color: #cd0a0a;
293
+ }
294
+ .ui-state-error-text,
295
+ .ui-widget-content .ui-state-error-text,
296
+ .ui-widget-header .ui-state-error-text {
297
+ color: #cd0a0a;
298
+ }
299
+ .ui-priority-primary,
300
+ .ui-widget-content .ui-priority-primary,
301
+ .ui-widget-header .ui-priority-primary {
302
+ font-weight: bold;
303
+ }
304
+ .ui-priority-secondary,
305
+ .ui-widget-content .ui-priority-secondary,
306
+ .ui-widget-header .ui-priority-secondary {
307
+ opacity: .7;
308
+ filter:Alpha(Opacity=70);
309
+ font-weight: normal;
310
+ }
311
+ .ui-state-disabled,
312
+ .ui-widget-content .ui-state-disabled,
313
+ .ui-widget-header .ui-state-disabled {
314
+ opacity: .35;
315
+ filter:Alpha(Opacity=35);
316
+ background-image: none;
317
+ }
318
+ .ui-state-disabled .ui-icon {
319
+ filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
320
+ }
321
+
322
+ #regenthumbs-debuglist {
323
+ margin-left: 0;
324
+ padding: 1em 1.2em;
325
+ list-style-position: inside;
326
+ background: #fff;
327
+ -webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
328
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
329
+ }
assets/regen.js ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($){
2
+ var i;
3
+ var rt_images = regen_thumbs.image_ids;
4
+ var rt_total = rt_images.length;
5
+ var rt_count = 1;
6
+ var rt_successes = 0;
7
+ var rt_errors = 0;
8
+ var rt_failedlist = '';
9
+ var rt_resulttext = '';
10
+ var rt_timestart = new Date().getTime();
11
+ var rt_timeend = 0;
12
+ var rt_totaltime = 0;
13
+ var rt_continue = true;
14
+
15
+ // Create the progress bar
16
+ $("#regenthumbs-bar").progressbar();
17
+
18
+ // Stop button
19
+ $("#regenthumbs-stop").click(function() {
20
+ rt_continue = false;
21
+ $('#regenthumbs-stop').val(regen_thumbs.stopping);
22
+ });
23
+
24
+ // Clear out the empty list element that's there for HTML validation purposes
25
+ $("#regenthumbs-debuglist li").remove();
26
+ $("#regenthumbs-debuglist").hide();
27
+
28
+ // Called after each resize. Updates debug information and the progress bar.
29
+ function RegenThumbsUpdateStatus(id, success, response) {
30
+ $("#regenthumbs-bar").progressbar("value", (rt_count / rt_total) * 100);
31
+ rt_count = rt_count + 1;
32
+
33
+ if (success) {
34
+ rt_successes = rt_successes + 1;
35
+ $("#regenthumbs-debug-successcount").html(rt_successes);
36
+ $("#regenthumbs-debuglist").append("<li>" + response.success + "</li>");
37
+ }
38
+ else {
39
+ rt_errors = rt_errors + 1;
40
+ rt_failedlist = rt_failedlist + ',' + id;
41
+ $("#regenthumbs-debug-failurecount").html(rt_errors);
42
+ $("#regenthumbs-debuglist").append("<li>" + response.error + "</li>");
43
+ }
44
+ $("#regenthumbs-debuglist").show();
45
+ }
46
+
47
+ // Called when all images have been processed. Shows the results and cleans up.
48
+ function RegenThumbsFinishUp() {
49
+ rt_timeend = new Date().getTime();
50
+ rt_totaltime = Math.round((rt_timeend - rt_timestart) / 1000);
51
+
52
+ $('#regenthumbs-stop').hide();
53
+
54
+ if (rt_errors > 0 && rt_failedlist ) {
55
+ $("#frt-retry-images").prop('href', $("#frt-retry-images").prop('href') + '&ids=' + rt_failedlist );
56
+ $("#frt-retry-container").show();
57
+ }
58
+
59
+ $("#frt-message").show();
60
+ }
61
+
62
+ // Regenerate a specified image via AJAX
63
+ function RegenThumbs(id) {
64
+ $.ajax({
65
+ type: 'POST',
66
+ cache: false,
67
+ url: ajaxurl,
68
+ data: { action: "regeneratethumbnail", id: id, frt_wpnonce: regen_thumbs._wpnonce },
69
+ success: function(response) {
70
+
71
+ //Catch unknown error
72
+ if(response === null) {
73
+ response = {};
74
+ response.success = false;
75
+ response.error = regen_thumbs.unknown_error;
76
+ }
77
+
78
+ if (response.success) {
79
+ RegenThumbsUpdateStatus(id, true, response);
80
+ } else {
81
+ RegenThumbsUpdateStatus(id, false, response);
82
+ }
83
+
84
+ if (rt_images.length && rt_continue) {
85
+ RegenThumbs(rt_images.shift());
86
+ } else {
87
+ RegenThumbsFinishUp();
88
+ }
89
+ },
90
+ error: function(request,response,error) {
91
+ var error_response = { error: response + ': ' + request.status + ' ' + error };
92
+ RegenThumbsUpdateStatus(id, false, error_response);
93
+
94
+ if (rt_images.length && rt_continue) {
95
+ RegenThumbs(rt_images.shift());
96
+ } else {
97
+ RegenThumbsFinishUp();
98
+ }
99
+ }
100
+ });
101
+ }
102
+
103
+ RegenThumbs(rt_images.shift());
104
+ });
changelog.txt ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ = 2.1.0 =
2
+ * added: PHP 8.0 compatibility
3
+ * added: support generating thumbnails from original (pre-scaled), on by default
4
+ * added: support for PDF thumbnail generation
5
+ * added: ability to skip an image by regenerate_thumbs_skip_image filter
6
+ * changed: escape all output, sanitize all input
7
+ * changed: ensure all strings are i18n
8
+ * changed: remove HTML from i18n strings
9
+ * changed: improve path lookup function
10
+ * fixed: call to set_time_limit() when it is not allowed
11
+
12
+ = 2.0.6 =
13
+ * Add PHP7 compatibility
14
+
15
+ = 2.0.5 =
16
+ * No timeout limit
17
+
18
+ = 2.0.4 =
19
+ * Fix issue when "opendir()" return FALSE (thanks Krody Robert)
20
+
21
+ = 2.0.3 =
22
+ * Add debug information on regenerate
23
+ * Fix issue with update "_wp_attachment_metadata" and "_wp_attached_file" on windows
24
+
25
+ = 2.0.2 =
26
+ * New style for results (thanks @justonarnar)
27
+ * Automatic update "_wp_attachment_metadata" and "_wp_attached_file" (thanks @norecipes)
28
+
29
+ = 2.0.1 =
30
+ * Fix issue with get_option('upload_path') in Wordpress 3.5+ (thanks @DavidLingren)
31
+
32
+ = 2.0.0 =
33
+ * Fix error handle
34
+
35
+ = 1.8 =
36
+ * New function to display ajax results
37
+
38
+ = 1.7 =
39
+ * Fix issue with getters path in Linux/Windows/Unix servers
40
+
41
+ = 1.6 =
42
+ * New CORE to regenerate thumbnails
43
+
44
+ = 1.5 =
45
+ * Reviewed some messages
46
+
47
+ = 1.4 =
48
+ * Change default image editor to GD in Wordpress 3.5+ (thanks @nikcree)
49
+
50
+ = 1.3 =
51
+ * Fix message error when WP_DEBUG in wp_config.php
52
+
53
+ = 1.2 =
54
+ * Fix for JPEG images
55
+
56
+ = 1.1 =
57
+ * Delete all custom image sizes when regenerate thumbnails
58
+ * Notifies you when thumbnails was deleted
59
+
60
+ = 1.0 =
61
+ * First release.
class-forceregeneratethumbnails.php ADDED
@@ -0,0 +1,920 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Main class for Force Regenerate Thumbnails.
4
+ *
5
+ * @link https://wordpress.org/plugins/force-regenerate-thumbnails/
6
+ * @package ForceRegenerateThumbnails
7
+ */
8
+
9
+ /**
10
+ * Force Regenerate Thumbnails
11
+ *
12
+ * @since 1.0
13
+ */
14
+ class ForceRegenerateThumbnails {
15
+
16
+ /**
17
+ * Register ID of management page, or false if the user has no permissions.
18
+ *
19
+ * @access public
20
+ * @var string|bool $menu_id
21
+ * @since 1.0
22
+ */
23
+ public $menu_id;
24
+
25
+ /**
26
+ * User capability required to use this plugin.
27
+ *
28
+ * @access public
29
+ * @var string $capability
30
+ * @since 1.0
31
+ */
32
+ public $capability;
33
+
34
+ /**
35
+ * Version of the plugin.
36
+ *
37
+ * @access public
38
+ * @var float VERSION
39
+ * @since 2.1.0
40
+ */
41
+ const VERSION = 210;
42
+
43
+ /**
44
+ * Plugin initialization
45
+ *
46
+ * @access public
47
+ * @since 1.0
48
+ */
49
+ function __construct() {
50
+
51
+ add_action( 'admin_menu', array( &$this, 'add_admin_menu' ) );
52
+ add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueues' ) );
53
+ add_action( 'wp_ajax_regeneratethumbnail', array( &$this, 'ajax_process_image' ) );
54
+ add_filter( 'media_row_actions', array( &$this, 'add_media_row_action' ), 10, 2 );
55
+ add_filter( 'bulk_actions-upload', array( &$this, 'add_bulk_actions' ) );
56
+ add_filter( 'handle_bulk_actions-upload', array( &$this, 'bulk_action_handler' ), 10, 3 );
57
+
58
+ // Allow people to change what capability is required to use this plugin.
59
+ $this->capability = apply_filters( 'regenerate_thumbs_cap', 'manage_options' );
60
+ }
61
+
62
+ /**
63
+ * Register the management page
64
+ *
65
+ * @access public
66
+ * @since 1.0
67
+ */
68
+ function add_admin_menu() {
69
+ $this->menu_id = add_management_page(
70
+ __( 'Force Regenerate Thumbnails', 'force-regenerate-thumbnails' ),
71
+ __( 'Force Regenerate Thumbnails', 'force-regenerate-thumbnails' ),
72
+ $this->capability,
73
+ 'force-regenerate-thumbnails',
74
+ array( &$this, 'force_regenerate_interface' )
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Enqueue the needed Javascript and CSS
80
+ *
81
+ * @param string $hook_suffix The hook suffix for the current admin page.
82
+ * @access public
83
+ * @since 1.0
84
+ */
85
+ function admin_enqueues( $hook_suffix ) {
86
+
87
+ if ( $hook_suffix !== $this->menu_id ) {
88
+ return;
89
+ }
90
+
91
+ global $wpdb;
92
+
93
+ wp_enqueue_style( 'jquery-ui-force-regen', plugins_url( 'assets/jquery-ui-1.10.1.custom.css', __FILE__ ), array(), self::VERSION );
94
+ wp_enqueue_script( 'force-regen-script', plugins_url( 'assets/regen.js', __FILE__ ), array( 'jquery-ui-progressbar' ), self::VERSION );
95
+ // If the button was clicked.
96
+ if ( ! empty( $_POST['force-regenerate-thumbnails'] ) || ! empty( $_REQUEST['ids'] ) ) {
97
+
98
+ // Capability check.
99
+ if ( ! current_user_can( $this->capability ) ) {
100
+ wp_die( esc_html__( 'Access denied.', 'force-regenerate-thumbnails' ) );
101
+ }
102
+
103
+ // Form nonce check.
104
+ check_admin_referer( 'force-regenerate-thumbnails' );
105
+
106
+ // Create the list of image IDs.
107
+ $images = array();
108
+ if (
109
+ ! empty( $_REQUEST['ids'] ) &&
110
+ (
111
+ preg_match( '/^[\d,]+$/', trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ), $request_ids ) ||
112
+ is_numeric( trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ) )
113
+ )
114
+ ) {
115
+ if ( is_numeric( trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ) ) ) {
116
+ $images[] = (int) trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' );
117
+ } else {
118
+ $images = explode( ',', $request_ids[0] );
119
+ array_walk( $images, 'intval' );
120
+ }
121
+ $ids = implode( ',', $images );
122
+ } else {
123
+
124
+ // Directly querying the database is normally frowned upon, but all
125
+ // of the API functions will return the full post objects which will
126
+ // suck up lots of memory. This is best, just not as future proof.
127
+ if ( extension_loaded( 'imagick' ) ) {
128
+ $images = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND (post_mime_type LIKE '%%image%%' OR post_mime_type LIKE '%%pdf%%') ORDER BY ID DESC" );
129
+ } else {
130
+ $images = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE '%%image%%' ORDER BY ID DESC" );
131
+ }
132
+ if ( empty( $images ) ) {
133
+ return;
134
+ }
135
+
136
+ $ids = implode( ',', $images );
137
+ }
138
+
139
+ $this->image_count = count( $images );
140
+
141
+ wp_localize_script(
142
+ 'force-regen-script',
143
+ 'regen_thumbs',
144
+ array(
145
+ '_wpnonce' => wp_create_nonce( 'force-regenerate-attachment' ),
146
+ 'image_ids' => $images,
147
+ 'image_count' => $this->image_count,
148
+ 'stopping' => esc_html__( 'Stopping...', 'force-regenerate-thumbnails' ),
149
+ 'unknown_error' => esc_html__( 'Unknown error occured.', 'force-regenerate-thumbnails' ),
150
+ )
151
+ );
152
+ $this->get_admin_colors();
153
+ wp_add_inline_style( 'jquery-ui-force-regen', '.ui-widget-header { background-color: ' . $this->admin_color . '; }' );
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Grabs the color scheme information from the current admin theme.
159
+ *
160
+ * @global array $_wp_admin_css_colors An array of available admin color/theme objects.
161
+ */
162
+ function get_admin_colors() {
163
+ if ( ! empty( $this->admin_color ) && preg_match( '/^\#([0-9a-fA-F]){3,6}$/', $this->admin_color ) ) {
164
+ return;
165
+ }
166
+ global $_wp_admin_css_colors;
167
+ if ( function_exists( 'wp_add_inline_style' ) ) {
168
+ $user_info = wp_get_current_user();
169
+ if (
170
+ is_array( $_wp_admin_css_colors ) &&
171
+ ! empty( $user_info->admin_color ) &&
172
+ isset( $_wp_admin_css_colors[ $user_info->admin_color ] ) &&
173
+ is_object( $_wp_admin_css_colors[ $user_info->admin_color ] ) &&
174
+ is_array( $_wp_admin_css_colors[ $user_info->admin_color ]->colors ) &&
175
+ ! empty( $_wp_admin_css_colors[ $user_info->admin_color ]->colors[2] ) &&
176
+ preg_match( '/^\#([0-9a-fA-F]){3,6}$/', $_wp_admin_css_colors[ $user_info->admin_color ]->colors[2] )
177
+ ) {
178
+ $this->admin_color = $_wp_admin_css_colors[ $user_info->admin_color ]->colors[2];
179
+ return;
180
+ }
181
+ switch ( $user_info->admin_color ) {
182
+ case 'midnight':
183
+ $this->admin_color = '#e14d43';
184
+ break;
185
+ case 'blue':
186
+ $this->admin_color = '#096484';
187
+ break;
188
+ case 'light':
189
+ $this->admin_color = '#04a4cc';
190
+ break;
191
+ case 'ectoplasm':
192
+ $this->admin_color = '#a3b745';
193
+ break;
194
+ case 'coffee':
195
+ $this->admin_color = '#c7a589';
196
+ break;
197
+ case 'ocean':
198
+ $this->admin_color = '#9ebaa0';
199
+ break;
200
+ case 'sunrise':
201
+ $this->admin_color = '#dd823b';
202
+ break;
203
+ default:
204
+ $this->admin_color = '#0073aa';
205
+ }
206
+ }
207
+ if ( empty( $this->admin_color ) ) {
208
+ $this->admin_color = '#0073aa';
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Add a "Force Regenerate Thumbnails" link to the media row actions
214
+ *
215
+ * @param array $actions A list of existing Media row actions.
216
+ * @param WP_Post $post The current attachment/post object.
217
+ * @return array
218
+ * @access public
219
+ * @since 1.0
220
+ */
221
+ function add_media_row_action( $actions, $post ) {
222
+ if ( 'application/pdf' === $post->post_mime_type && ! extension_loaded( 'imagick' ) ) {
223
+ return $actions;
224
+ }
225
+ if ( 'image/svg+xml' === $post->post_mime_type ) {
226
+ return $actions;
227
+ }
228
+ if (
229
+ ( 'application/pdf' === $post->post_mime_type || 'image/' === substr( $post->post_mime_type, 0, 6 ) ) &&
230
+ current_user_can( $this->capability )
231
+ ) {
232
+ $url = wp_nonce_url(
233
+ add_query_arg(
234
+ array(
235
+ 'page' => 'force-regenerate-thumbnails',
236
+ 'goback' => add_query_arg( null, null ),
237
+ 'ids' => (int) $post->ID,
238
+ ),
239
+ admin_url( 'tools.php' ),
240
+ ),
241
+ 'force-regenerate-thumbnails'
242
+ );
243
+
244
+ $actions['regenerate_thumbnails'] = '<a href="' . esc_url( $url ) . '" title="' . esc_attr__( 'Regenerate the thumbnails for this single image', 'force-regenerate-thumbnails' ) . '">' . __( 'Force Regenerate Thumbnails', 'force-regenerate-thumbnails' ) . '</a>';
245
+ }
246
+
247
+ return $actions;
248
+ }
249
+
250
+ /**
251
+ * Add "Force Regenerate Thumbnails" to the Bulk Actions media dropdown
252
+ *
253
+ * @param array $actions Bulk actions list.
254
+ * @return array
255
+ * @access public
256
+ * @since 1.0
257
+ */
258
+ function add_bulk_actions( $actions ) {
259
+
260
+ $delete = false;
261
+ if ( ! empty( $actions['delete'] ) ) {
262
+ $delete = $actions['delete'];
263
+ unset( $actions['delete'] );
264
+ }
265
+
266
+ $actions['bulk_force_regenerate_thumbnails'] = __( 'Force Regenerate Thumbnails', 'force-regenerate-thumbnails' );
267
+
268
+ if ( $delete ) {
269
+ $actions['delete'] = $delete;
270
+ }
271
+
272
+ return $actions;
273
+ }
274
+
275
+ /**
276
+ * Handles the bulk actions POST
277
+ *
278
+ * @access public
279
+ * @since 1.0
280
+ *
281
+ * @param string $redirect_to The URL from whence we came.
282
+ * @param string $doaction The action requested.
283
+ * @param array $post_ids An array of attachment ID numbers.
284
+ * @return string The URL to go back to when we are done handling the action.
285
+ */
286
+ function bulk_action_handler( $redirect_to, $doaction, $post_ids ) {
287
+ if ( empty( $doaction ) || 'bulk_force_regenerate_thumbnails' !== $doaction ) {
288
+ return $redirect_to;
289
+ }
290
+
291
+ if ( empty( $post_ids ) || ! is_array( $post_ids ) ) {
292
+ return $redirect_to;
293
+ }
294
+
295
+ check_admin_referer( 'bulk-media' );
296
+ $ids = implode( ',', array_map( 'intval', $post_ids ) );
297
+
298
+ return add_query_arg(
299
+ array(
300
+ '_wpnonce' => wp_create_nonce( 'force-regenerate-thumbnails' ),
301
+ 'page' => 'force-regenerate-thumbnails',
302
+ 'ids' => $ids,
303
+ 'goback' => esc_url( wp_get_referer() ),
304
+ ),
305
+ admin_url( 'tools.php' )
306
+ );
307
+ }
308
+
309
+
310
+ /**
311
+ * The user interface plus thumbnail regenerator
312
+ *
313
+ * @access public
314
+ * @since 1.0
315
+ */
316
+ function force_regenerate_interface() {
317
+ $retry_url = wp_nonce_url(
318
+ admin_url( 'tools.php?page=force-regenerate-thumbnails' ),
319
+ 'force-regenerate-thumbnails'
320
+ );
321
+ ?>
322
+
323
+ <div id="frt-message" class="notice notice-success is-dismissible" style="display:none">
324
+ <p><strong><?php esc_html_e( 'All done!', 'force-regenerate-thumbnails' ); ?></strong>
325
+ <?php if ( ! empty( $_GET['goback'] ) ) : ?>
326
+ <a href=<?php echo '"' . esc_url( sanitize_url( wp_unslash( $_GET['goback'] ) ) ) . '"'; ?>><?php esc_html_e( 'Go back to the previous page.', 'force-regenerate-thumbnails' ); ?></a>
327
+ <?php endif; ?>
328
+ </p>
329
+ <p id="frt-retry-container" style="display:none">
330
+ <a id="frt-retry-images" href="<?php echo esc_url( $retry_url ); ?>" class="button-secondary"><?php esc_html_e( 'Retry Failed Images', 'force-regenerate-thumbnails' ); ?></a>
331
+ </p>
332
+ </div>
333
+
334
+ <div class="wrap regenthumbs">
335
+ <h2><?php esc_html_e( 'Force Regenerate Thumbnails', 'force-regenerate-thumbnails' ); ?></h2>
336
+
337
+ <?php
338
+ // If the button was clicked.
339
+ if ( ! empty( $_POST['force-regenerate-thumbnails'] ) || ! empty( $_REQUEST['ids'] ) ) {
340
+
341
+ // Capability check.
342
+ if ( ! current_user_can( $this->capability ) ) {
343
+ wp_die( esc_html__( 'Access denied.', 'force-regenerate-thumbnails' ) );
344
+ }
345
+
346
+ // Form nonce check.
347
+ check_admin_referer( 'force-regenerate-thumbnails' );
348
+ if ( empty( $this->image_count ) ) {
349
+ echo '<p>' . esc_html__( 'You do not appear to have uploaded any images or PDF files yet.', 'force-regenerate-thumbnails' ) . '</p></div>';
350
+ return;
351
+ }
352
+ ?>
353
+
354
+ <p><?php esc_html_e( 'Please be patient while the thumbnails are regenerated. Details will be displayed below as each image is completed.', 'force-regenerate-thumbnails' ); ?></p>
355
+
356
+ <noscript><p><em><?php esc_html_e( 'You must enable Javascript in order to proceed!', 'force-regenerate-thumbnails' ); ?></em></p></noscript>
357
+
358
+ <div id="regenthumbs-bar" style="position:relative;height:25px;">
359
+ </div>
360
+
361
+ <p><input type="button" class="button hide-if-no-js" name="regenthumbs-stop" id="regenthumbs-stop" value="<?php esc_html_e( 'Abort Process', 'force-regenerate-thumbnails' ); ?>" /></p>
362
+
363
+ <h3 class="title"><?php esc_html_e( 'Process Information', 'force-regenerate-thumbnails' ); ?></h3>
364
+
365
+ <p>
366
+ <?php
367
+ /* translators: %d: the total number of images */
368
+ printf( esc_html__( 'Total: %s', 'force-regenerate-thumbnails' ), '<span id="regenthumbs-debug-totalcount">' . (int) $this->image_count . '</span>' );
369
+ echo '<br>';
370
+ /* translators: %d: the number of successfully regenerated images */
371
+ printf( esc_html__( 'Success: %s', 'force-regenerate-thumbnails' ), '<span id="regenthumbs-debug-successcount">0</span>' );
372
+ echo '<br>';
373
+ /* translators: %d: the number of regeneration failures */
374
+ printf( esc_html__( 'Failure: %s', 'force-regenerate-thumbnails' ), '<span id="regenthumbs-debug-failurecount">0</span>' );
375
+ ?>
376
+ </p>
377
+
378
+ <ol id="regenthumbs-debuglist">
379
+ <li style="display:none"></li>
380
+ </ol>
381
+
382
+ <?php
383
+ } else {
384
+ // No button click? Display the form.
385
+ ?>
386
+ <form method="post" action="">
387
+ <?php wp_nonce_field( 'force-regenerate-thumbnails' ); ?>
388
+
389
+ <h3><?php esc_html_e( 'All Thumbnails', 'force-regenerate-thumbnails' ); ?></h3>
390
+
391
+ <p>
392
+ <?php esc_html_e( 'You may regenerate thumbnails for all images that you have uploaded to your blog.', 'force-regenerate-thumbnails' ); ?><br>
393
+ <?php esc_html_e( 'Be sure to backup your site before you begin.', 'force-regenerate-thumbnails' ); ?>
394
+ </p>
395
+
396
+ <p>
397
+ <noscript><p><em><?php esc_html_e( 'You must enable Javascript in order to proceed!', 'force-regenerate-thumbnails' ); ?></em></p></noscript>
398
+ <input type="submit" class="button-primary hide-if-no-js" name="force-regenerate-thumbnails" id="force-regenerate-thumbnails" value="<?php esc_attr_e( 'Regenerate All Thumbnails', 'force-regenerate-thumbnails' ); ?>" />
399
+ </p>
400
+
401
+ </br>
402
+ <h3><?php esc_html_e( 'Specific Thumbnails', 'force-regenerate-thumbnails' ); ?></h3>
403
+
404
+ <p>
405
+ <?php /* translators: %s: Media Library (link) */ ?>
406
+ <?php printf( esc_html__( 'You may regenerate thumbnails for specific images from the %s in List mode.', 'force-regenerate-thumbnails' ), '<a href="' . esc_url( admin_url( 'upload.php?mode=list' ) ) . '">' . esc_html__( 'Media Library', 'force-regenerate-thumbnails' ) . '</a>' ); ?>
407
+ <?php esc_html_e( 'Be sure to backup your site before you begin.', 'force-regenerate-thumbnails' ); ?>
408
+ </p>
409
+ </form>
410
+ <?php
411
+ } // End if button
412
+ ?>
413
+ <?php if ( ! function_exists( 'ewww_image_optimizer' ) ) : ?>
414
+ <p>
415
+ <?php
416
+ printf(
417
+ /* translators: %s: link to install EWWW Image Optimizer plugin */
418
+ esc_html__( 'Install the free %s for sharper thumbnails, better compression, and to control which thumbnails are created.', 'force-regenerate-thumbnails' ),
419
+ '<a href="' . esc_url( admin_url( 'plugin-install.php?s=ewww+image+optimizer&tab=search&type=term' ) ) . '">EWWW Image Optimizer</a>'
420
+ );
421
+ ?>
422
+ </p>
423
+ <?php endif; ?>
424
+ </div>
425
+
426
+ <?php
427
+ }
428
+
429
+
430
+ /**
431
+ * Process a single image ID (this is an AJAX handler)
432
+ *
433
+ * @access public
434
+ * @since 1.0
435
+ * @throws Exception Any time we find an image we can't handle: permissions, corruption, doesn't exist, etc.
436
+ */
437
+ function ajax_process_image() {
438
+ if ( empty( $_REQUEST['id'] ) ) {
439
+ $this->ob_clean();
440
+ wp_die( wp_json_encode( array( 'error' => esc_html__( 'No attachment ID submitted.', 'force-regenerate-thumbnails' ) ) ) );
441
+ }
442
+
443
+ // No timeout limit.
444
+ if ( $this->stl_check() ) {
445
+ set_time_limit( 0 );
446
+ }
447
+
448
+ // Don't break the JSON result.
449
+ error_reporting( 0 );
450
+ $id = (int) $_REQUEST['id'];
451
+
452
+ try {
453
+ header( 'Content-type: application/json' );
454
+ if ( ! current_user_can( $this->capability ) ) {
455
+ throw new Exception( esc_html__( 'Your user account does not have permission to regenerate thumbnails.', 'force-regenerate-thumbnails' ) );
456
+ }
457
+ if ( empty( $_REQUEST['frt_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['frt_wpnonce'] ), 'force-regenerate-attachment' ) ) {
458
+ throw new Exception( esc_html__( 'Access token has expired, please reload the page.', 'force-regenerate-thumbnails' ) );
459
+ }
460
+
461
+ if ( apply_filters( 'regenerate_thumbs_skip_image', false, $id ) ) {
462
+ die(
463
+ wp_json_encode(
464
+ array(
465
+ /* translators: %d: attachment ID number */
466
+ 'success' => '<div id="message" class="notice notice-info"><p>' . sprintf( esc_html__( 'Skipped: %d', 'force-regenerate-thumbnails' ), (int) $id ) . '</p></div>',
467
+ )
468
+ )
469
+ );
470
+ }
471
+
472
+ $image = get_post( $id );
473
+
474
+ if ( is_null( $image ) ) {
475
+ /* translators: %d: attachment ID number */
476
+ throw new Exception( sprintf( esc_html__( 'Failed: %d is an invalid media ID.', 'force-regenerate-thumbnails' ), (int) $id ) );
477
+ }
478
+
479
+ if ( 'attachment' !== $image->post_type || ( 'image/' !== substr( $image->post_mime_type, 0, 6 ) && 'application/pdf' !== $image->post_mime_type ) ) {
480
+ /* translators: %d: attachment ID number */
481
+ throw new Exception( sprintf( esc_html__( 'Failed: %d is an invalid media ID.', 'force-regenerate-thumbnails' ), (int) $id ) );
482
+ }
483
+
484
+ if ( 'application/pdf' === $image->post_mime_type && ! extension_loaded( 'imagick' ) ) {
485
+ throw new Exception( esc_html__( 'Failed: The imagick extension is required for PDF files.', 'force-regenerate-thumbnails' ) );
486
+ }
487
+
488
+ if ( 'image/svg+xml' === $image->post_mime_type ) {
489
+ die(
490
+ wp_json_encode(
491
+ array(
492
+ /* translators: %d: attachment ID number */
493
+ 'success' => '<div id="message" class="notice notice-info"><p>' . sprintf( esc_html__( 'Skipped: %d is a SVG', 'force-regenerate-thumbnails' ), (int) $id ) . '</p></div>',
494
+ )
495
+ )
496
+ );
497
+ }
498
+
499
+ $upload_dir = wp_get_upload_dir();
500
+ $meta = wp_get_attachment_metadata( $image->ID );
501
+
502
+ // Get full-size image.
503
+ $image_fullpath = $this->get_attachment_path( $image->ID, $meta );
504
+
505
+ $debug_1 = $image_fullpath;
506
+ $debug_2 = '';
507
+ $debug_3 = '';
508
+ $debug_4 = '';
509
+
510
+ // Didn't get a valid image path.
511
+ if ( empty( $image_fullpath ) || false === realpath( $image_fullpath ) ) {
512
+ throw new Exception( esc_html__( 'The originally uploaded image file cannot be found.', 'force-regenerate-thumbnails' ) );
513
+ }
514
+
515
+ $thumb_deleted = array();
516
+ $thumb_error = array();
517
+ $thumb_regenerate = array();
518
+
519
+ /**
520
+ * Try delete all thumbnails
521
+ */
522
+ if ( ! empty( $meta['sizes'] ) && is_iterable( $meta['sizes'] ) ) {
523
+ foreach ( $meta['sizes'] as $size_data ) {
524
+ if ( empty( $size_data['file'] ) ) {
525
+ continue;
526
+ }
527
+ $thumb_fullpath = trailingslashit( $file_info['dirname'] ) . wp_basename( $size_data['file'] );
528
+ if ( apply_filters( 'regenerate_thumbs_weak', false, $thumb_fullpath ) ) {
529
+ continue;
530
+ }
531
+ if ( $thumb_fullpath !== $image_fullpath && is_file( $thumb_fullpath ) ) {
532
+ do_action( 'regenerate_thumbs_pre_delete', $thumb_fullpath );
533
+ unlink( $thumb_fullpath );
534
+ if ( is_file( $thumb_fullpath . '.webp' ) ) {
535
+ unlink( $thumb_fullpath . '.webp' );
536
+ }
537
+ clearstatcache();
538
+ do_action( 'regenerate_thumbs_post_delete', $thumb_fullpath );
539
+ if ( ! is_file( $thumb_fullpath ) ) {
540
+ $thumb_deleted[] = sprintf( '%dx%d', $size_data['width'], $size_data['height'] );
541
+ } else {
542
+ $thumb_error[] = sprintf( '%dx%d', $size_data['width'], $size_data['height'] );
543
+ }
544
+ }
545
+ }
546
+ }
547
+
548
+ // Hack to find thumbnail.
549
+ $file_info = pathinfo( $image_fullpath );
550
+ $file_stem = $this->remove_from_end( $file_info['filename'], '-scaled' ) . '-';
551
+
552
+ $files = array();
553
+ $path = opendir( $file_info['dirname'] );
554
+
555
+ if ( false !== $path ) {
556
+ $thumb = readdir( $path );
557
+ while ( false !== $thumb ) {
558
+ if ( 0 === strpos( $thumb, $file_stem ) && str_ends_with( $thumb, $file_info['extension'] ) ) {
559
+ $files[] = $thumb;
560
+ }
561
+ $thumb = readdir( $path );
562
+ }
563
+ closedir( $path );
564
+ sort( $files );
565
+ }
566
+
567
+ foreach ( $files as $thumb ) {
568
+ $thumb_fullpath = trailingslashit( $file_info['dirname'] ) . $thumb;
569
+ if ( apply_filters( 'regenerate_thumbs_weak', false, $thumb_fullpath ) ) {
570
+ continue;
571
+ }
572
+
573
+ $thumb_info = pathinfo( $thumb_fullpath );
574
+ $valid_thumb = explode( $file_stem, $thumb_info['filename'] );
575
+ if ( '' === $valid_thumb[0] && ! empty( $valid_thumb[1] ) ) {
576
+ if ( 0 === strpos( $valid_thumb[1], 'scaled-' ) ) {
577
+ $valid_thumb[1] = str_replace( 'scaled-', '', $valid_thumb[1] );
578
+ }
579
+ $dimension_thumb = explode( 'x', $valid_thumb[1] );
580
+ if ( 2 === count( $dimension_thumb ) ) {
581
+ if ( is_numeric( $dimension_thumb[0] ) && is_numeric( $dimension_thumb[1] ) ) {
582
+ do_action( 'regenerate_thumbs_pre_delete', $thumb_fullpath );
583
+ unlink( $thumb_fullpath );
584
+ if ( is_file( $thumb_fullpath . '.webp' ) ) {
585
+ unlink( $thumb_fullpath . '.webp' );
586
+ }
587
+ clearstatcache();
588
+ do_action( 'regenerate_thumbs_post_delete', $thumb_fullpath );
589
+ if ( ! is_file( $thumb_fullpath ) ) {
590
+ $thumb_deleted[] = sprintf( '%dx%d', $dimension_thumb[0], $dimension_thumb[1] );
591
+ } else {
592
+ $thumb_error[] = sprintf( '%dx%d', $dimension_thumb[0], $dimension_thumb[1] );
593
+ }
594
+ }
595
+ }
596
+ }
597
+ }
598
+
599
+ /**
600
+ * Regenerate all thumbnails
601
+ */
602
+ if ( function_exists( 'wp_get_original_image_path' ) ) {
603
+ $original_path = apply_filters( 'regenerate_thumbs_original_image', wp_get_original_image_path( $image->ID, true ) );
604
+ }
605
+ if ( empty( $original_path ) || ! is_file( $original_path ) ) {
606
+ $original_path = $image_fullpath;
607
+ }
608
+
609
+ $metadata = wp_generate_attachment_metadata( $image->ID, $original_path );
610
+ if ( is_wp_error( $metadata ) ) {
611
+ throw new Exception( esc_html( $metadata->get_error_message() ) );
612
+ }
613
+ if ( empty( $metadata ) ) {
614
+ throw new Exception( esc_html__( 'Unknown failure.', 'force-regenerate-thumbnails' ) );
615
+ }
616
+ wp_update_attachment_metadata( $image->ID, $metadata );
617
+ do_action( 'regenerate_thumbs_post_update', $image->ID, $original_path );
618
+
619
+ /**
620
+ * Verify results (deleted, errors, success)
621
+ */
622
+ $files = array();
623
+ $path = opendir( $file_info['dirname'] );
624
+ if ( false !== $path ) {
625
+ $thumb = readdir( $path );
626
+ while ( false !== $thumb ) {
627
+ /* if ( ! ( strrpos( $thumb, $file_info['filename'] ) === false ) ) { */
628
+ if ( 0 === strpos( $thumb, $file_stem ) && str_ends_with( $thumb, $file_info['extension'] ) ) {
629
+ $files[] = $thumb;
630
+ }
631
+ $thumb = readdir( $path );
632
+ }
633
+ closedir( $path );
634
+ sort( $files );
635
+ }
636
+ if ( ! empty( $metadata['sizes'] ) && is_iterable( $metadata['sizes'] ) ) {
637
+ foreach ( $metadata['sizes'] as $size_data ) {
638
+ if ( empty( $size_data['file'] ) ) {
639
+ continue;
640
+ }
641
+ if ( in_array( $size_data['file'], $files, true ) ) {
642
+ continue;
643
+ }
644
+ $thumb_regenerate[] = sprintf( '%dx%d', $size_data['width'], $size_data['height'] );
645
+ }
646
+ }
647
+
648
+ foreach ( $files as $thumb ) {
649
+ $thumb_fullpath = trailingslashit( $file_info['dirname'] ) . $thumb;
650
+ $thumb_info = pathinfo( $thumb_fullpath );
651
+ $valid_thumb = explode( $file_stem, $thumb_info['filename'] );
652
+ if ( '' === $valid_thumb[0] ) {
653
+ $dimension_thumb = explode( 'x', $valid_thumb[1] );
654
+ if ( 2 === count( $dimension_thumb ) ) {
655
+ if ( is_numeric( $dimension_thumb[0] ) && is_numeric( $dimension_thumb[1] ) ) {
656
+ $thumb_regenerate[] = sprintf( '%dx%d', $dimension_thumb[0], $dimension_thumb[1] );
657
+ }
658
+ }
659
+ }
660
+ }
661
+
662
+ // Remove success if has in error list.
663
+ foreach ( $thumb_regenerate as $key => $regenerate ) {
664
+ if ( in_array( $regenerate, $thumb_error, true ) ) {
665
+ unset( $thumb_regenerate[ $key ] );
666
+ }
667
+ }
668
+
669
+ // Remove deleted if has in success list, so that we only show those that were no longer registered.
670
+ foreach ( $thumb_deleted as $key => $deleted ) {
671
+ if ( in_array( $deleted, $thumb_regenerate, true ) ) {
672
+ unset( $thumb_deleted[ $key ] );
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Display results
678
+ */
679
+ ob_start(); // To suppress any error output.
680
+ $message = '<strong>' .
681
+ sprintf(
682
+ /* translators: 1: attachment title 2: attachment ID number */
683
+ esc_html__( '%1$s (ID %2$d)', 'force-regenerate-thumbnails' ),
684
+ esc_html( get_the_title( $id ) ),
685
+ (int) $image->ID
686
+ ) .
687
+ '</strong>';
688
+ $message .= '<br><br>';
689
+ /* translators: %s: the path to the uploads directory */
690
+ $message .= '<code>' . sprintf( esc_html__( 'Upload Directory: %s', 'force-regenerate-thumbnails' ), esc_html( $upload_dir['basedir'] ) ) . '</code><br>';
691
+ /* translators: %s: the base URL of the uploads directory */
692
+ $message .= '<code>' . sprintf( esc_html__( 'Upload URL: %s', 'force-regenerate-thumbnails' ), esc_html( $upload_dir['baseurl'] ) ) . '</code><br>';
693
+ /* translators: %s: the full path of the image */
694
+ $message .= '<code>' . sprintf( esc_html__( 'Image: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_1 ) ) . '</code><br>';
695
+ if ( $debug_2 ) {
696
+ /* translators: %s: debug info, if populated */
697
+ $message .= '<code>' . sprintf( esc_html__( 'Image Debug 2: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_2 ) ) . '</code><br>';
698
+ }
699
+ if ( $debug_3 ) {
700
+ /* translators: %s: debug info, if populated */
701
+ $message .= '<code>' . sprintf( esc_html__( 'Image Debug 3: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_3 ) ) . '</code><br>';
702
+ }
703
+ if ( $debug_4 ) {
704
+ /* translators: %s: debug info, if populated */
705
+ $message .= '<code>' . sprintf( esc_html__( 'Image Debug 4: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_4 ) ) . '</code><br>';
706
+ }
707
+
708
+ $message .= '<br>';
709
+
710
+ if ( count( $thumb_deleted ) > 0 ) {
711
+ /* translators: %s: list of deleted thumbs */
712
+ $message .= sprintf( esc_html__( 'Deleted: %s', 'force-regenerate-thumbnails' ), esc_html( implode( ', ', $thumb_deleted ) ) ) . '<br>';
713
+ }
714
+ if ( count( $thumb_error ) > 0 ) {
715
+ /* translators: %s: an error message (translated elsewhere) */
716
+ $message .= '<strong><span style="color: #dd3d36;">' . sprintf( esc_html__( 'Deleted error: %s', 'force-regenerate-thumbnails' ), esc_html( implode( ', ', $thumb_error ) ) ) . '</span></strong><br>';
717
+ /* translators: %s: the path to the uploads directory */
718
+ $message .= '<span style="color: #dd3d36;">' . sprintf( esc_html__( 'Please ensure the folder has write permissions (chmod 755): %s', 'force-regenerate-thumbnails' ), esc_html( $upload_dir['basedir'] ) ) . '</span><br>';
719
+ }
720
+ if ( count( $thumb_regenerate ) > 0 ) {
721
+ /* translators: %s: list of regenerated thumbs/sizes */
722
+ $message .= sprintf( esc_html__( 'Regenerate: %s', 'force-regenerate-thumbnails' ), esc_html( implode( ', ', $thumb_regenerate ) ) ) . '<br>';
723
+ if ( count( $thumb_error ) <= 0 ) {
724
+ /* translators: %s: time elapsed */
725
+ $message .= sprintf( esc_html__( 'Successfully regenerated in %s seconds', 'force-regenerate-thumbnails' ), esc_html( timer_stop() ) ) . '<br>';
726
+ }
727
+ }
728
+
729
+ $message = $this->remove_from_end( $message, '<br>' );
730
+
731
+ $this->ob_clean();
732
+ if ( count( $thumb_error ) > 0 ) {
733
+ die( wp_json_encode( array( 'error' => '<div id="message" class="notice notice-error"><p>' . $message . '</p></div>' ) ) );
734
+ } else {
735
+ die( wp_json_encode( array( 'success' => '<div id="message" class="notice notice-success"><p>' . $message . '</p></div>' ) ) );
736
+ }
737
+ } catch ( Exception $e ) {
738
+ $this->die_json_failure_msg( $id, '<b><span style="color: #DD3D36;">' . $e->getMessage() . '</span></b>' );
739
+ }
740
+ exit;
741
+ }
742
+
743
+ /**
744
+ * Retrieves the path of an attachment via the ID number.
745
+ *
746
+ * @param int $id The attachment ID number.
747
+ * @param array $meta The attachment metadata.
748
+ * @return string The full path to the image.
749
+ */
750
+ function get_attachment_path( $id, $meta ) {
751
+
752
+ // Retrieve the location of the WordPress upload folder.
753
+ $upload_dir = wp_upload_dir( null, false, true );
754
+ $upload_path = trailingslashit( $upload_dir['basedir'] );
755
+
756
+ // Get the file path from postmeta.
757
+ $file = get_post_meta( $id, '_wp_attached_file', true );
758
+ $file_path = ( 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ? $upload_path . $file : $file );
759
+ $filtered_file_path = apply_filters( 'get_attached_file', $file_path, $id );
760
+ if (
761
+ (
762
+ ! $this->stream_wrapped( $filtered_file_path ) ||
763
+ $this->stream_wrapper_exists()
764
+ )
765
+ && is_file( $filtered_file_path )
766
+ ) {
767
+ return $filtered_file_path;
768
+ }
769
+ if (
770
+ (
771
+ ! $this->stream_wrapped( $file_path ) ||
772
+ $this->stream_wrapper_exists()
773
+ )
774
+ && is_file( $file_path )
775
+ ) {
776
+ return $file_path;
777
+ }
778
+ if ( is_array( $meta ) && ! empty( $meta['file'] ) ) {
779
+ $file_path = $meta['file'];
780
+ if ( $this->stream_wrapped( $file_path ) && ! $this->stream_wrapper_exists() ) {
781
+ return '';
782
+ }
783
+ if ( is_file( $file_path ) ) {
784
+ return $file_path;
785
+ }
786
+ $file_path = trailingslashit( $upload_path ) . $file_path;
787
+ if ( is_file( $file_path ) ) {
788
+ return $file_path;
789
+ }
790
+ $upload_path = trailingslashit( WP_CONTENT_DIR ) . 'uploads/';
791
+ $file_path = $upload_path . $meta['file'];
792
+ if ( is_file( $file_path ) ) {
793
+ return $file_path;
794
+ }
795
+ }
796
+ return '';
797
+ }
798
+
799
+ /**
800
+ * Checks the existence of a cloud storage stream wrapper.
801
+ *
802
+ * @return bool True if a supported stream wrapper is found, false otherwise.
803
+ */
804
+ function stream_wrapper_exists() {
805
+ $wrappers = stream_get_wrappers();
806
+ if ( ! is_iterable( $wrappers ) ) {
807
+ return false;
808
+ }
809
+ foreach ( $wrappers as $wrapper ) {
810
+ if ( strpos( $wrapper, 's3' ) === 0 ) {
811
+ return true;
812
+ }
813
+ if ( strpos( $wrapper, 'gs' ) === 0 ) {
814
+ return true;
815
+ }
816
+ }
817
+ return false;
818
+ }
819
+
820
+ /**
821
+ * Checks the filename for an S3 or GCS stream wrapper.
822
+ *
823
+ * @param string $filename The filename to be searched.
824
+ * @return bool True if a stream wrapper is found, false otherwise.
825
+ */
826
+ function stream_wrapped( $filename ) {
827
+ if ( false !== strpos( $filename, '://' ) ) {
828
+ if ( strpos( $filename, 's3' ) === 0 ) {
829
+ return true;
830
+ }
831
+ if ( strpos( $filename, 'gs' ) === 0 ) {
832
+ return true;
833
+ }
834
+ }
835
+ return false;
836
+ }
837
+
838
+ /**
839
+ * Trims the given 'needle' from the end of the 'haystack'.
840