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

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