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

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