PluginProbe
WP-Stateless – Google Cloud Storage / 2.2.5
WP-Stateless – Google Cloud Storage v2.2.5
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / lib / classes / class-ajax.php

class-ajax.php in WP-Stateless – Google Cloud Storage 2.2.5, at lib/classes/class-ajax.php

571 lines 19.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AJAX Handler
4 *
5 * @since 1.0.0
6 */
7 namespace wpCloud\StatelessMedia {
8
9 if( !class_exists( 'wpCloud\StatelessMedia\Ajax' ) ) {
10
11 final class Ajax {
12
13 /**
14 * The list of wp_ajax_{name} actions
15 *
16 * @var array
17 */
18 var $actions = array(
19 'stateless_process_image',
20 'get_images_media_ids',
21 'get_other_media_ids',
22 'get_non_library_files_id',
23 'stateless_process_file',
24 'stateless_process_non_library_file',
25 'stateless_get_current_progresses',
26 'stateless_wizard_update_settings',
27 'stateless_reset_progress',
28 'stateless_get_all_fails'
29 );
30
31 /**
32 * The list of wp_ajax_nopriv_{name} actions
33 *
34 * @var array
35 */
36 var $nopriv_actions = array();
37
38 /**
39 * Init AJAX actions
40 *
41 * @author peshkov@UD
42 */
43 public function __construct(){
44
45 foreach( $this->actions as $action ) {
46 add_action( 'wp_ajax_' . $action, array( $this, 'request' ) );
47 }
48
49 foreach( $this->nopriv_actions as $action ) {
50 add_action( 'wp_ajax_nopriv_' . $action, array( $this, 'request' ) );
51 }
52
53 }
54
55 /**
56 * Handles AJAX request
57 *
58 * @author peshkov@UD
59 */
60 public function request() {
61 global $doing_manual_sync;
62
63 $response = array(
64 'message' => '',
65 'html' => '',
66 );
67
68 try{
69 $doing_manual_sync = true;
70
71 $action = $_REQUEST[ 'action' ];
72
73 /** Determine if the current class has the method to handle request */
74 if( is_callable( array( $this, 'action_'. $action ) ) ) {
75 $response = call_user_func_array( array( $this, 'action_' . $action ), array( $_REQUEST ) );
76 }
77 /** Determine if external function exists to handle request */
78 elseif ( is_callable( 'action_' . $action ) ) {
79 $response = call_user_func_array( $action, array( $_REQUEST ) );
80 }
81 elseif ( is_callable( $action ) ) {
82 $response = call_user_func_array( $action, array( $_REQUEST ) );
83 }
84 /** Oops! */
85 else {
86 throw new \Exception( __( 'Incorrect Request' ) );
87 }
88
89 } catch( \Exception $e ) {
90 wp_send_json_error( $e->getMessage() );
91 }
92
93 wp_send_json_success( $response );
94
95 }
96
97 /**
98 * Update json key to database.
99 */
100 public function action_stateless_wizard_update_settings($data) {
101 $bucket = $data['bucket'];
102 $privateKeyData = base64_decode($data['privateKeyData']);
103
104 if(current_user_can('manage_network_options') && wp_verify_nonce( $data['nonce'], 'network_update_json')){
105 if(get_site_option('sm_mode', 'disabled') == 'disabled')
106 update_site_option( 'sm_mode', 'cdn');
107 update_site_option( 'sm_bucket', $bucket);
108 update_site_option( 'sm_key_json', $privateKeyData);
109 }
110 elseif(wp_verify_nonce( $data['nonce'], 'update_json')){
111 if(get_option('sm_mode', 'disabled') == 'disabled')
112 update_option( 'sm_mode', 'cdn');
113 update_option( 'sm_bucket', $bucket);
114 update_option( 'sm_key_json', $privateKeyData);
115 }
116
117 ud_get_stateless_media()->flush_transients();
118 wp_send_json(array('success' => true));
119 }
120
121 /**
122 * Fail over to image URL if not found on disk
123 * In case image not available on both local and bucket
124 * try to pull image from image URL in case it is accessible by some sort of proxy.
125 *
126 * @param:
127 * $url (int/string): URL of the image.
128 * $save_to (string): Path where to save the image.
129 *
130 * @return bool|int
131 * @throws \Exception
132 */
133 public function get_attachment_if_exist($url, $save_to){
134 if(is_int($url))
135 $url = wp_get_attachment_url($url);
136
137 $response = wp_remote_get( $url );
138 if ( !is_wp_error($response) && is_array( $response ) ) {
139 if(!empty($response['response']['code']) && $response['response']['code'] == 200){
140 try{
141 if(wp_mkdir_p(dirname($save_to))){
142 return file_put_contents($save_to, $response['body']);
143 }
144 }
145 catch(\Exception $e){
146 throw $e;
147 }
148 }
149 }
150 return false;
151 }
152
153 /**
154 * Regenerate image sizes.
155 */
156 public function action_stateless_process_image() {
157
158 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
159 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
160 }
161
162 @error_reporting( 0 );
163
164 $id = (int) $_REQUEST['id'];
165 $image = get_post( $id );
166
167 if ( ! $image || 'attachment' != $image->post_type || 'image/' != substr( $image->post_mime_type, 0, 6 ) )
168 throw new \Exception( sprintf( __( 'Failed resize: %s is an invalid image ID.', ud_get_stateless_media()->domain ), esc_html( $_REQUEST['id'] ) ) );
169
170 if ( ! current_user_can( 'manage_options' ) )
171 throw new \Exception( __( "Your user account doesn't have permission to resize images", ud_get_stateless_media()->domain ) );
172
173 $fullsizepath = get_attached_file( $image->ID );
174
175 // If no file found
176 if ( false === $fullsizepath || ! file_exists( $fullsizepath ) ) {
177 $upload_dir = wp_upload_dir();
178
179 // Try get it and save
180 $result_code = ud_get_stateless_media()->get_client()->get_media( apply_filters( 'wp_stateless_file_name', str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath )), true, $fullsizepath );
181
182 if ( $result_code !== 200 ) {
183 if(!$this->get_attachment_if_exist($image->ID, $fullsizepath)){ // Save file to local from proxy.
184 $this->store_failed_attachment( $image->ID, 'images' );
185 throw new \Exception(sprintf(__('Both local and remote files are missing. Unable to resize. (%s)', ud_get_stateless_media()->domain), $image->guid));
186 }
187 }
188 }
189
190 @set_time_limit( -1 );
191
192 $metadata = wp_generate_attachment_metadata( $image->ID, $fullsizepath );
193
194 if(get_post_mime_type($image->ID) !== 'image/svg+xml'){
195 if ( is_wp_error( $metadata ) ) {
196 $this->store_failed_attachment( $image->ID, 'images' );
197 throw new \Exception($metadata->get_error_message());
198 }
199
200 if ( empty( $metadata ) ) {
201 $this->store_failed_attachment( $image->ID, 'images' );
202 throw new \Exception(sprintf( __('No metadata generated for %1$s (ID %2$s).', ud_get_stateless_media()->domain), esc_html( get_the_title( $image->ID ) ), $image->ID));
203 }
204 }
205
206 // If this fails, then it just means that nothing was changed (old value == new value)
207 wp_update_attachment_metadata( $image->ID, $metadata );
208
209 $this->store_current_progress( 'images', $id );
210 $this->maybe_fix_failed_attachment( 'images', $image->ID );
211 do_action( 'sm:synced::image', $id, $metadata);
212
213 return sprintf( __( '%1$s (ID %2$s) was successfully resized in %3$s seconds.', ud_get_stateless_media()->domain ), esc_html( get_the_title( $image->ID ) ), $image->ID, timer_stop() );
214 }
215
216 /**
217 * @return string
218 * @throws \Exception
219 */
220 public function action_stateless_process_file() {
221 @error_reporting( 0 );
222
223 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
224 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
225 }
226
227 $id = (int) $_REQUEST['id'];
228 $file = get_post( $id );
229
230 if ( ! $file || 'attachment' != $file->post_type )
231 throw new \Exception( sprintf( __( 'Attachment not found: %s is an invalid file ID.', ud_get_stateless_media()->domain ), esc_html( $id ) ) );
232
233 if ( ! current_user_can( 'manage_options' ) )
234 throw new \Exception( __( "You are not allowed to do this.", ud_get_stateless_media()->domain ) );
235
236 $fullsizepath = get_attached_file( $file->ID );
237 $local_file_exists = file_exists( $fullsizepath );
238
239 if ( false === $fullsizepath || ! $local_file_exists ) {
240 $upload_dir = wp_upload_dir();
241
242 // Try get it and save
243 $result_code = ud_get_stateless_media()->get_client()->get_media( str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ), true, $fullsizepath );
244
245 if ( $result_code !== 200 ) {
246 if(!$this->get_attachment_if_exist($file->ID, $fullsizepath)){ // Save file to local from proxy.
247 $this->store_failed_attachment( $file->ID, 'other' );
248 throw new \Exception(sprintf(__('File not found (%s)', ud_get_stateless_media()->domain), $file->guid));
249 }
250 else{
251 $local_file_exists = true;
252 }
253 }
254 else{
255 $local_file_exists = true;
256 }
257 }
258
259 if($local_file_exists){
260 $upload_dir = wp_upload_dir();
261
262 if ( !ud_get_stateless_media()->get_client()->media_exists( str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ) ) ) {
263
264 @set_time_limit( -1 );
265
266 $metadata = wp_generate_attachment_metadata( $file->ID, $fullsizepath );
267
268 if ( is_wp_error( $metadata ) ) {
269 $this->store_failed_attachment( $file->ID, 'other' );
270 throw new \Exception($metadata->get_error_message());
271 }
272
273 wp_update_attachment_metadata( $file->ID, $metadata );
274 do_action( 'sm:synced::nonImage', $id, $metadata);
275
276 }
277 else{
278 // Stateless mode: we don't need the local version.
279 if(ud_get_stateless_media()->get( 'sm.mode' ) === 'stateless'){
280 unlink($fullsizepath);
281 }
282 }
283
284 }
285
286 $this->store_current_progress( 'other', $id );
287 $this->maybe_fix_failed_attachment( 'other', $file->ID );
288
289 return sprintf( __( '%1$s (ID %2$s) was successfully synchronised in %3$s seconds.', ud_get_stateless_media()->domain ), esc_html( get_the_title( $file->ID ) ), $file->ID, timer_stop() );
290 }
291
292 /**
293 * @return string
294 * @throws \Exception
295 * @todo Show error when file not exist on both local and gcs.
296 */
297 public function action_stateless_process_non_library_file() {
298 @error_reporting( 0 );
299
300 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
301 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
302 }
303
304 $upload_dir = wp_upload_dir();
305 $client = ud_get_stateless_media()->get_client();
306
307 $file_path = trim($_REQUEST['file_path'], '/');
308 $fullsizepath = $upload_dir['basedir'] . '/' . $file_path;
309
310 if ( ! current_user_can( 'manage_options' ) )
311 throw new \Exception( __( "You are not allowed to do this.", ud_get_stateless_media()->domain ) );
312
313
314 do_action( 'sm:sync::syncFile', $file_path, $fullsizepath, true);
315
316 // $this->store_current_progress( 'other', $file_path );
317 // $this->maybe_fix_failed_attachment( 'other', $file_path );
318
319 return sprintf( __( '%1$s (ID %2$s) was successfully synchronised in %3$s seconds.', ud_get_stateless_media()->domain ), esc_html( get_the_title( $file_path ) ), $file_path, timer_stop() );
320 }
321
322 /**
323 * Returns IDs of images media objects
324 */
325 public function action_get_images_media_ids() {
326 global $wpdb;
327
328 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
329 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
330 }
331
332 if ( ! $images = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%' ORDER BY ID DESC" ) ) {
333 throw new \Exception( __('No images media objects found.', ud_get_stateless_media()->domain) );
334 }
335
336 $continue = false;
337 $start_from = 0;
338 if ( isset( $_REQUEST['continue'] ) ) {
339 $continue = (bool) $_REQUEST['continue'];
340 $start_from = isset( $_REQUEST['start_from'] ) ? (int) $_REQUEST['start_from'] : 0;
341 }
342
343 return $this->get_non_processed_media_ids( 'images', $images, $continue, $start_from );
344 }
345
346 /**
347 * Returns IDs of images media objects
348 */
349 public function action_get_other_media_ids() {
350 global $wpdb;
351
352 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
353 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
354 }
355
356 if ( ! $files = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type NOT LIKE 'image/%' ORDER BY ID DESC" ) ) {
357 throw new \Exception( __('No files found.', ud_get_stateless_media()->domain) );
358 }
359
360 $continue = false;
361 $start_from = 0;
362 if ( isset( $_REQUEST['continue'] ) ) {
363 $continue = (bool) $_REQUEST['continue'];
364 $start_from = isset( $_REQUEST['start_from'] ) ? (int) $_REQUEST['start_from'] : 0;
365 }
366
367 return $this->get_non_processed_media_ids( 'other', $files, $continue, $start_from );
368 }
369
370 /**
371 * Returns IDs of non media library files
372 * Return files to be manually sync from sync tab.
373 */
374 public function action_get_non_library_files_id() {
375 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
376 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
377 }
378
379 $files = apply_filters( 'sm:sync::nonMediaFiles', array() );
380 if(empty($files)){
381 throw new \Exception( __('', ud_get_stateless_media()->domain) );
382 }
383 return array_values(array_unique($files));
384 }
385
386 /**
387 * Returns current progress storage for all modes (to check whether there is something to continue in JS)
388 */
389 public function action_stateless_get_current_progresses() {
390 return array(
391 'images' => $this->retrieve_current_progress( 'images' ),
392 'other' => $this->retrieve_current_progress( 'other' ),
393 );
394 }
395
396 /**
397 * @return array
398 */
399 public function action_stateless_get_all_fails() {
400 return array(
401 'images' => $this->get_fails( 'images' ),
402 'other' => $this->get_fails( 'other' )
403 );
404 }
405
406 /**
407 * Get_fails
408 *
409 * @param $mode
410 * @return mixed|void
411 */
412 private function get_fails( $mode ) {
413 if ( $mode !== 'other' ) {
414 $mode = 'images';
415 }
416
417 return get_option( 'wp_stateless_failed_' . $mode );
418 }
419
420 /**
421 * Resets the current progress for a specific mode.
422 */
423 public function action_stateless_reset_progress() {
424 $mode = 'images';
425 if ( isset( $_REQUEST['mode'] ) && 'other' === $_REQUEST['mode'] ) {
426 $mode = 'other';
427 }
428
429 $this->reset_current_progress( $mode );
430
431 return true;
432 }
433
434 /**
435 * Get_non_processed_media_ids
436 *
437 * @param $mode
438 * @param $files
439 * @param bool $continue
440 * @return array
441 * @throws \Exception
442 */
443 private function get_non_processed_media_ids( $mode, $files, $continue = false, $start_from = 0 ) {
444 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
445 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
446 }
447
448 if ( $continue ) {
449 $progress = $this->retrieve_current_progress( $mode );
450
451 if ( false !== $progress ) {
452 if($start_from && $start_from != 0){
453 // adding 1 because we subtracted 1 in js code for presentation.
454 $progress[1] = $start_from + 1;
455 }
456 $ids = array();
457 foreach ( $files as $file ) {
458 $id = (int) $file->ID;
459 // only include IDs that have not been processed yet
460 if ( $id > $progress[0] || $id < $progress[1] ) {
461 $ids[] = $id;
462 }
463 }
464 return $ids;
465 }
466 }
467
468 $this->reset_current_progress( $mode );
469
470 $ids = array();
471 foreach ( $files as $file )
472 $ids[] = (int)$file->ID;
473
474 return $ids;
475 }
476
477 /**
478 * @param $mode
479 * @param $id
480 */
481 private function store_current_progress( $mode, $id ) {
482 if ( $mode !== 'other' ) {
483 $mode = 'images';
484 }
485
486 $first_processed = get_option( 'wp_stateless_' . $mode . '_first_processed' );
487 if ( ! $first_processed ) {
488 update_option( 'wp_stateless_' . $mode . '_first_processed', $id );
489 }
490 $last_processed = get_option( 'wp_stateless_' . $mode . '_last_processed' );
491 if ( ! $last_processed || $id < (int) $last_processed ) {
492 update_option( 'wp_stateless_' . $mode . '_last_processed', $id );
493 }
494 }
495
496 /**
497 * @param $attachment_id
498 * @param $mode
499 */
500 private function store_failed_attachment( $attachment_id, $mode ) {
501 if ( $mode !== 'other' ) {
502 $mode = 'images';
503 }
504
505 $fails = get_option( 'wp_stateless_failed_' . $mode );
506 if ( !empty( $fails ) && is_array( $fails ) ) {
507 if ( !in_array( $attachment_id, $fails ) ) {
508 $fails[] = $attachment_id;
509 }
510 } else {
511 $fails = array( $attachment_id );
512 }
513
514 update_option( 'wp_stateless_failed_' . $mode, $fails );
515 }
516
517 /**
518 * @param $mode
519 * @param $attachment_id
520 */
521 private function maybe_fix_failed_attachment( $mode, $attachment_id ) {
522 $fails = get_option( 'wp_stateless_failed_' . $mode );
523
524 if ( !empty( $fails ) && is_array( $fails ) ) {
525 if ( in_array( $attachment_id, $fails ) ) {
526 foreach (array_keys($fails, $attachment_id) as $key) {
527 unset($fails[$key]);
528 }
529 }
530 }
531
532 update_option( 'wp_stateless_failed_' . $mode, $fails );
533 }
534
535 /**
536 * @param $mode
537 * @return array|bool
538 */
539 private function retrieve_current_progress( $mode ) {
540 if ( $mode !== 'other' ) {
541 $mode = 'images';
542 }
543
544 $first_processed = get_option( 'wp_stateless_' . $mode . '_first_processed' );
545 $last_processed = get_option( 'wp_stateless_' . $mode . '_last_processed' );
546
547 if ( ! $first_processed || ! $last_processed ) {
548 return false;
549 }
550
551 return array( (int) $first_processed, (int) $last_processed );
552 }
553
554 /**
555 * @param $mode
556 */
557 private function reset_current_progress( $mode ) {
558 if ( $mode !== 'other' ) {
559 $mode = 'images';
560 }
561
562 delete_option( 'wp_stateless_' . $mode . '_first_processed' );
563 delete_option( 'wp_stateless_' . $mode . '_last_processed' );
564 }
565
566 }
567
568 }
569
570 }
571