PluginProbe
Media Cloud Sync / 1.2.13
Media Cloud Sync v1.2.13
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / compatbility / compatibility.php

compatibility.php in Media Cloud Sync 1.2.13, at includes/compatbility/compatibility.php

346 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 use WP_CLI;
7
8 class Compatibility {
9 private static $instance = null;
10 private $assets_url;
11 private $version;
12 private $token;
13 /**
14 * Whether to wait for the attachment metadata to be regenerated
15 * before re-offloading the file.
16 *
17 * @var bool
18 */
19 public $wait_for_generate_attachment_metadata = false;
20
21 /**
22 * Compatibility constructor.
23 * @since 1.0.0
24 */
25 private function __construct() {
26 $this->assets_url = WPMCS_ASSETS_URL;
27 $this->version = WPMCS_VERSION;
28 $this->token = WPMCS_TOKEN;
29
30 // Initialize setup
31 $this->init();
32
33 }
34
35 public function init() {
36 /*
37 * WP_Customize_Control
38 * /wp-includes/class-wp-customize_control.php
39 */
40 add_filter('attachment_url_to_postid', [$this, 'customizer_background_image'], 10, 2);
41
42 /*
43 * Legacy filter
44 * 'wpmcs_get_attached_file_copy_back_to_server'
45 */
46 add_filter('wpmcs_get_attached_file', [$this, 'legacy_copy_back_to_server'], 10, 4);
47
48 /*
49 * Regenerate Thumbnails (before v3)
50 * https://wordpress.org/plugins/regenerate-thumbnails/
51 */
52 add_filter('wpmcs_get_attached_file', [$this, 'regenerate_thumbnails_download_file'], 10, 4);
53
54 /**
55 * Regenerate Thumbnails v3+ and other REST-API using plugins that need a server file.
56 */
57 add_filter('rest_dispatch_request', [$this, 'rest_dispatch_request_copy_back_to_server'], 10, 4);
58 add_filter('rest_request_after_callbacks', [$this, 'rest_request_after_callbacks_remove_from_server'], 10, 3);
59 add_filter('wpmcs_wait_for_generate_attachment_metadata', array( $this, 'wait_for_generate_attachment_metadata' ) );
60
61 /*
62 * WP-CLI Compatibility
63 */
64 if (defined('WP_CLI') && class_exists('WP_CLI')) {
65 WP_CLI::add_hook('before_invoke:media regenerate', [$this, 'enable_copy_back_and_wait_for_generate_metadata']);
66 }
67 }
68
69
70 public function rest_request_after_callbacks_remove_from_server($response, $handler, $request) {
71 $routes = [
72 '/regenerate-thumbnails/v\d+/regenerate/',
73 ];
74
75 // Get the current REST route
76 $route = $request->get_route();
77
78 // Apply filter so devs can modify routes
79 $routes = apply_filters('wpmcs_rest_api_enable_get_attached_file_remove_from_server', $routes);
80 $routes = is_array($routes) ? $routes : (array) $routes;
81
82 if (!empty($routes)) {
83 foreach ($routes as $match_route) {
84 if (preg_match('@' . $match_route . '@i', $route)) {
85 if ($request->get_param('id')) {
86 $attachment_id = absint($request->get_param('id'));
87 // Remove the file from the server
88 Item::instance()->may_be_delete_server_files_by_id($attachment_id, 'media_library', true, true);
89 }
90 break;
91 }
92 }
93 }
94
95 return $response;
96 }
97
98 /**
99 * Filters the REST dispatch request to determine whether route needs compatibility actions.
100 *
101 * @param bool $dispatch_result Dispatch result, will be used if not empty.
102 * @param WP_REST_Request $request Request used to generate the response.
103 * @param string $route Route matched for the request.
104 * @param array $handler Route handler used for the request.
105 *
106 * @return bool
107 */
108 public function rest_dispatch_request_copy_back_to_server($dispatch_result, $request, $route, $handler) {
109 $routes = [
110 '/regenerate-thumbnails/v\d+/regenerate/',
111 ];
112
113 $routes = apply_filters('wpmcs_rest_api_enable_get_attached_file_copy_back_to_server', $routes);
114 $routes = is_array($routes) ? $routes : (array)$routes;
115
116 if (!empty($routes)) {
117 foreach ($routes as $match_route) {
118 if (preg_match('@' . $match_route . '@i', $route)) {
119 $this->enable_copy_back_and_wait_for_generate_metadata();
120 break;
121 }
122 }
123 }
124
125 return $dispatch_result;
126 }
127
128 /**
129 * Enable copying back attachments from provider
130 * and waiting for their metadata to be regenerated
131 * before re-offloading.
132 *
133 * @handles WP_CLI:before_invoke:media regenerate
134 */
135 public function enable_copy_back_and_wait_for_generate_metadata() {
136 add_filter('wpmcs_get_attached_file_copy_back_to_server', '__return_true');
137 $this->enable_get_attached_file_copy_back_to_server();
138 $this->wait_for_generate_attachment_metadata = true;
139 }
140
141 /**
142 * Enables copying missing local files back to the server when `get_attached_file` filter is called.
143 */
144 public function enable_get_attached_file_copy_back_to_server() {
145 add_filter('wpmcs_get_attached_file_copy_back_to_server', '__return_true');
146
147 add_filter( 'wp_generate_attachment_metadata', array( $this, 'wp_generate_attachment_metadata' ) );
148 }
149
150 /**
151 * Handler for wp_generate_attachment_metadata. Updates class
152 * member variable when the filter has fired.
153 *
154 * @handles wp_generate_attachment_metadata
155 *
156 * @param mixed $metadata
157 *
158 * @return mixed
159 */
160 public function wp_generate_attachment_metadata( $metadata ) {
161 $this->wait_for_generate_attachment_metadata = false;
162
163 return $metadata;
164 }
165
166 /**
167 * Are we waiting for the wp_generate_attachment_metadata filter and
168 * if so, has it run yet?
169 *
170 * @handles wpmcs_wait_for_generate_attachment_metadata
171 *
172 * @param bool $wait
173 *
174 * @return bool
175 */
176 public function wait_for_generate_attachment_metadata( $wait ) {
177 if ( $this->wait_for_generate_attachment_metadata ) {
178 return true;
179 }
180
181 return $wait;
182 }
183
184 /**
185 * Allow the Regenerate Thumbnails plugin to copy the bucket file back to the server
186 * server when the file is missing on the server via get_attached_file.
187 *
188 * @param string $url
189 * @param string $file
190 * @param int $attachment_id
191 *
192 * @return string
193 */
194 public function regenerate_thumbnails_download_file($url, $file, $attachment_id, $item) {
195 return $this->copy_image_to_server_on_action('regeneratethumbnail', true, $url, $file, $attachment_id);
196 }
197
198 /**
199 * Allow any process to trigger the copy back to server with
200 * the filter 'wpmcs_get_attached_file_copy_back_to_server'
201 *
202 * @param string $url
203 * @param string $file
204 * @param int $attachment_id
205 *
206 * @return string
207 */
208 public function legacy_copy_back_to_server($url, $file, $attachment_id, $wpmcs_item) {
209 $copy_back_to_server = apply_filters('wpmcs_get_attached_file_copy_back_to_server', false, $file, $attachment_id, $wpmcs_item);
210 if (false === $copy_back_to_server) {
211 // Not copying back file
212 return $url;
213 }
214
215 if (($file = $this->copy_provider_file_to_server($attachment_id, $file))) {
216 // Return the file if successfully downloaded from S3
217 return $file;
218 }
219
220 // Return S3 URL as a fallback
221 return $url;
222 }
223
224 /**
225 * Show the correct background image in the customizer
226 *
227 * @param int|null $post_id
228 * @param string $url
229 *
230 * @return int|null
231 */
232 public function customizer_background_image($post_id, $url) {
233 if (!is_null($post_id)) {
234 return $post_id;
235 }
236
237 // There seems to be a bug in the WP Customizer whereby sometimes it puts the attachment ID on the URL.
238 if (is_numeric($url)) {
239 $item = Item::get($url);
240
241 // If we found an offloaded Media Library item for that ID, job's a good'n'.
242 if (!Utils::is_empty($item)) {
243 $post_id = $url;
244 }
245 } else {
246 $path = Utils::get_attachment_source_path($url);
247 if (!Utils::is_empty($path)) {
248 $items = Item::instance()->get_items_by_source_paths([$path]);
249 if (!Utils::is_empty($items)) {
250 // If we found an offloaded Media Library item for that path, job's a good'n'.
251 $post_id = $items[0]['source_id'];
252 }
253 }
254 }
255
256 // Must return null if not found.
257 return empty($post_id) ? null : $post_id;
258 }
259
260 /**
261 * Check the current request is a specific one based on action and
262 * optional context
263 *
264 * @param string $action_key
265 * @param bool $ajax
266 * @param null|string|array $context_key
267 *
268 * @return bool
269 */
270 public function maybe_process_on_action($action_key, $ajax, $context_key = null) {
271 if ($ajax !== Utils::is_ajax()) {
272 return false;
273 }
274
275 $var_type = 'GET';
276
277 if (isset($_GET['action'])) {
278 $action = Utils::filter_input('action');
279 } elseif (isset($_POST['action'])) {
280 $var_type = 'POST';
281 $action = Utils::filter_input('action', INPUT_POST);
282 } else {
283 return false;
284 }
285
286 $context_check = true;
287 if (!is_null($context_key)) {
288 $global = constant('INPUT_' . $var_type);
289 $context = Utils::filter_input('context', $global);
290
291 if (is_array($context_key)) {
292 $context_check = in_array($context, $context_key);
293 } else {
294 $context_check = ($context_key === $context);
295 }
296 }
297
298 return ($action_key === sanitize_key($action) && $context_check);
299 }
300
301 /**
302 * Generic method for copying back an S3 file to the server on a specific AJAX action
303 *
304 * @return string
305 */
306 public function copy_image_to_server_on_action($action_key, $ajax, $url, $file, $attachment_id) {
307 if (false === $this->maybe_process_on_action($action_key, $ajax)) {
308 return $url;
309 }
310
311 if (($file = $this->copy_provider_file_to_server($attachment_id, $file))) {
312 // Return the file if successfully downloaded from S3
313 return $file;
314 }
315
316 return $url;
317 }
318
319 /**
320 * Download a file from bucket if the file does not exist serverly and places it where
321 * the attachment's file should be.
322 *
323 * @return string|bool File if downloaded, false on failure
324 */
325 private function copy_provider_file_to_server($attachment_id, $file) {
326 // Download files
327 if (!Item::instance()->moveToServerBySourcePath($attachment_id, $file)) {
328 return false;
329 }
330
331 return $file;
332 }
333
334 /**
335 * Get the singleton instance of the Compatibility class.
336 *
337 * @return Compatibility
338 */
339 public static function instance() {
340 if (self::$instance === null) {
341 self::$instance = new self();
342 }
343 return self::$instance;
344 }
345 }
346