wp-offload.php
11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
<?php
namespace EnableMediaReplace\Externals;
if (! defined('ABSPATH')) {
exit; // Exit if accessed directly.
}
use EnableMediaReplace\ShortPixelLogger\ShortPixelLogger as Log;
use EnableMediaReplace\Notices\NoticeController as Notices;
class WPOffload
{
private static $instance;
private $as3cf;
private $sources; // cache for url > source_id lookup, to prevent duplicate queries.
private static $offloadPrevented = array();
private $post_id; // source_id. The plugin has this, so why so tricky checks.
public function __construct()
{
add_action('as3cf_init', array($this, 'init'));
add_action('emr/converter/prevent-offload', array($this, 'preventOffload'), 10);
add_action('emr/converter/prevent-offload-off', array($this, 'preventOffloadOff'), 10);
add_filter('as3cf_pre_update_attachment_metadata', array($this, 'preventUpdateMetaData'), 10,4);
}
public static function getInstance()
{
if (is_null(self::$instance))
{
self::$instance = new WPOffload();
}
return self::$instance;
}
public function init($as3cf)
{
if (! class_exists('\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item'))
{
Notices::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'enable-media-replace'), true);
return false;
}
$this->as3cf = $as3cf;
if (method_exists($as3cf, 'get_item_handler'))
{
}
else {
Notices::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'enable-media-replace'), true);
return false;
}
// @todo This all is begging for the creating of an enviroment model / controller.
if( !function_exists('is_plugin_active') ) {
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
}
$spio_active = \is_plugin_active('shortpixel-image-optimiser/wp-shortpixel.php');
// Let spio handle this.
if (false === $spio_active)
{
add_filter('shortpixel/image/urltopath', array($this, 'checkIfOffloaded'), 10,2);
}
add_action('emr_after_remove_current', array($this, 'removeRemote'), 10, 5);
add_filter('emr/file/virtual/translate', array($this, 'getLocalPath'), 10, 3);
add_filter('emr/replace/file_is_movable', array($this, 'isFileMovable'), 10, 2);
add_filter('emr/replace/original_image_path', array($this, 'checkScaledUrl'), 10,2);
add_action('enable-media-replace-upload-done', array($this, 'updateOriginalPath'), 10, 3);
}
/*
* @param $post_id int The post ID
* @param $meta array Old Metadata before remove
* @param $backup_sizes array WP Backup sizes
* @param $sourceFile Object Source File
* @param $targetFile Object Target File
*/
public function removeRemote($post_id, $meta, $backup_sizes, $sourceFile, $targetFile )
{
// Always remove because also thumbnails can be different.
$a3cfItem = $this->getItemById($post_id); // MediaItem is AS3CF Object
if ($a3cfItem === false)
{
Log::addDebug('S3-Offload MediaItem not remote - ' . $post_id);
return false;
}
$remove = \DeliciousBrains\WP_Offload_Media\Items\Remove_Provider_Handler::get_item_handler_key_name();
$itemHandler = $this->as3cf->get_item_handler($remove);
$result = $itemHandler->handle($a3cfItem, array( 'verify_exists_on_local' => false)); //handle it then.
}
// @param s3 based URL that which is needed for finding local path
// @return String Filepath. Translated file path
public function getLocalPath($url, $sourceFileObj, $source_id)
{
$item = $this->getItemById($source_id);
if ($item === false)
{
$source_id = $this->getSourceIDByURL($url);
if (false !== $source_id)
$item = $this->getItemById($source_id);
}
if ($source_id == false)
{
Log::addError('Get Local Path: No source id for URL (Offload) ' . $url);
return false;
}
$original_path = $item->original_source_path(); // $values['original_source_path'];
if (wp_basename($url) !== wp_basename($original_path)) // thumbnails translate to main file.
{
$original_path = str_replace(wp_basename($original_path), wp_basename($url), $original_path);
}
$fs = emr()->filesystem();
$base = $fs->getWPUploadBase();
$file = $base . $original_path;
return $file;
}
public function isFileMovable($bool, $attach_id)
{
$item = $this->getItemById($attach_id);
if ($item === false)
{
return $bool;
}
// Can't move offloaded items.
if (is_object($item))
{
return false;
}
}
public function checkIfOffloaded($bool, $url)
{
$source_id = $this->sourceCache($url);
if (false === $source_id)
{
$extension = substr($url, strrpos($url, '.') + 1);
// If these filetypes are not in the cache, they cannot be found via geSourceyIDByUrl method ( not in path DB ), so it's pointless to try. If they are offloaded, at some point the extra-info might load.
if ($extension == 'webp' || $extension == 'avif')
{
return false;
}
$source_id = $this->getSourceIDByURL($url);
}
if ($source_id !== false)
{
return true;
}
else
{
return false;
}
}
// This is used in the converted. Might be deployed elsewhere for better control.
public function preventOffload($attach_id)
{
self::$offloadPrevented[$attach_id] = true;
}
public function preventOffloadOff($attach_id)
{
unset(self::$offloadPrevented[$attach_id]);
}
public function updateOriginalPath($source_url, $target_url, $post_id)
{
$item = $this->getItemById($post_id);
// If no item comes back, probably it's not offloaded
if (false === $item)
{
return;
}
$original_path = $item->original_path(); // Original path (non-scaled-)
$original_source_path = $item->original_source_path();
$path = $item->path();
$source_path = $item->source_path();
$wp_original = wp_get_original_image_path($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ));
$wp_original = apply_filters('emr/replace/original_image_path', $wp_original, $post_id);
$wp_source = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
$updated = false;
// If image is replaced with another name, the original soruce path will not match. This could also happen when an image is with -scaled as main is replaced by an image that doesn't have it. In all cases update the table to reflect proper changes.
if (wp_basename($wp_original) !== wp_basename($original_path))
{
$newpath = str_replace( wp_basename( $original_path ), wp_basename($wp_original), $original_path );
$item->set_original_path($newpath);
$newpath = str_replace( wp_basename( $original_source_path ), wp_basename($wp_original), $original_source_path );
$updated = true;
$item->set_original_source_path($newpath);
$item->save();
}
}
// When Offload is not offloaded but is created during the process of generate metadata in WP, wp_create_image_subsizes fires an update metadata after just moving the upload, before making any thumbnails. If this is the case and the file has an -scaled / original image setup, the original_source_path becomes the same as the source_path which creates issue later on when dealing with optimizing it, if the file is deleted on local server. Prevent this, and lean on later update metadata.
public function preventUpdateMetaData($bool, $data, $post_id, $old_provider_object)
{
if (isset(self::$offloadPrevented[$post_id]))
{
return true ; // return true to cancel.
}
return $bool;
}
// WP Offload -for some reason - returns the same result of get_attached_file and wp_get_original_image_path , which are different files (one scaled) which then causes a wrong copy action after optimizing the image ( wrong destination download of the remote file ). This happens if offload with delete is on. Attempt to fix the URL to reflect the differences between -scaled and not.
public function checkScaledUrl($filepath, $id)
{
// Original filepath can never have a scaled in there.
// @todo This should probably check -scaled.<extension> as string end preventing issues.
if (strpos($filepath, '-scaled') !== false)
{
$filepath = str_replace('-scaled', '', $filepath);
}
return $filepath;
}
/** @return Returns S3Ofload MediaItem, or false when this does not exist */
protected function getItemById($id, $create = false)
{
$class = $this->getMediaClass();
$mediaItem = $class::get_by_source_id($id);
if (true === $create && $mediaItem === false)
{
$mediaItem = $class::create_from_source_id($id);
}
return $mediaItem;
}
protected function getSourceIDByURL($url)
{
$source_id = $this->sourceCache($url); // check cache first.
if (false === $source_id) // check on the raw url.
{
$class = $this->getMediaClass();
$parsedUrl = parse_url($url);
if (! isset($parsedUrl['scheme']) || ! in_array($parsedUrl['scheme'], array('http','https')))
{
$url = 'http://' . $url; //str_replace($parsedUrl['scheme'], 'https', $url);
}
$source = $class::get_item_source_by_remote_url($url);
$source_id = isset($source['id']) ? intval($source['id']) : false;
}
if (false === $source_id) // check now via the thumbnail hocus.
{
$pattern = '/(.*)-\d+[xX]\d+(\.\w+)/m';
$url = preg_replace($pattern, '$1$2', $url);
$source_id = $this->sourceCache($url); // check cache first.
if (false === $source_id)
{
$source = $class::get_item_source_by_remote_url($url);
$source_id = isset($source['id']) ? intval($source['id']) : false;
}
}
// Check issue with double extensions. If say double webp/avif is on, the double extension causes the URL not to be found (ie .jpg)
if (false === $source_id)
{
if (substr_count($parsedUrl['path'], '.') > 1)
{
// Get extension
$ext = substr(strrchr($url, '.'), 1);
// Remove all extensions from the URL
$checkurl = substr($url, 0, strpos($url,'.')) ;
// Add back the last one.
$checkurl .= '.' . $ext;
// Retry
$source_id = $this->sourceCache($checkurl); // check cache first.
if (false === $source_id)
{
$source = $class::get_item_source_by_remote_url($url);
$source_id = isset($source['id']) ? intval($source['id']) : false;
}
}
}
if ($source_id !== false)
{
$this->sourceCache($url, $source_id); // cache it.
// get item
$item = $this->getItemById($source_id);
if (is_object($item) && method_exists($item, 'extra_info'))
{
$baseUrl = str_replace(basename($url),'', $url);
$extra_info = $item->extra_info();
if (isset($extra_info['objects']))
{
foreach($extra_info['objects'] as $extraItem)
{
if (is_array($extraItem) && isset($extraItem['source_file']))
{
// Add source stuff into cache.
$this->sourceCache($baseUrl . $extraItem['source_file'], $source_id);
}
}
}
}
return $source_id;
}
return false;
}
private function sourceCache($url, $source_id = null)
{
if ($source_id === null && isset($this->sources[$url]))
{
$source_id = $this->sources[$url];
return $source_id;
}
elseif ($source_id !== null)
{
if (! isset($this->sources[$url]))
{
$this->sources[$url] = $source_id;
}
return $source_id;
}
return false;
}
private function getMediaClass()
{
$class = $this->as3cf->get_source_type_class('media-library');
return $class;
}
}