PluginProbe
WP-Stateless – Google Cloud Storage / 2.3.2
WP-Stateless – Google Cloud Storage v2.3.2
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.3.2, at lib/classes/class-ajax.php

573 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 //
193 do_action( 'sm:pre::synced::image', $id);
194 $metadata = wp_generate_attachment_metadata( $image->ID, $fullsizepath );
195
196 if(get_post_mime_type($image->ID) !== 'image/svg+xml'){
197 if ( is_wp_error( $metadata ) ) {
198 $this->store_failed_attachment( $image->ID, 'images' );
199 throw new \Exception($metadata->get_error_message());
200 }
201
202 if ( empty( $metadata ) ) {
203 $this->store_failed_attachment( $image->ID, 'images' );
204 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));
205 }
206 }
207
208 // If this fails, then it just means that nothing was changed (old value == new value)
209 wp_update_attachment_metadata( $image->ID, $metadata );
210
211 $this->store_current_progress( 'images', $id );
212 $this->maybe_fix_failed_attachment( 'images', $image->ID );
213 do_action( 'sm:synced::image', $id, $metadata);
214
215 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() );
216 }
217
218 /**
219 * @return string
220 * @throws \Exception
221 */
222 public function action_stateless_process_file() {
223 @error_reporting( 0 );
224
225 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
226 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
227 }
228
229 $id = (int) $_REQUEST['id'];
230 $file = get_post( $id );
231
232 if ( ! $file || 'attachment' != $file->post_type )
233 throw new \Exception( sprintf( __( 'Attachment not found: %s is an invalid file ID.', ud_get_stateless_media()->domain ), esc_html( $id ) ) );
234
235 if ( ! current_user_can( 'manage_options' ) )
236 throw new \Exception( __( "You are not allowed to do this.", ud_get_stateless_media()->domain ) );
237
238 $fullsizepath = get_attached_file( $file->ID );
239 $local_file_exists = file_exists( $fullsizepath );
240
241 if ( false === $fullsizepath || ! $local_file_exists ) {
242 $upload_dir = wp_upload_dir();
243
244 // Try get it and save
245 $result_code = ud_get_stateless_media()->get_client()->get_media( str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ), true, $fullsizepath );
246
247 if ( $result_code !== 200 ) {
248 if(!$this->get_attachment_if_exist($file->ID, $fullsizepath)){ // Save file to local from proxy.
249 $this->store_failed_attachment( $file->ID, 'other' );
250 throw new \Exception(sprintf(__('File not found (%s)', ud_get_stateless_media()->domain), $file->guid));
251 }
252 else{
253 $local_file_exists = true;
254 }
255 }
256 else{
257 $local_file_exists = true;
258 }
259 }
260
261 if($local_file_exists){
262 $upload_dir = wp_upload_dir();
263
264 if ( !ud_get_stateless_media()->get_client()->media_exists( str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ) ) ) {
265
266 @set_time_limit( -1 );
267
268 $metadata = wp_generate_attachment_metadata( $file->ID, $fullsizepath );
269
270 if ( is_wp_error( $metadata ) ) {
271 $this->store_failed_attachment( $file->ID, 'other' );
272 throw new \Exception($metadata->get_error_message());
273 }
274
275 wp_update_attachment_metadata( $file->ID, $metadata );
276 do_action( 'sm:synced::nonImage', $id, $metadata);
277
278 }
279 else{
280 // Stateless mode: we don't need the local version.
281 if(ud_get_stateless_media()->get( 'sm.mode' ) === 'stateless'){
282 unlink($fullsizepath);
283 }
284 }
285
286 }
287
288 $this->store_current_progress( 'other', $id );
289 $this->maybe_fix_failed_attachment( 'other', $file->ID );
290
291 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() );
292 }
293
294 /**
295 * @return string
296 * @throws \Exception
297 * @todo Show error when file not exist on both local and gcs.
298 */
299 public function action_stateless_process_non_library_file() {
300 @error_reporting( 0 );
301
302 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
303 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
304 }
305
306 $upload_dir = wp_upload_dir();
307 $client = ud_get_stateless_media()->get_client();
308
309 $file_path = trim($_REQUEST['file_path'], '/');
310 $fullsizepath = $upload_dir['basedir'] . '/' . $file_path;
311
312 if ( ! current_user_can( 'manage_options' ) )
313 throw new \Exception( __( "You are not allowed to do this.", ud_get_stateless_media()->domain ) );
314
315
316 do_action( 'sm:sync::syncFile', $file_path, $fullsizepath, true);
317
318 // $this->store_current_progress( 'other', $file_path );
319 // $this->maybe_fix_failed_attachment( 'other', $file_path );
320
321 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() );
322 }
323
324 /**
325 * Returns IDs of images media objects
326 */
327 public function action_get_images_media_ids() {
328 global $wpdb;
329
330 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
331 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
332 }
333
334 if ( ! $images = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%' ORDER BY ID DESC" ) ) {
335 throw new \Exception( __('No images media objects found.', ud_get_stateless_media()->domain) );
336 }
337
338 $continue = false;
339 $start_from = 0;
340 if ( isset( $_REQUEST['continue'] ) ) {
341 $continue = (bool) $_REQUEST['continue'];
342 $start_from = isset( $_REQUEST['start_from'] ) ? (int) $_REQUEST['start_from'] : 0;
343 }
344
345 return $this->get_non_processed_media_ids( 'images', $images, $continue, $start_from );
346 }
347
348 /**
349 * Returns IDs of images media objects
350 */
351 public function action_get_other_media_ids() {
352 global $wpdb;
353
354 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
355 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
356 }
357
358 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" ) ) {
359 throw new \Exception( __('No files found.', ud_get_stateless_media()->domain) );
360 }
361
362 $continue = false;
363 $start_from = 0;
364 if ( isset( $_REQUEST['continue'] ) ) {
365 $continue = (bool) $_REQUEST['continue'];
366 $start_from = isset( $_REQUEST['start_from'] ) ? (int) $_REQUEST['start_from'] : 0;
367 }
368
369 return $this->get_non_processed_media_ids( 'other', $files, $continue, $start_from );
370 }
371
372 /**
373 * Returns IDs of non media library files
374 * Return files to be manually sync from sync tab.
375 */
376 public function action_get_non_library_files_id() {
377 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
378 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
379 }
380
381 $files = apply_filters( 'sm:sync::nonMediaFiles', array() );
382 if(empty($files)){
383 throw new \Exception( __('', ud_get_stateless_media()->domain) );
384 }
385 return array_values(array_unique($files));
386 }
387
388 /**
389 * Returns current progress storage for all modes (to check whether there is something to continue in JS)
390 */
391 public function action_stateless_get_current_progresses() {
392 return array(
393 'images' => $this->retrieve_current_progress( 'images' ),
394 'other' => $this->retrieve_current_progress( 'other' ),
395 );
396 }
397
398 /**
399 * @return array
400 */
401 public function action_stateless_get_all_fails() {
402 return array(
403 'images' => $this->get_fails( 'images' ),
404 'other' => $this->get_fails( 'other' )
405 );
406 }
407
408 /**
409 * Get_fails
410 *
411 * @param $mode
412 * @return mixed|void
413 */
414 private function get_fails( $mode ) {
415 if ( $mode !== 'other' ) {
416 $mode = 'images';
417 }
418
419 return get_option( 'wp_stateless_failed_' . $mode );
420 }
421
422 /**
423 * Resets the current progress for a specific mode.
424 */
425 public function action_stateless_reset_progress() {
426 $mode = 'images';
427 if ( isset( $_REQUEST['mode'] ) && 'other' === $_REQUEST['mode'] ) {
428 $mode = 'other';
429 }
430
431 $this->reset_current_progress( $mode );
432
433 return true;
434 }
435
436 /**
437 * Get_non_processed_media_ids
438 *
439 * @param $mode
440 * @param $files
441 * @param bool $continue
442 * @return array
443 * @throws \Exception
444 */
445 private function get_non_processed_media_ids( $mode, $files, $continue = false, $start_from = 0 ) {
446 if(ud_get_stateless_media()->is_connected_to_gs() !== true){
447 throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) );
448 }
449
450 if ( $continue ) {
451 $progress = $this->retrieve_current_progress( $mode );
452
453 if ( false !== $progress ) {
454 if($start_from && $start_from != 0){
455 // adding 1 because we subtracted 1 in js code for presentation.
456 $progress[1] = $start_from + 1;
457 }
458 $ids = array();
459 foreach ( $files as $file ) {
460 $id = (int) $file->ID;
461 // only include IDs that have not been processed yet
462 if ( $id > $progress[0] || $id < $progress[1] ) {
463 $ids[] = $id;
464 }
465 }
466 return $ids;
467 }
468 }
469
470 $this->reset_current_progress( $mode );
471
472 $ids = array();
473 foreach ( $files as $file )
474 $ids[] = (int)$file->ID;
475
476 return $ids;
477 }
478
479 /**
480 * @param $mode
481 * @param $id
482 */
483 private function store_current_progress( $mode, $id ) {
484 if ( $mode !== 'other' ) {
485 $mode = 'images';
486 }
487
488 $first_processed = get_option( 'wp_stateless_' . $mode . '_first_processed' );
489 if ( ! $first_processed ) {
490 update_option( 'wp_stateless_' . $mode . '_first_processed', $id );
491 }
492 $last_processed = get_option( 'wp_stateless_' . $mode . '_last_processed' );
493 if ( ! $last_processed || $id < (int) $last_processed ) {
494 update_option( 'wp_stateless_' . $mode . '_last_processed', $id );
495 }
496 }
497
498 /**
499 * @param $attachment_id
500 * @param $mode
501 */
502 private function store_failed_attachment( $attachment_id, $mode ) {
503 if ( $mode !== 'other' ) {
504 $mode = 'images';
505 }
506
507 $fails = get_option( 'wp_stateless_failed_' . $mode );
508 if ( !empty( $fails ) && is_array( $fails ) ) {
509 if ( !in_array( $attachment_id, $fails ) ) {
510 $fails[] = $attachment_id;
511 }
512 } else {
513 $fails = array( $attachment_id );
514 }
515
516 update_option( 'wp_stateless_failed_' . $mode, $fails );
517 }
518
519 /**
520 * @param $mode
521 * @param $attachment_id
522 */
523 private function maybe_fix_failed_attachment( $mode, $attachment_id ) {
524 $fails = get_option( 'wp_stateless_failed_' . $mode );
525
526 if ( !empty( $fails ) && is_array( $fails ) ) {
527 if ( in_array( $attachment_id, $fails ) ) {
528 foreach (array_keys($fails, $attachment_id) as $key) {
529 unset($fails[$key]);
530 }
531 }
532 }
533
534 update_option( 'wp_stateless_failed_' . $mode, $fails );
535 }
536
537 /**
538 * @param $mode
539 * @return array|bool
540 */
541 private function retrieve_current_progress( $mode ) {
542 if ( $mode !== 'other' ) {
543 $mode = 'images';
544 }
545
546 $first_processed = get_option( 'wp_stateless_' . $mode . '_first_processed' );
547 $last_processed = get_option( 'wp_stateless_' . $mode . '_last_processed' );
548
549 if ( ! $first_processed || ! $last_processed ) {
550 return false;
551 }
552
553 return array( (int) $first_processed, (int) $last_processed );
554 }
555
556 /**
557 * @param $mode
558 */
559 private function reset_current_progress( $mode ) {
560 if ( $mode !== 'other' ) {
561 $mode = 'images';
562 }
563
564 delete_option( 'wp_stateless_' . $mode . '_first_processed' );
565 delete_option( 'wp_stateless_' . $mode . '_last_processed' );
566 }
567
568 }
569
570 }
571
572 }
573