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-bootstrap.php

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

1,463 lines 55.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Bootstrap
4 *
5 * @since 0.2.0
6 */
7 namespace wpCloud\StatelessMedia {
8
9 if( !class_exists( 'wpCloud\StatelessMedia\Bootstrap' ) ) {
10
11 final class Bootstrap extends \UsabilityDynamics\WP\Bootstrap_Plugin {
12
13 /**
14 * Google Storage Client
15 * Use $this->get_client()
16 *
17 * @var \wpCloud\StatelessMedia\GS_CLient
18 */
19 private $client;
20
21 /**
22 * Plugin core version.
23 *
24 * @static
25 * @property $version
26 * @type {Object}
27 */
28 public static $version = '2.1';
29
30 /**
31 * Singleton Instance Reference.
32 *
33 * @protected
34 * @static
35 * @property $instance
36 * @type \wpCloud\StatelessMedia\Bootstrap object
37 */
38 protected static $instance = null;
39
40
41 /**
42 * Constructor
43 * Attention: MUST NOT BE CALLED DIRECTLY! USE get_instance() INSTEAD!
44 *
45 * @author peshkov@UD
46 */
47 protected function __construct( $args ) {
48 parent::__construct( $args );
49
50 //** Define our Admin Notices handler object */
51 $this->errors = new Errors( array_merge( $args, array(
52 'type' => $this->type
53 ) ) );
54
55 // Initialize compatibility modules.
56 add_action( 'plugins_loaded', function(){
57 new Module();
58 });
59 }
60
61 /**
62 * Instantiate class.
63 */
64 public function init() {
65
66 /**
67 * Copied from wp-property
68 * Duplicates UsabilityDynamics\WP\Bootstrap_Plugin::load_textdomain();
69 *
70 * There is a bug with localization in lib-wp-bootstrap 1.1.3 and lower.
71 * So we load textdomain here again, in case old version lib-wp-bootstrap is being loaded
72 * by another plugin.
73 *
74 * @since 1.9.1
75 */
76 load_plugin_textdomain($this->domain, false, dirname(plugin_basename($this->boot_file)) . '/static/languages/');
77
78 // Parse feature falgs, set constants.
79 $this->parse_feature_flags();
80
81 new SyncNonMedia();
82
83 /**
84 * Define settings and UI.
85 *
86 * Example:
87 *
88 * Get option
89 * $this->get( 'sm.client_id' )
90 *
91 * Manually Update/Add option
92 * $this->set( 'sm.client_id', 'zxcvv12adffse' );
93 */
94 $this->settings = new Settings();
95
96 // Invoke REST API
97 add_action( 'rest_api_init', array( $this, 'api_init' ) );
98
99 /**
100 * Register SM metaboxes
101 */
102 add_action( 'admin_init', array( $this, 'register_metaboxes' ) );
103
104 /**
105 * Init hook
106 */
107 add_action( 'admin_init', array( $this, 'admin_init' ) );
108
109 /**
110 * Add custom actions to media rows
111 */
112 add_filter( 'media_row_actions', array( $this, 'add_custom_row_actions' ), 10, 3 );
113
114 /**
115 * Handle switch blog properly.
116 */
117 add_action( 'switch_blog', array( $this, 'on_switch_blog' ), 10, 2 );
118
119 /**
120 * Filter for getting stateless settings
121 */
122 add_filter( 'stateless::get_settings', array( $this, 'get_settings' ), 10);
123
124 /**
125 * Init AJAX jobs
126 */
127 new Ajax();
128
129 /**
130 * Load WP-CLI Commands
131 */
132 if( defined( 'WP_CLI' ) && WP_CLI ) {
133 include_once($this->path('lib/cli/class-sm-cli-command.php', 'dir'));
134 }
135
136 $this->is_network_detected();
137
138 /**
139 * Add scripts
140 */
141 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
142
143 /**
144 * Hashify file name if option is enabled
145 */
146 if ( $this->get( 'sm.hashify_file_name' ) == 'true' ) {
147 add_filter('sanitize_file_name', array( 'wpCloud\StatelessMedia\Utility', 'randomize_filename' ), 10);
148 }
149
150 /* Initialize plugin only if Mode is not 'disabled'. */
151 if ( $this->get( 'sm.mode' ) !== 'disabled' ) {
152
153 /**
154 * Override Cache Control is option is enabled
155 */
156 $cacheControl = trim($this->get( 'sm.cache_control' ));
157 if ( !empty($cacheControl) ) {
158 add_filter( 'sm:item:cacheControl', array( $this, 'override_cache_control' ) );
159 }
160
161 /**
162 * Determine if we have issues with connection to Google Storage Bucket
163 * if SM is not disabled.
164 */
165 $is_connected = $this->is_connected_to_gs();
166
167 if ( is_wp_error( $is_connected ) ) {
168 $this->errors->add( $is_connected->get_error_message() );
169 }
170
171 if ( $googleSDKVersionConflictError = get_transient( "wp_stateless_google_sdk_conflict" ) ) {
172 $this->errors->add( $googleSDKVersionConflictError, 'warning' );
173 }
174
175 // To prevent fatal errors for users who use PHP 5.5 or less.
176 if( version_compare(PHP_VERSION, '5.5', '<') ) {
177 $this->errors->add( sprintf( __( 'The plugin requires PHP %s or higher. You current PHP version %s is too old.', ud_get_stateless_media()->domain ), '<b>5.5</b>', '<b>' . PHP_VERSION . '</b>' ) );
178 }
179
180 /** Temporary fix to WP 4.4 srcset feature **/
181 //add_filter( 'max_srcset_image_width', function(){return 1;} );
182
183 /**
184 * Carry on only if we do not have errors.
185 */
186 if( !$this->has_errors() ) {
187
188 if( $this->get( 'sm.mode' ) === 'cdn' || $this->get( 'sm.mode' ) === 'stateless' ) {
189 add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 20, 3 );
190 add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 20, 2 );
191 add_filter( 'attachment_url_to_postid', array( $this, 'attachment_url_to_postid' ), 20, 2 );
192
193 if ( $this->get( 'sm.body_rewrite' ) == 'true' || $this->get( 'sm.body_rewrite' ) == 'enable_editor' ) {
194 add_filter( 'the_content', array( $this, 'the_content_filter' ), 99 );
195 }
196
197 if ( $this->get( 'sm.body_rewrite' ) == 'true' || $this->get( 'sm.body_rewrite' ) == 'enable_meta' ) {
198 add_filter( 'get_post_metadata', array( $this, 'post_metadata_filter' ), 2, 4 );
199 }
200
201 add_filter( 'wp_stateless_bucket_link', array( $this, 'wp_stateless_bucket_link' ) );
202 }
203
204 if ( $root_dir = $this->get( 'sm.root_dir' ) ) {
205 if ( trim( $root_dir, '/ ' ) !== '' ) { // Remove any forward slash and empty space.
206 add_filter( 'wp_stateless_file_name', array( $this, 'handle_root_dir' ) );
207 }
208 }
209
210 /**
211 * Rewrite Image URLS
212 */
213 add_filter( 'image_downsize', array( $this, 'image_downsize' ), 99, 3 );
214 add_filter( 'wp_calculate_image_srcset', array($this, 'wp_calculate_image_srcset'), 10, 5 );
215
216 /**
217 * Extends metadata by adding GS information.
218 */
219 add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
220
221 /**
222 * Add/Edit Media
223 *
224 * Once added or edited we can get into Attachment ID then get all image sizes and sync them with GS
225 * We can't use this. That's prevent removing this filter.
226 */
227 add_filter( 'wp_update_attachment_metadata', array( 'wpCloud\StatelessMedia\Utility', 'add_media' ), 999, 2 );
228
229 /**
230 * Upload the full size image first.
231 *
232 */
233 if(!defined('WP_STATELESS_MEDIA_DISABLE_FULL_IMAGE_FIRST') || WP_STATELESS_MEDIA_DISABLE_FULL_IMAGE_FIRST != true){
234 add_filter( 'intermediate_image_sizes_advanced', array( $this, 'before_intermediate_image_sizes' ), 10, 2 );
235 }
236
237 /**
238 * Add Media
239 *
240 * Once added we can get into Attachment ID then get all image sizes and sync them with GS
241 */
242 // add_filter( 'wp_generate_attachment_metadata', array( $this, 'add_media' ), 100, 2 );
243
244 if ( $this->get( 'sm.delete_remote' ) == 'true' ) {
245 /**
246 * On physical file deletion we remove any from GS
247 */
248 add_filter('delete_attachment', array($this, 'remove_media'));
249 }
250
251 // Trigger module initialization and registration.
252 do_action('sm::module::init', $this->get( 'sm' ));
253 }
254
255 }
256 }
257
258 /**
259 * Rebuild srcset from gs_link.
260 * Using calculations returned from WordPress wp_calculate_image_srcset()
261 *
262 */
263 public function wp_calculate_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id){
264 if (empty($image_meta['gs_link'])) {
265 $image_meta = wp_get_attachment_metadata($attachment_id);
266 }
267
268 if(is_array($sources) && !empty( $image_meta['gs_link'] )){
269 $gs_name = $image_meta['gs_name'];
270 // getting position of root_dir in gs_name.
271 $root_dir_pos = strpos($gs_name, $image_meta['file']);
272 // removing rood_dir from gs_name so we can compare to replace url with gs_link.
273 if($root_dir_pos !== false){
274 $gs_name = substr($gs_name, $root_dir_pos);
275 }
276
277 foreach ($sources as $width => &$image) {
278 if (!isset($image_meta['gs_name']) || empty($image_meta['gs_name'])) {
279 continue;
280 }
281
282 // If srcset includes original image src, replace it
283 if (substr_compare($image['url'], $gs_name, -strlen($gs_name)) === 0) {
284 $image['url'] = $image_meta['gs_link'];
285 // Replace all sizes
286 } elseif (isset($image_meta['sizes']) && is_array($image_meta['sizes'])) {
287 foreach ($image_meta['sizes'] as $key => $meta) {
288 if (!isset($meta['gs_name']) || empty($meta['gs_name'])) {
289 continue;
290 }
291
292 $thumb_gs_name = $meta['gs_name'];
293 // removing rood_dir from gs_name because
294 if($root_dir_pos !== false){
295 $thumb_gs_name = substr($thumb_gs_name, $root_dir_pos);
296 }
297
298 if (substr_compare($image['url'], $thumb_gs_name, -strlen($thumb_gs_name)) === 0) {
299 $image['url'] = $meta['gs_link'];
300 break;
301 }
302 }
303 }
304 }
305 }
306
307 return $sources;
308 }
309
310 /**
311 * Return gs host.
312 * If custom domain is set it's return bucket name as host,
313 * else return storage.googleapis.com as host and append bucket name at the end.
314 * @param none
315 * @return Host name to use
316 */
317 public function get_gs_host($sm = array()) {
318 $sm = $sm?$sm: $this->get( 'sm');
319 $image_host = 'https://storage.googleapis.com/';
320 $image_host .= $sm['bucket'];
321
322 $custom_domain = $sm['custom_domain'];
323 $is_ssl = strpos($custom_domain, 'https://');
324 $custom_domain = str_replace(array('http://', 'https://'), '', $custom_domain);
325 $custom_domain = trim($custom_domain, '/');
326
327 if ( !empty($sm['bucket']) && !empty($custom_domain) && $custom_domain !== 'storage.googleapis.com' && ( $is_ssl === 0 || $custom_domain != $sm['bucket'] ) ) {
328 $image_host = $is_ssl === 0 ? 'https://' : 'http://'; // bucketname will be host
329 $image_host .= $custom_domain;
330 }
331
332 return apply_filters( 'get_gs_host', $image_host, $image_host, $sm['bucket'], $is_ssl, $sm );
333 }
334
335 /**
336 * Filter for wp_stateless_bucket_link if custom domain is set.
337 * It's get attachment url and remove "storage.googleapis.com" from url.
338 * So that custom url can be used.
339 * @param $new_blog
340 * @param $prev_blog_id
341 */
342 public function wp_stateless_bucket_link($fileLink) {
343 $bucketname = $this->get( 'sm.bucket' );
344 $custom_domain = $this->get( 'sm.custom_domain' );
345 $is_ssl = strpos($custom_domain, 'https://') === 0;
346 $fileLink_is_ssl = strpos($fileLink, 'https://') === 0;
347 $custom_domain = str_replace(array('http://', 'https://'), '', $custom_domain);
348 $custom_domain = trim($custom_domain, '/');
349
350 if ( $custom_domain !== 'storage.googleapis.com' && $custom_domain == $bucketname && strpos($fileLink, $bucketname) > 8 ) {
351 $fileLink = ($is_ssl ? 'https://' : 'http://') . substr($fileLink, strpos($fileLink, $bucketname));
352 }
353 elseif( $custom_domain !== 'storage.googleapis.com' && $custom_domain == $bucketname && $fileLink_is_ssl !== $is_ssl){
354 if($is_ssl)
355 $fileLink = str_replace(array('http://', 'https://'), 'https://', $fileLink);
356 else
357 $fileLink = str_replace(array('http://', 'https://'), 'http://', $fileLink);
358 }
359 return $fileLink;
360 }
361
362 /**
363 * Return settings page url.
364 * @param $path
365 */
366 public function get_settings_page_url( $path = '' ) {
367 $url = get_admin_url( get_current_blog_id(), ( is_network_admin() ? 'network/settings.php' : 'upload.php' ) );
368 return $url . $path;
369 }
370
371 /**
372 * Get new blog settings once switched blog.
373 * @param $new_blog
374 * @param $prev_blog_id
375 */
376 public function on_switch_blog( $new_blog, $prev_blog_id ) {
377 $this->settings->refresh();
378 }
379
380 /**
381 * Get settings handler.
382 * Filling array if some settings missing.
383 *
384 * @param $settings
385 * @return
386 */
387 public function get_settings($settings) {
388
389 $settings_list = array(
390 'mode',
391 'body_rewrite',
392 'body_rewrite_types',
393 'bucket',
394 'root_dir',
395 'key_json',
396 'cache_control',
397 'delete_remote',
398 'custom_domain',
399 'organize_media',
400 'hashify_file_name'
401 );
402
403 foreach ($settings_list as $setting) {
404
405 /** If setting is already exist, just skip it */
406 if( isset( $settings[ $setting ] ) ) {
407 continue;
408 }
409
410 $value = $this->get( 'sm.' . $setting );
411
412 /** Decode json to array */
413 if( $value && is_string( $value ) && $setting === 'key_json' ) {
414 $value = json_decode( $value, true );
415 $setting = 'key';
416 }
417
418 $settings[ $setting ] = $value;
419 }
420
421 return $settings;
422 }
423
424 /**
425 * Remove all settings.
426 */
427 public function reset($network = false) {
428 $this->settings->reset($network);
429 }
430
431 /**
432 * @param $actions
433 * @param $post
434 * @param $detached
435 * @return mixed
436 */
437 public function add_custom_row_actions( $actions, $post, $detached ) {
438
439 if ( !current_user_can( 'upload_files' ) ) return $actions;
440
441 if ( $post && 'attachment' == $post->post_type && 'image/' == substr( $post->post_mime_type, 0, 6 ) ) {
442 $actions['sm_sync'] = '<a href="javascript:;" data-id="'.$post->ID.'" data-type="image" class="sm_inline_sync">' . __('Regenerate and Sync with GCS', ud_get_stateless_media()->domain) . '</a>';
443 }
444
445 if ( $post && 'attachment' == $post->post_type && 'image/' != substr( $post->post_mime_type, 0, 6 ) ) {
446 $actions['sm_sync'] = '<a href="javascript:;" data-id="'.$post->ID.'" data-type="other" class="sm_inline_sync">' . __('Sync with GCS', ud_get_stateless_media()->domain) . '</a>';
447 }
448
449 return $actions;
450
451 }
452
453 /**
454 * Register metaboxes
455 */
456 public function register_metaboxes() {
457 add_meta_box(
458 'sm-attachment-metabox',
459 __( 'Google Cloud Storage', ud_get_stateless_media()->domain ),
460 array($this, 'attachment_meta_box_callback'),
461 'attachment',
462 'side',
463 'low'
464 );
465 }
466
467 /**
468 * Define REST API.
469 *
470 * // https://usabilitydynamics-sandbox-uds-io-stateless-testing.c.rabbit.ci/wp-json/wp-stateless/v1
471 *
472 * @author potanin@UD
473 */
474 public function api_init() {
475
476 $route_namespace = 'wp-stateless/v1';
477 $api_namespace = 'wpCloud\StatelessMedia\API';
478
479 register_rest_route( $route_namespace, '/status', array(
480 'methods' => 'GET',
481 'callback' => array( $api_namespace, 'status' ),
482 ) );
483
484 register_rest_route( $route_namespace, '/jobs', array(
485 'methods' => 'GET',
486 'callback' => array( $api_namespace, 'jobs' ),
487 ) );
488
489 /**
490 * Return stateless settings.
491 *
492 * Request parameter: none
493 *
494 * Response:
495 * ok: Whether API is up or not
496 * message: Describe what is done or error message on error.
497 * settings: array of stateless settings
498 *
499 */
500 register_rest_route( $route_namespace, '/getSettings', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getSettings' ), ) );
501
502 /**
503 * Essentially for scrolling through media library to build our index.
504 *
505 * Request parameter: none
506 *
507 * Response:
508 * ok: Whether API is up or not
509 * message: Describe what is done or error message on error.
510 * settings: array of media files
511 *
512 */
513 register_rest_route( $route_namespace, '/getMediaLibrary', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getMediaLibrary' ), ) );
514
515 /**
516 * Get detailed information of media file
517 *
518 * Request parameter: none
519 *
520 * Response:
521 * ok: Whether API is up or not
522 * message: Describe what is done or error message on error.
523 * settings: media file array
524 *
525 */
526 register_rest_route( $route_namespace, '/getMediaItem', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getMediaItem' ), ) );
527
528 }
529
530 /**
531 * @param $post
532 */
533 public function attachment_meta_box_callback( $post ) {
534 ob_start();
535
536 $sm_cloud = get_post_meta( $post->ID, 'sm_cloud', 1 );
537
538 if ( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) { ?>
539
540 <?php if( !empty( $sm_cloud[ 'cacheControl' ] ) ) { ?>
541 <div class="misc-pub-cache-control hidden">
542 <?php _e( 'Cache Control:', ud_get_stateless_media()->domain ); ?> <strong><span><?php echo $sm_cloud[ 'cacheControl' ]; ?></span> </strong>
543 </div>
544 <?php } ?>
545
546 <div class="misc-pub-gs-file-link" style="margin-bottom: 15px;">
547 <label>
548 <?php _e( 'Storage Bucket URL:', ud_get_stateless_media()->domain ); ?> <a href="<?php echo $sm_cloud[ 'fileLink' ]; ?>" target="_blank" class="sm-view-link"><?php _e( '[view]' ); ?></a>
549 <input type="text" class="widefat urlfield" readonly="readonly" value="<?php echo esc_attr($sm_cloud[ 'fileLink' ]); ?>" />
550 </label>
551 </div>
552
553 <?php
554
555 if ( !empty( $sm_cloud[ 'bucket' ] ) ) {
556 ?>
557 <div class="misc-pub-gs-bucket" style="margin-bottom: 15px;">
558 <label>
559 <?php _e( 'Storage Bucket:', ud_get_stateless_media()->domain ); ?>
560 <input type="text" class="widefat urlfield" readonly="readonly" value="gs://<?php echo esc_attr($sm_cloud[ 'bucket' ]); ?>" />
561 </label>
562 </div>
563 <?php
564 }
565
566 if ( current_user_can( 'upload_files' ) ) {
567 if ( $post && 'attachment' == $post->post_type && 'image/' == substr( $post->post_mime_type, 0, 6 ) ) {
568 ?>
569 <a href="javascript:;" data-type="image" data-id="<?php echo $post->ID; ?>"
570 class="button-secondary sm_inline_sync"><?php _e('Regenerate and Sync with GCS', ud_get_stateless_media()->domain); ?></a>
571 <?php
572 }
573
574 if ( $post && 'attachment' == $post->post_type && 'image/' != substr( $post->post_mime_type, 0, 6 ) ) {
575 ?>
576 <a href="javascript:;" data-type="other" data-id="<?php echo $post->ID; ?>"
577 class="button-secondary sm_inline_sync"><?php _e('Sync with GCS', ud_get_stateless_media()->domain); ?></a>
578 <?php
579 }
580 }
581 }
582
583 echo apply_filters( 'sm::attachment::meta', ob_get_clean(), $post->ID );
584 }
585
586 /**
587 * @param $current_path
588 * @return string
589 */
590 public function handle_root_dir( $current_path ) {
591 $root_dir = $this->get( 'sm.root_dir' );
592 $root_dir = trim( $root_dir, '/ ' ); // Remove any forward slash and empty space.
593
594 if ( !empty( $root_dir ) ) {
595 return $root_dir . '/' . $current_path;
596 }
597
598 return $current_path;
599 }
600
601 /**
602 * @param $content
603 * @return mixed
604 */
605 public function the_content_filter( $content ) {
606
607 if ( $upload_data = wp_upload_dir() ) {
608
609 if ( !empty( $upload_data['baseurl'] ) && !empty( $content ) ) {
610 $baseurl = preg_replace('/https?:\/\//','',$upload_data['baseurl']);
611 $root_dir = trim( $this->get( 'sm.root_dir' ), '/ ' ); // Remove any forward slash and empty space.
612 $root_dir = !empty( $root_dir ) ? $root_dir . '/' : false;
613 $image_host = $this->get_gs_host();
614 $file_ext = $this->replaceable_file_types();
615 $content = preg_replace( '/(href|src)=(\'|")(https?:\/\/'.str_replace('/', '\/', $baseurl).')\/(.+?)('.$file_ext.')(\'|")/i',
616 '$1=$2'.$image_host.'/'.($root_dir?$root_dir:'').'$4$5$6', $content);
617 }
618 }
619
620 return $content;
621 }
622
623 /**
624 * Return file types supported by File URL Replacement.
625 *
626 */
627 public function replaceable_file_types(){
628 $types = $this->get('sm.body_rewrite_types');
629
630 // Removing extra space.
631 $types = trim($types);
632 $types = preg_replace("/\s{2,}/", ' ', $types);
633
634 $types_arr = explode(' ', $types);
635 return '\.' . implode('|\.', $types_arr);
636 }
637
638 /**
639 * Copied from https://developer.wordpress.org/reference/functions/get_metadata/
640 *
641 * @param $value null unless other filter hooked in this function.
642 * @param $object_id post id
643 * @param $meta_key
644 * @param $single
645 * @return mixed
646 */
647 public function post_metadata_filter($value, $object_id, $meta_key, $single){
648 if(empty($value)){
649 $meta_type = 'post';
650 $transient_key = "stateless_{$meta_type}_meta";
651
652 $meta_cache = wp_cache_get($object_id, $transient_key);
653 if(empty($meta_cache)){
654 $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
655
656 if ( !$meta_cache ) {
657 $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
658 $meta_cache = $meta_cache[$object_id];
659 }
660
661 foreach($meta_cache as $key => $meta){
662 $meta_cache[$key] = array_map('maybe_unserialize', $meta_cache[$key]);
663 }
664
665 $meta_cache = $this->convert_to_gs_link($meta_cache);
666 wp_cache_set($object_id, $meta_cache, $transient_key);
667 }
668
669 if ( ! $meta_key ) {
670 return $meta_cache;
671 }
672
673 if ( isset($meta_cache[$meta_key]) ) {
674 if ( $single )
675 return $meta_cache[$meta_key][0];
676 else
677 return $meta_cache[$meta_key];
678 }
679
680 // in case no metadata is found return what was passed in $value.
681 // $value most of the time is null.
682 return $value;
683 }
684
685 return $this->convert_to_gs_link($value);
686
687 }
688
689 /**
690 * Rplace all image link with gs link and return only if meta modified.
691 * @param $meta
692 * @return mixed or null when not changed.
693 */
694 public function convert_to_gs_link($meta){
695 $updated = $meta;
696 if ( $meta && $upload_data = wp_upload_dir() ) {
697 if ( !empty( $upload_data['baseurl'] ) && !empty( $meta ) ) {
698 $baseurl = preg_replace('/https?:\/\//','',$upload_data['baseurl']);
699 $root_dir = trim( $this->get( 'sm.root_dir' ), '/ ' ); // Remove any forward slash and empty space.
700 $root_dir = !empty( $root_dir ) ? $root_dir . '/': false;
701 $image_host = $this->get_gs_host().'/'.($root_dir?$root_dir:'');
702 $file_ext = $this->replaceable_file_types();
703 $updated = $this->_convert_to_gs_link($meta, $image_host, $baseurl, $file_ext);
704 }
705 }
706
707 if($updated == $meta){
708 return null; // Not changed.
709 }
710 return $updated;
711 }
712
713 /**
714 * Rplace all image link with gs link
715 * @param $meta
716 * @return mixed
717 */
718 public function _convert_to_gs_link($meta, $image_host, $baseurl, $file_ext){
719 if(is_array($meta)){
720 foreach ($meta as $key => $value) {
721 $meta[$key] = $this->_convert_to_gs_link($value, $image_host, $baseurl, $file_ext);
722 }
723 return $meta;
724 } elseif (is_object($meta) && $meta instanceof \stdClass ) {
725 foreach (get_object_vars($meta) as $key => $value) {
726 $meta->{$key} = $this->_convert_to_gs_link($value, $image_host, $baseurl, $file_ext);
727 }
728 return $meta;
729 } elseif(is_string($meta)){
730 return preg_replace( '/(https?:\/\/'.str_replace('/', '\/', $baseurl).')\/(.+?)(' . $file_ext . ')/i', $image_host.'$2$3', $meta);
731 }
732
733 return $meta;
734 }
735
736
737 /**
738 * @param $links
739 * @param $file
740 * @return mixed
741 */
742 public function plugin_action_links( $links, $file ) {
743
744 if ($file == plugin_basename( dirname( __DIR__ ) . '/wp-stateless-media.php' ) ) {
745 $settings_link = '<a href="'. '' .'">'.__( 'Settings' , 'ssd').'</a>';
746 array_unshift( $links, $settings_link );
747 }
748
749 if ($file == plugin_basename( dirname( __DIR__ ) . '/wp-stateless.php' ) ) {
750 $settings_link = '<a href="'. '' .'">'.__( 'Settings' , 'ssd').'</a>';
751 array_unshift( $links, $settings_link );
752 }
753
754 return $links;
755 }
756
757 /**
758 * Determines if plugin is loaded via mu-plugins
759 * or Network Enabled.
760 *
761 * @author peshkov@UD
762 */
763 public function is_network_detected() {
764 /* Plugin is loaded via mu-plugins. */
765
766 if( strpos( Utility::normalize_path( $this->root_path ), Utility::normalize_path( WPMU_PLUGIN_DIR ) ) !== false ) {
767 return true;
768 }
769
770 if( is_multisite() ) {
771 /* Looks through network enabled plugins to see if our one is there. */
772 foreach (wp_get_active_network_plugins() as $path) {
773 // Trying again using readlink in case it's a symlink file.
774 // boot_file is already solved.
775 // wp_normalize_path is helpfull in windows.
776 if (wp_normalize_path($this->boot_file) == wp_normalize_path($path) || (is_link($path) AND $this->boot_file == readlink($path))) {
777 return true;
778 }
779 }
780 }
781 return false;
782 }
783
784 /**
785 * Initialization.
786 * Register scripts and styles
787 */
788 public function admin_init() {
789 $this->show_notice_stateless_cache_busting();
790 wp_register_style( 'wp-stateless', $this->path( 'static/styles/wp-stateless.css', 'url' ), array(), self::$version );
791
792 /* Attachment or upload page */
793 wp_register_script( 'wp-stateless-uploads-js', $this->path( 'static/scripts/wp-stateless-uploads.js', 'url' ), array( 'jquery' ), self::$version );
794
795 /* Setup wizard styles and scripts. */
796 wp_register_style( 'wp-stateless-bootstrap', $this->path( 'static/styles/bootstrap.min.css', 'url' ), array(), '3.3.7' );
797 wp_register_style( 'bootstrap-grid-v4', $this->path( 'static/styles/bootstrap-grid.min.css', 'url' ), array(), '3.3.7' );
798 wp_register_style( 'wp-stateless-setup-wizard', $this->path( 'static/styles/wp-stateless-setup-wizard.css', 'url' ), array(), self::$version );
799
800 wp_register_script( 'async.min', ud_get_stateless_media()->path( 'static/scripts/async.js', 'url' ), array(), ud_get_stateless_media()->version );
801 wp_register_script( 'jquery.history', ud_get_stateless_media()->path( 'static/scripts/jquery.history.js', 'url' ), array( 'jquery' ), ud_get_stateless_media()->version, true );
802 wp_register_script( 'wp-stateless-validation', ud_get_stateless_media()->path( 'static/scripts/jquery.validation.js', 'url' ), array( 'jquery' ), ud_get_stateless_media()->version, true );
803 wp_register_script( 'wp-stateless-loading', ud_get_stateless_media()->path( 'static/scripts/jquery.loading.js', 'url' ), array( 'jquery' ), ud_get_stateless_media()->version, true );
804 wp_register_script( 'wp-stateless-comboBox', ud_get_stateless_media()->path( 'static/scripts/jquery.wp-stateless-combo-box.js', 'url' ), array( 'jquery' ), ud_get_stateless_media()->version, true );
805 wp_register_script( 'wp-stateless-setup', ud_get_stateless_media()->path( 'static/scripts/wp-stateless-setup.js', 'url' ), array( 'jquery-ui-core', 'wp-api', 'jquery.history' ), ud_get_stateless_media()->version, true );
806 wp_localize_script( 'wp-stateless-setup', 'stateless_l10n', $this->get_l10n_data() );
807
808 wp_register_script( 'wp-stateless-setup-wizard-js', ud_get_stateless_media()->path( 'static/scripts/wp-stateless-setup-wizard.js', 'url' ), array( 'jquery', 'wp-api', 'async.min', 'wp-stateless-setup', 'wp-stateless-comboBox', 'wp-stateless-validation', 'wp-stateless-loading' ), ud_get_stateless_media()->version, true );
809
810
811 /* Stateless settings page */
812 wp_register_script( 'wp-stateless-settings', ud_get_stateless_media()->path( 'static/scripts/wp-stateless-settings.js', 'url' ), array(), ud_get_stateless_media()->version );
813 wp_localize_script( 'wp-stateless-settings', 'stateless_l10n', $this->get_l10n_data() );
814
815 wp_register_style( 'wp-stateless-settings', $this->path( 'static/styles/wp-stateless-settings.css', 'url' ), array(), self::$version );
816
817 // Sync tab
818 if ( wp_script_is( 'jquery-ui-widget', 'registered' ) ){
819 wp_register_script( 'jquery-ui-progressbar', ud_get_stateless_media()->path('static/scripts/jquery-ui/jquery.ui.progressbar.min.js', 'url'), array( 'jquery-ui-core', 'jquery-ui-widget' ), '1.8.6' );
820 }
821 else{
822 wp_register_script( 'jquery-ui-progressbar', ud_get_stateless_media()->path( 'static/scripts/jquery-ui/jquery.ui.progressbar.min.1.7.2.js', 'url' ), array( 'jquery-ui-core' ), '1.7.2' );
823 }
824 wp_register_script( 'wp-stateless-angular', ud_get_stateless_media()->path( 'static/scripts/angular.min.js', 'url' ), array(), '1.5.0', true );
825 wp_register_script( 'wp-stateless', ud_get_stateless_media()->path( 'static/scripts/wp-stateless.js', 'url' ), array( 'jquery-ui-core', 'wp-stateless-settings' ), ud_get_stateless_media()->version, true );
826
827 wp_localize_script( 'wp-stateless', 'stateless_l10n', $this->get_l10n_data() );
828 wp_localize_script('wp-stateless', 'wp_stateless_configs', array(
829 'WP_DEBUG' => defined('WP_DEBUG') ? WP_DEBUG : false,
830 ));
831
832 $settings = ud_get_stateless_media()->get('sm');
833 if(defined('WP_STATELESS_MEDIA_JSON_KEY') && WP_STATELESS_MEDIA_JSON_KEY){
834 $settings['key_json'] = "Currently configured via a constant.";
835 }
836 wp_localize_script('wp-stateless', 'wp_stateless_settings', $settings);
837 wp_localize_script('wp-stateless', 'wp_stateless_compatibility', Module::get_modules());
838 wp_register_style( 'jquery-ui-regenthumbs', ud_get_stateless_media()->path( 'static/scripts/jquery-ui/redmond/jquery-ui-1.7.2.custom.css', 'url' ), array(), '1.7.2' );
839
840 }
841
842 public function get_l10n_data($value=''){
843 include ud_get_stateless_media()->path( 'l10n.php', 'dir');
844 return $l10n;
845 }
846
847 /**
848 *
849 * @todo: it should not be loaded everywhere. peshkov@UD
850 * @param $hook
851 */
852 public function admin_enqueue_scripts( $hook ) {
853
854
855 switch( $hook ) {
856
857 case 'options-media.php':
858 //wp_enqueue_script( 'wp-api' );
859
860 wp_enqueue_style( 'wp-stateless');
861 wp_enqueue_script( 'wp-stateless-setup' );
862 break;
863
864 case 'upload.php':
865
866 wp_enqueue_style( 'wp-stateless');
867 wp_enqueue_script( 'wp-stateless-uploads-js' );
868
869 break;
870
871 case 'post.php':
872
873 global $post;
874
875 if ( $post->post_type == 'attachment' ) {
876 wp_enqueue_style( 'wp-stateless');
877 wp_enqueue_script( 'wp-stateless-uploads-js' );
878 }
879
880 break;
881
882 case 'media_page_stateless-setup':
883 wp_enqueue_style( 'wp-stateless');
884 wp_enqueue_style( 'wp-stateless-bootstrap' );
885 wp_enqueue_style( 'wp-stateless-setup-wizard' );
886
887 wp_enqueue_script( 'async.min' );
888 wp_enqueue_script( 'jquery.history' );
889 wp_enqueue_script( 'wp-stateless-validation' );
890 wp_enqueue_script( 'wp-stateless-loading' );
891 wp_enqueue_script( 'wp-stateless-comboBox' );
892 wp_enqueue_script( 'wp-stateless-setup' );
893 wp_enqueue_script( 'wp-stateless-setup-wizard-js' );
894 break;
895 case 'media_page_stateless-settings':
896 wp_enqueue_style( 'wp-stateless');
897 wp_enqueue_script( 'wp-stateless-settings' );
898 wp_enqueue_style( 'bootstrap-grid-v4' );
899 wp_enqueue_style( 'wp-stateless-settings' );
900
901 // Sync tab
902 wp_enqueue_script( 'jquery-ui-progressbar' );
903 wp_enqueue_script( 'wp-stateless-angular' );
904 wp_enqueue_script( 'wp-stateless' );
905 wp_enqueue_style( 'jquery-ui-regenthumbs' );
906
907 $data = array(
908 'key' => 'stateless-cache-busting',
909 'class' => 'notice',
910 'title' => sprintf( __( "Stateless mode enables and requires the Cache-Busting option.", ud_get_stateless_media()->domain ) ),
911 'message' => sprintf( __("WordPress looks at local files to prevent files with the same filenames.
912 Since Stateless mode bypasses this check, there is a potential for files to be stored with the same file name. We enforce the Cache-Busting option to prevent this.
913 Override with the <a href='%s' target='_blank'>%s</a> constant.", ud_get_stateless_media()->domain),"https://github.com/wpCloud/wp-stateless/wiki/Constants#wp_stateless_media_cache_busting", "WP_STATELESS_MEDIA_CACHE_BUSTING" ),
914 );
915 echo "<script id='template-stateless-cache-busting' type='text/html'>";
916 include ud_get_stateless_media()->path( '/static/views/error-notice.php', 'dir' );
917 echo "</script>";
918 default: break;
919 }
920
921 }
922
923 /**
924 * Add Attributes to media HTML
925 *
926 * @author potanin@UD
927 * @param $attr
928 * @param $attachment
929 * @param $size
930 * @return mixed
931 */
932 public function wp_get_attachment_image_attributes( $attr, $attachment, $size = null ) {
933
934 $sm_cloud = get_post_meta( $attachment->ID, 'sm_cloud', true );
935 if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'name' ] ) ) {
936 $attr[ 'class' ] = $attr[ 'class' ] . ' wp-stateless-item';
937 $attr[ 'data-image-size' ] = is_array( $size ) ? implode( 'x', $size ) : $size;
938 $attr[ 'data-stateless-media-bucket' ] = isset( $sm_cloud[ 'bucket' ] ) ? $sm_cloud[ 'bucket' ] : false;
939 $attr[ 'data-stateless-media-name' ] = $sm_cloud[ 'name' ];
940 }
941
942 return $attr;
943
944 }
945
946 /**
947 * Adds filter link to Media Library table.
948 *
949 * @param $views
950 * @return mixed
951 */
952 public function views_upload( $views ) {
953 $views['stateless'] = '<a href="#">' . __( 'Stateless Media' ) . '</a>';
954 return $views;
955 }
956
957 /**
958 * Replace media URL
959 *
960 * @param bool $false
961 * @param integer $id
962 * @param string $size
963 * @return mixed $false
964 */
965 public function image_downsize( $false = false, $id, $size ) {
966
967 if ( !isset( $this->client ) || !$this->client || is_wp_error( $this->client ) ) {
968 return $false;
969 }
970
971 /**
972 * Check if enabled
973 */
974 if ( $this->get( 'sm.mode' ) !== 'cdn' && $this->get( 'sm.mode' ) !== 'stateless' ) {
975 return $false;
976 }
977
978 /** Start determine remote file */
979 $img_url = wp_get_attachment_url($id);
980 $meta = wp_get_attachment_metadata($id);
981 $width = $height = 0;
982 $is_intermediate = false;
983
984 //** try for a new style intermediate size */
985 if ( $intermediate = image_get_intermediate_size( $id, $size ) ) {
986 if(!empty( $intermediate['gs_link'] )){
987 $img_url = $intermediate['gs_link'];
988 }
989 else if(!empty( $intermediate['url'] )){
990 $img_url = $intermediate['url'];
991 }
992 else{
993 $img_url = dirname($img_url) . $intermediate['file'];
994 }
995
996 $width = $intermediate['width'];
997 $height = $intermediate['height'];
998 $is_intermediate = true;
999 }
1000 //die( '<pre>' . print_r( $intermediate, true ) . '</pre>' );
1001
1002 /**
1003 * maybe try to get images info from sm_cloud
1004 * this case may happen when no local files
1005 * @author korotkov@ud
1006 */
1007 if ( !$width && !$height ) {
1008 $sm_cloud = get_post_meta( $id, 'sm_cloud', true );
1009 if ( is_string($size) && !empty( $sm_cloud['sizes'] ) && !empty( $sm_cloud['sizes'][$size] ) ) {
1010 global $_wp_additional_image_sizes;
1011
1012 $img_url = !empty( $sm_cloud['sizes'][$size]['fileLink'] ) ? $sm_cloud['sizes'][$size]['fileLink'] : $img_url;
1013
1014 if ( !empty( $_wp_additional_image_sizes[ $size ] ) ) {
1015 $width = !empty( $_wp_additional_image_sizes[ $size ]['width'] ) ? $_wp_additional_image_sizes[ $size ]['width'] : $width;
1016 $height = !empty( $_wp_additional_image_sizes[ $size ]['height'] ) ? $_wp_additional_image_sizes[ $size ]['height'] : $height;
1017 }
1018
1019 $is_intermediate = true;
1020 }
1021 }
1022
1023 if ( !$width && !$height && isset( $meta['width'], $meta['height'] ) ) {
1024
1025 //** any other type: use the real image */
1026 $width = $meta['width'];
1027 $height = $meta['height'];
1028 }
1029
1030
1031 if ( $img_url) {
1032
1033 //** we have the actual image size, but might need to further constrain it if content_width is narrower */
1034 list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
1035 $img_url = apply_filters('wp_stateless_bucket_link', $img_url);
1036 return array( $img_url, $width, $height, $is_intermediate );
1037 }
1038
1039
1040 /**
1041 * All other cases work as usually
1042 */
1043 return $false;
1044
1045 }
1046
1047 /**
1048 * Extends metadata by adding GS information.
1049 * Note: must not be called directly. It's used only on hook
1050 *
1051 * @action wp_get_attachment_metadata
1052 * @param $metadata
1053 * @param $attachment_id
1054 * @return $metadata
1055 */
1056 public function wp_get_attachment_metadata( $metadata, $attachment_id ) {
1057 /* Determine if the media file has GS data at all. */
1058 $sm_cloud = get_post_meta( $attachment_id, 'sm_cloud', true );
1059 // If metadata not passed the get metadata from post meta.
1060 if(empty($metadata)){
1061 $metadata = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
1062 }
1063
1064 if( is_array( $metadata ) && is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) {
1065 $metadata[ 'gs_link' ] = apply_filters('wp_stateless_bucket_link', $sm_cloud[ 'fileLink' ]);
1066 $metadata[ 'gs_name' ] = isset( $sm_cloud[ 'name' ] ) ? $sm_cloud[ 'name' ] : false;
1067 $metadata[ 'gs_bucket' ] = isset( $sm_cloud[ 'bucket' ] ) ? $sm_cloud[ 'bucket' ] : false;
1068 if( !empty( $metadata[ 'sizes' ] ) && is_array( $metadata[ 'sizes' ] ) ) {
1069 foreach( $metadata[ 'sizes' ] as $k => $v ) {
1070 if( !empty( $sm_cloud[ 'sizes' ][ $k ][ 'name' ] ) ) {
1071 $metadata['sizes'][$k]['gs_name'] = $sm_cloud[ 'sizes' ][ $k ][ 'name' ];
1072 $metadata['sizes'][$k]['gs_link'] = apply_filters('wp_stateless_bucket_link', $sm_cloud[ 'sizes' ][ $k ][ 'fileLink' ]);
1073 }
1074 }
1075 }
1076 }
1077
1078 return $metadata;
1079 }
1080
1081 /**
1082 * Returns client object
1083 * or WP_Error on failure.
1084 *
1085 * @author peshkov@UD
1086 * @return object $this->client. \wpCloud\StatelessMedia\GS_Client or \WP_Error
1087 */
1088 public function get_client() {
1089
1090 if( null === $this->client ) {
1091
1092 $key_json = get_site_option( 'sm_key_json' );
1093 if ( empty($key_json) ) {
1094 $key_json = $this->get( 'sm.key_json' );
1095 }
1096
1097 /* Try to initialize GS Client */
1098 $this->client = GS_Client::get_instance( array(
1099 'bucket' => $this->get( 'sm.bucket' ),
1100 'key_json' => $key_json
1101 ) );
1102 }
1103
1104 return $this->client;
1105
1106 }
1107
1108 /**
1109 * Determines if we can connect to Google Storage Bucket.
1110 *
1111 * @author peshkov@UD
1112 */
1113 public function is_connected_to_gs() {
1114
1115 $trnst = get_transient( 'sm::is_connected_to_gs' );
1116
1117 if ( empty($trnst) || false === $trnst || !isset( $trnst[ 'hash' ] ) || $trnst[ 'hash' ] != md5( serialize( $this->get( 'sm' ) ) ) ) {
1118 $trnst = array(
1119 'success' => 'true',
1120 'error' => '',
1121 'hash' => md5( serialize( $this->get( 'sm' ) ) ),
1122 );
1123 $client = $this->get_client();
1124
1125 if ( is_wp_error( $client ) ) {
1126 $trnst[ 'success' ] = 'false';
1127 $trnst[ 'error' ] = $client->get_error_message();
1128 } else {
1129 $connected = $client->is_connected();
1130 if( $connected !== true ) {
1131 $trnst[ 'success' ] = 'false';
1132 $trnst[ 'error' ] = sprintf( __( 'Could not connect to Google Storage bucket. Please, be sure that bucket with name <b>%s</b> exists.', $this->domain ), $this->get( 'sm.bucket' ) );
1133
1134 if( is_callable(array($connected, 'getHandlerContext')) && $handlerContext = $connected->getHandlerContext() ){
1135 if(!empty($handlerContext['error'])){
1136 $handlerContext['error'];
1137 $trnst[ 'error' ] = "Could not connect to Google Storage bucket. " . make_clickable($handlerContext['error']);
1138 }
1139 }
1140
1141 if( is_callable(array($connected, 'getErrors')) && $error = $connected->getErrors() ){
1142 $error = reset($error);
1143 if($error['reason'] == 'accessNotConfigured')
1144 $trnst[ 'error' ] = "Could not connect to Google Storage bucket. " . make_clickable($error['message']);
1145 }
1146 }
1147 }
1148 set_transient( 'sm::is_connected_to_gs', $trnst, 4 * HOUR_IN_SECONDS );
1149 }
1150
1151 if( isset( $trnst[ 'success' ] ) && $trnst[ 'success' ] == 'false' ) {
1152 return new \WP_Error( 'error', ( !empty( $trnst[ 'error' ] ) ? $trnst[ 'error' ] : __( 'There is an Error on connection to Google Storage.', $this->domain ) ) );
1153 }
1154
1155 return true;
1156 }
1157
1158 /**
1159 * Flush all plugin transients
1160 *
1161 */
1162 public function flush_transients() {
1163 delete_transient( 'sm::is_connected_to_gs' );
1164 }
1165
1166 /**
1167 * Plugin Activation
1168 *
1169 */
1170 public function activate() {
1171 add_action( 'activated_plugin', array($this, 'redirect_to_splash'), 99 );
1172 $this->run_upgrade_process();
1173 }
1174
1175 /**
1176 * Run Install Process.
1177 * Triggered on plugins_loaded instead of register_activation_hook action.
1178 * Works on even manual plugin update.
1179 *
1180 * @param string $old_version Old version.
1181 * @author alim@UD
1182 */
1183 public function run_install_process()
1184 {
1185 // calling the upgrade function because it's same as this point for fresh install or updates.
1186 $this->run_upgrade_process();
1187 }
1188
1189 /**
1190 * Run Upgrade Process:
1191 * Triggered on plugins_loaded instead of register_activation_hook action.
1192 * Works on even manual plugin update.
1193 *
1194 * @author alim@UD
1195 */
1196 public function run_upgrade_process()
1197 {
1198 // Creating database on new installation.
1199 $this->create_db();
1200 /**
1201 * Maybe Upgrade current Version
1202 */
1203 Upgrader::call( $this->args[ 'version' ] );
1204 }
1205
1206 /**
1207 * Create database on plugin activation.
1208 * @param $force whether to create db even if option exists. For debug purpose only.
1209 */
1210 public function create_db($force = false) {
1211 global $wpdb;
1212 $sm_sync_db_version = get_option( 'sm_sync_db_version' );
1213
1214 if( $sm_sync_db_version && $force == false ) {
1215 return;
1216 }
1217
1218 $table_name = $wpdb->prefix . 'sm_sync';
1219 $charset_collate = $wpdb->get_charset_collate();
1220
1221 // `expire` timestamp NULL DEFAULT NULL,
1222 $sql = "CREATE TABLE $table_name (
1223 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT ,
1224 `file` varchar(255) NOT NULL ,
1225 `status` varchar(10) NOT NULL ,
1226 PRIMARY KEY (`id`)
1227 ) $charset_collate;";
1228
1229 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1230 dbDelta( $sql );
1231
1232 add_option( 'sm_sync_db_version', $this->args[ 'version' ] );
1233 }
1234
1235 public function redirect_to_splash($plugin =''){
1236 $this->settings = new Settings();
1237
1238 if(defined( 'WP_CLI' ) || $this->settings->get('sm.key_json') || isset($_POST['checked']) && count($_POST['checked']) > 1){
1239 return;
1240 }
1241
1242 if(
1243 !$this->settings->get('sm.key_json') &&
1244 defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true &&
1245 defined('WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL') && WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL == true
1246 ) {
1247 return;
1248 }
1249
1250 if( !$this->settings->get('sm.key_json') && defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true ) {
1251 $url = $this->get_settings_page_url('?page=stateless-settings');
1252 exit( wp_redirect($url));
1253 }
1254
1255 if( $plugin == plugin_basename( $this->boot_file ) ) {
1256 $url = $this->get_settings_page_url('?page=stateless-setup&step=splash-screen');
1257 if(json_decode($this->settings->get('sm.key_json'))){
1258 $url = $this->get_settings_page_url('?page=stateless-settings');
1259 }
1260 exit( wp_redirect($url));
1261 }
1262 }
1263
1264 /**
1265 * Plugin Deactivation
1266 *
1267 */
1268 public function deactivate() {}
1269
1270 public function show_notice_stateless_cache_busting(){
1271 $this->errors->add( array(
1272 'key' => 'stateless_cache_busting',
1273 'button' => 'View Settings',
1274 'button_link' => admin_url('upload.php?page=stateless-settings'),
1275 'title' => sprintf( __( "Stateless mode now requires the Cache-Busting option.", ud_get_stateless_media()->domain ) ),
1276 'message' => sprintf( __("WordPress looks at local files to prevent files with the same filenames.
1277 Since Stateless mode bypasses this check, there is a potential for files to be stored with the same file name. We enforce the Cache-Busting option to prevent this.
1278 Override with the <a href='%s' target='_blank'>%s</a> constant.", ud_get_stateless_media()->domain),"https://github.com/wpCloud/wp-stateless/wiki/Constants#wp_stateless_media_cache_busting", "WP_STATELESS_MEDIA_CACHE_BUSTING" ),
1279 ), 'notice' );
1280 }
1281
1282 /**
1283 * Filter for wp_get_attachment_url();
1284 *
1285 * @param string $url
1286 * @param string $post_id
1287 * @return mixed|null|string
1288 */
1289 public function wp_get_attachment_url( $url = '', $post_id = '' ) {
1290 $sm_cloud = get_post_meta( $post_id, 'sm_cloud', 1 );
1291 if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) {
1292 $_url = parse_url($sm_cloud[ 'fileLink' ]);
1293 $url = !isset($_url['scheme']) ? ( 'https:' . $sm_cloud[ 'fileLink' ] ) : $sm_cloud[ 'fileLink' ];
1294 return apply_filters('wp_stateless_bucket_link', $url);
1295 }
1296 return $url;
1297 }
1298
1299 /**
1300 * Filter for attachment_url_to_postid()
1301 *
1302 * @param int|false $post_id originally found post ID (or false if not found)
1303 * @param string $url the URL to find the post ID for
1304 * @return int|false found post ID from cloud storage URL
1305 */
1306 public function attachment_url_to_postid( $post_id, $url ) {
1307 global $wpdb;
1308
1309 if ( ! $post_id ) {
1310 $post_id = get_transient("stateless_url_to_postid_" . md5($url));
1311
1312 if(defined('WP_STATELESS_LEGACY_URL_TO_POSTID')){
1313 // User can use this constant if they change the Bucket Folder (root_dir) after uploading image.
1314 // This can be little slow at first run.
1315 if(empty($post_id)){
1316 $query = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sm_cloud' AND meta_value LIKE '%s'";
1317 $post_id = $wpdb->get_var( $wpdb->prepare( $query, '%' . $url . '%' ) );
1318
1319 if($post_id){
1320 set_transient("stateless_url_to_postid_" . md5($url), $post_id);
1321 }
1322 }
1323 return $post_id;
1324 }
1325
1326 if(empty($post_id)){
1327 $gs_base_url = $this->get_gs_host();
1328 $gs_url = $this->get_gs_host() . '/' . $this->get( 'sm.root_dir' );
1329 $site_url = parse_url($gs_url);
1330 $image_path = parse_url( $url );
1331
1332 //force the protocols to match if needed
1333 if( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) {
1334 $url = str_replace( $image_path['scheme'], $site_url['scheme'], $url );
1335 }
1336
1337 if( 0 === strpos( $url, $gs_url . '/' ) ) {
1338 $url = substr( $url, strlen( $gs_url . '/' ) );
1339 }
1340 else if( 0 === strpos( $url, $gs_base_url . '/' ) ) {
1341 // In case user added Bucket Folder (root_dir) after uploading image.
1342 $url = substr( $url, strlen( $gs_base_url . '/' ) );
1343 }
1344
1345 $sql = $wpdb->prepare(
1346 "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
1347 $url
1348 );
1349 $post_id = $wpdb->get_var( $sql );
1350
1351 if($post_id){
1352 set_transient("stateless_url_to_postid_" . md5($url), $post_id);
1353 }
1354 }
1355 }
1356
1357 return $post_id;
1358 }
1359
1360 /**
1361 * Upload the full size image first.
1362 *
1363 */
1364 public function before_intermediate_image_sizes($sizes, $metadata = array()){
1365 if(empty($metadata)){
1366 return $sizes;
1367 }
1368
1369 try{
1370 $attachment_id = attachment_url_to_postid($metadata['file']);
1371 $this->add_media(null, $attachment_id, false, array('no_thumb' => true));
1372 }
1373 catch(Exception $e){
1374
1375 }
1376
1377 return $sizes;
1378 }
1379
1380 /**
1381 * Change Upload BaseURL when CDN Used.
1382 *
1383 * @param $data
1384 * @return mixed
1385 */
1386 public function upload_dir( $data ) {
1387 $data[ 'basedir' ] = $this->get_gs_host();
1388 $data[ 'baseurl' ] = $this->get_gs_host();
1389 $data[ 'url' ] = $data[ 'baseurl' ] . $data[ 'subdir' ];
1390
1391 return $data;
1392
1393 }
1394
1395 /**
1396 * Set Feature Flag constants by parsing composer.json
1397 *
1398 * @todo Make sure settings from DB can override these.
1399 *
1400 * @author potanin@UD
1401 * @return array|mixed|null|object
1402 */
1403 public function parse_feature_flags( ) {
1404
1405 try {
1406
1407 $_raw = file_get_contents( Utility::normalize_path( $this->root_path ) . 'composer.json' );
1408
1409 $_parsed = json_decode( $_raw );
1410
1411 // @todo Catch poorly formatted JSON.
1412 if( !is_object( $_parsed ) ) {
1413 // throw new Error( "unable to parse." );
1414 }
1415
1416 foreach( (array) $_parsed->extra->featureFlags as $_feature ) {
1417
1418 if( !defined( $_feature->constant ) ) {
1419 define( $_feature->constant, $_feature->enabled );
1420
1421 if( $_feature->enabled ) {
1422 Utility::log( 'Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] enabled.' );
1423 } else {
1424 Utility::log( 'Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] disabled.' );
1425 }
1426 }
1427
1428 }
1429
1430 } catch ( Exception $e ) {
1431 Utility::log( 'Unable to parse [composer.json] feature flags. Error: [' . $e->getMessage() . ']' );
1432 // echo 'Caught exception: ', $e->getMessage(), "\n";
1433 }
1434
1435
1436 return isset( $_parsed ) ? $_parsed : null;
1437
1438
1439 }
1440
1441 /**
1442 * Determine if Utility class contains missed function
1443 * in other case, just return NULL to prevent ERRORS
1444 *
1445 * @author peshkov@UD
1446 * @param $name
1447 * @param $arguments
1448 * @return mixed|null
1449 */
1450 public function __call( $name, $arguments ) {
1451 if( is_callable( array( "wpCloud\\StatelessMedia\\Utility", $name ) ) ) {
1452 return call_user_func_array( array( "wpCloud\\StatelessMedia\\Utility", $name ), $arguments );
1453 } else {
1454 return NULL;
1455 }
1456 }
1457
1458 }
1459
1460 }
1461
1462 }
1463