PluginProbe
WP-Stateless – Google Cloud Storage / 2.2.0
WP-Stateless – Google Cloud Storage v2.2.0
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.0, at lib/classes/class-bootstrap.php

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