PluginProbe
WP-Stateless – Google Cloud Storage / 2.1.4
WP-Stateless – Google Cloud Storage v2.1.4
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / lib / classes / class-bootstrap.php

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

1,194 lines 42.0 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 * Instantaite class.
42 */
43 public function init() {
44
45 /**
46 * Copied from wp-property
47 * Duplicates UsabilityDynamics\WP\Bootstrap_Plugin::load_textdomain();
48 *
49 * There is a bug with localisation in lib-wp-bootstrap 1.1.3 and lower.
50 * So we load textdomain here again, in case old version lib-wp-bootstrap is being loaded
51 * by another plugin.
52 *
53 * @since 1.9.1
54 */
55 load_plugin_textdomain($this->domain, false, dirname(plugin_basename($this->boot_file)) . '/static/languages/');
56
57 // Parse feature falgs, set constants.
58 $this->parse_feature_flags();
59
60 // Initialize compatibility modules.
61 new Module();
62 new SyncNonMedia();
63
64 /**
65 * Define settings and UI.
66 *
67 * Example:
68 *
69 * Get option
70 * $this->get( 'sm.client_id' )
71 *
72 * Manually Update/Add option
73 * $this->set( 'sm.client_id', 'zxcvv12adffse' );
74 */
75 $this->settings = new Settings();
76
77 // Invoke REST API
78 add_action( 'rest_api_init', array( $this, 'api_init' ) );
79
80 /**
81 * Register SM metaboxes
82 */
83 add_action( 'admin_init', array( $this, 'register_metaboxes' ) );
84
85 /**
86 * Init hook
87 */
88 add_action( 'admin_init', array( $this, 'admin_init' ) );
89
90 /**
91 * Add custom actions to media rows
92 */
93 add_filter( 'media_row_actions', array( $this, 'add_custom_row_actions' ), 10, 3 );
94
95 /**
96 * Handle switch blog properly.
97 */
98 add_action( 'switch_blog', array( $this, 'on_switch_blog' ), 10, 2 );
99
100 /**
101 * Filter for getting stateless settings
102 */
103 add_filter( 'stateless::get_settings', array( $this, 'get_settings' ), 10);
104
105 /**
106 * Init AJAX jobs
107 */
108 new Ajax();
109
110 /**
111 * Maybe Upgrade current Version
112 */
113 Upgrader::call( $this->args[ 'version' ] );
114
115 /**
116 * Load WP-CLI Commands
117 */
118 if( defined( 'WP_CLI' ) && WP_CLI ) {
119 include_once($this->path('lib/cli/class-sm-cli-command.php', 'dir'));
120 }
121
122 $this->is_network_detected();
123
124 /**
125 * Add scripts
126 */
127 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
128
129 /**
130 * Hashify file name if option is enabled
131 */
132 if ( $this->get( 'sm.hashify_file_name' ) == 'true' ) {
133 add_filter('sanitize_file_name', array( $this, 'randomize_filename' ), 10);
134 }
135
136 /* Initialize plugin only if Mode is not 'disabled'. */
137 if ( $this->get( 'sm.mode' ) !== 'disabled' ) {
138
139 /**
140 * Override Cache Control is option is enabled
141 */
142 $cacheControl = trim($this->get( 'sm.cache_control' ));
143 if ( !empty($cacheControl) ) {
144 add_filter( 'sm:item:cacheControl', array( $this, 'override_cache_control' ) );
145 }
146
147 /**
148 * Determine if we have issues with connection to Google Storage Bucket
149 * if SM is not disabled.
150 */
151 $is_connected = $this->is_connected_to_gs();
152
153 if ( is_wp_error( $is_connected ) ) {
154 $this->errors->add( $is_connected->get_error_message() );
155 }
156
157 if ( $googleSDKVersionConflictError = get_transient( "wp_stateless_google_sdk_conflict" ) ) {
158 $this->errors->add( $googleSDKVersionConflictError, 'warning' );
159 }
160
161 // To prevent fatal errors for users who use PHP 5.5 or less.
162 if( version_compare(PHP_VERSION, '5.5', '<') ) {
163 $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>' ) );
164 }
165
166 /** Temporary fix to WP 4.4 srcset feature **/
167 //add_filter( 'max_srcset_image_width', create_function( '', 'return 1;' ) );
168
169 /**
170 * Carry on only if we do not have errors.
171 */
172 if( !$this->has_errors() ) {
173
174 do_action('sm::module::init', $this->get( 'sm' ));
175
176 if( $this->get( 'sm.mode' ) === 'cdn' || $this->get( 'sm.mode' ) === 'stateless' ) {
177 add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 20, 3 );
178 add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 20, 2 );
179 add_filter( 'attachment_url_to_postid', array( $this, 'attachment_url_to_postid' ), 20, 2 );
180
181 if ( $this->get( 'sm.body_rewrite' ) == 'true' ) {
182 add_filter( 'the_content', array( $this, 'the_content_filter' ) );
183 add_filter( 'get_post_metadata', array( $this, 'post_metadata_filter' ), 2, 4 );
184 }
185
186 if ( $this->get( 'sm.custom_domain' ) == $this->get( 'sm.bucket' ) ) {
187 add_filter( 'wp_stateless_bucket_link', array( $this, 'wp_stateless_bucket_link' ) );
188 }
189 }
190
191 if ( $root_dir = $this->get( 'sm.root_dir' ) ) {
192 if ( trim( $root_dir, '/ ' ) !== '' ) { // Remove any forward slash and empty space.
193 add_filter( 'wp_stateless_file_name', array( $this, 'handle_root_dir' ) );
194 }
195 }
196
197 /**
198 * Rewrite Image URLS
199 */
200 add_filter( 'image_downsize', array( $this, 'image_downsize' ), 99, 3 );
201 add_filter( 'wp_calculate_image_srcset', array($this, 'wp_calculate_image_srcset'), 10, 5 );
202
203 /**
204 * Extends metadata by adding GS information.
205 */
206 add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
207
208 /**
209 * Add/Edit Media
210 *
211 * Once added or edited we can get into Attachment ID then get all image sizes and sync them with GS
212 */
213 add_filter( 'wp_update_attachment_metadata', array( $this, 'add_media' ), 100, 2 );
214
215 /**
216 * Add Media
217 *
218 * Once added we can get into Attachment ID then get all image sizes and sync them with GS
219 */
220 // add_filter( 'wp_generate_attachment_metadata', array( $this, 'add_media' ), 100, 2 );
221
222 if ( $this->get( 'sm.delete_remote' ) == 'true' ) {
223 /**
224 * On physical file deletion we remove any from GS
225 */
226 add_filter('delete_attachment', array($this, 'remove_media'));
227 }
228 }
229
230 }
231 }
232
233 /**
234 * Rebuild srcset from gs_link.
235 *
236 *
237 */
238 public function wp_calculate_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id){
239
240 if ( empty( $image_meta['gs_link'] ) ) {
241 $image_meta = wp_get_attachment_metadata($attachment_id);
242 }
243
244 foreach ($sources as $width => &$image) {
245 if($width == $image_meta['width'] && isset( $image_meta['gs_link'] ) && $image_meta['gs_link'] ){
246 $image['url'] = $image_meta['gs_link'];
247 }
248 elseif(isset($image_meta['sizes']) && is_array($image_meta['sizes'])){
249 foreach ($image_meta['sizes'] as $key => $meta) {
250 if($width == $meta['width'] && isset($meta['gs_link']) && $meta['gs_link']){
251 $image['url'] = $meta['gs_link'];
252 }
253 }
254 }
255 }
256
257 return $sources;
258 }
259
260 /**
261 * Return gs host.
262 * If custom domain is set it's return bucket name as host,
263 * else return storage.googleapis.com as host and append bucket name at the end.
264 * @param none
265 * @return Host name to use
266 */
267 public function get_gs_host($sm = array()) {
268 $sm = $sm?$sm: $this->get( 'sm');
269 $image_host = 'https://storage.googleapis.com/';
270 $is_ssl = strpos($sm['custom_domain'], 'https://');
271 $custom_domain = str_replace('https://', '', $sm['custom_domain']);
272 if ( $sm['bucket'] && $custom_domain == $sm['bucket']) {
273 $image_host = $is_ssl === 0 ? 'https://' : 'http://'; // bucketname will be host
274 }
275 return $image_host . $sm['bucket'];
276 }
277
278 /**
279 * Filter for wp_stateless_bucket_link if custom domain is set.
280 * It's get attachment url and remove "storage.googleapis.com" from url.
281 * So that custom url can be used.
282 * @param $new_blog
283 * @param $prev_blog_id
284 */
285 public function wp_stateless_bucket_link($fileLink) {
286 $bucketname = $this->get( 'sm.bucket' );
287 if ( strpos($fileLink, $bucketname) > 8) {
288 $fileLink = 'http://' . substr($fileLink, strpos($fileLink, $bucketname));
289 }
290 return $fileLink;
291 }
292
293 /**
294 * Return settings page url.
295 * @param $path
296 */
297 public function get_settings_page_url( $path = '' ) {
298 $url = get_admin_url( get_current_blog_id(), ( is_network_admin() ? 'network/settings.php' : 'upload.php' ) );
299 return $url . $path;
300 }
301
302 /**
303 * Get new blog settings once switched blog.
304 * @param $new_blog
305 * @param $prev_blog_id
306 */
307 public function on_switch_blog( $new_blog, $prev_blog_id ) {
308 $this->settings->refresh();
309 }
310
311 /**
312 * Get settings handler.
313 * Filling array if some settings missing.
314 *
315 * @param $settings
316 * @return
317 */
318 public function get_settings($settings) {
319
320 $settings_list = array(
321 'mode',
322 'body_rewrite',
323 'body_rewrite_types',
324 'bucket',
325 'root_dir',
326 'key_json',
327 'cache_control',
328 'delete_remote',
329 'custom_domain',
330 'organize_media',
331 'hashify_file_name'
332 );
333
334 foreach ($settings_list as $setting) {
335
336 /** If setting is already exist, just skip it */
337 if( isset( $settings[ $setting ] ) ) {
338 continue;
339 }
340
341 $value = $this->get( 'sm.' . $setting );
342
343 /** Decode json to array */
344 if( $value && is_string( $value ) && $setting === 'key_json' ) {
345 $value = json_decode( $value, true );
346 $setting = 'key';
347 }
348
349 $settings[ $setting ] = $value;
350 }
351
352 return $settings;
353 }
354
355 /**
356 * Remove all settings.
357 */
358 public function reset($network = false) {
359 $this->settings->reset($network);
360 }
361
362 /**
363 * @param $actions
364 * @param $post
365 * @param $detached
366 * @return mixed
367 */
368 public function add_custom_row_actions( $actions, $post, $detached ) {
369
370 if ( !current_user_can( 'upload_files' ) ) return $actions;
371
372 if ( $post && 'attachment' == $post->post_type && 'image/' == substr( $post->post_mime_type, 0, 6 ) ) {
373 $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>';
374 }
375
376 if ( $post && 'attachment' == $post->post_type && 'image/' != substr( $post->post_mime_type, 0, 6 ) ) {
377 $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>';
378 }
379
380 return $actions;
381
382 }
383
384 /**
385 * Register metaboxes
386 */
387 public function register_metaboxes() {
388 add_meta_box(
389 'sm-attachment-metabox',
390 __( 'Google Cloud Storage', ud_get_stateless_media()->domain ),
391 array($this, 'attachment_meta_box_callback'),
392 'attachment',
393 'side',
394 'low'
395 );
396 }
397
398 /**
399 * Define REST API.
400 *
401 * // https://usabilitydynamics-sandbox-uds-io-stateless-testing.c.rabbit.ci/wp-json/wp-stateless/v1
402 *
403 * @author potanin@UD
404 */
405 public function api_init() {
406
407 $route_namespace = 'wp-stateless/v1';
408 $api_namespace = 'wpCloud\StatelessMedia\API';
409
410 register_rest_route( $route_namespace, '/status', array(
411 'methods' => 'GET',
412 'callback' => array( $api_namespace, 'status' ),
413 ) );
414
415 register_rest_route( $route_namespace, '/jobs', array(
416 'methods' => 'GET',
417 'callback' => array( $api_namespace, 'jobs' ),
418 ) );
419
420 /**
421 * Return stateless settings.
422 *
423 * Request parameter: none
424 *
425 * Response:
426 * ok: Whether API is up or not
427 * message: Describe what is done or error message on error.
428 * settings: array of stateless settings
429 *
430 */
431 register_rest_route( $route_namespace, '/getSettings', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getSettings' ), ) );
432
433 /**
434 * Essentially for scrolling through media library to build our index.
435 *
436 * Request parameter: none
437 *
438 * Response:
439 * ok: Whether API is up or not
440 * message: Describe what is done or error message on error.
441 * settings: array of media files
442 *
443 */
444 register_rest_route( $route_namespace, '/getMediaLibrary', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getMediaLibrary' ), ) );
445
446 /**
447 * Get detailed information of media file
448 *
449 * Request parameter: none
450 *
451 * Response:
452 * ok: Whether API is up or not
453 * message: Describe what is done or error message on error.
454 * settings: media file array
455 *
456 */
457 register_rest_route( $route_namespace, '/getMediaItem', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getMediaItem' ), ) );
458
459 }
460
461 /**
462 * @param $post
463 */
464 public function attachment_meta_box_callback( $post ) {
465 ob_start();
466
467 $sm_cloud = get_post_meta( $post->ID, 'sm_cloud', 1 );
468
469 if ( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) { ?>
470
471 <?php if( !empty( $sm_cloud[ 'cacheControl' ] ) ) { ?>
472 <div class="misc-pub-cache-control hidden">
473 <?php _e( 'Cache Control:', ud_get_stateless_media()->domain ); ?> <strong><span><?php echo $sm_cloud[ 'cacheControl' ]; ?></span> </strong>
474 </div>
475 <?php } ?>
476
477 <div class="misc-pub-gs-file-link" style="margin-bottom: 15px;">
478 <label>
479 <?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>
480 <input type="text" class="widefat urlfield" readonly="readonly" value="<?php echo esc_attr($sm_cloud[ 'fileLink' ]); ?>" />
481 </label>
482 </div>
483
484 <?php
485
486 if ( !empty( $sm_cloud[ 'bucket' ] ) ) {
487 ?>
488 <div class="misc-pub-gs-bucket" style="margin-bottom: 15px;">
489 <label>
490 <?php _e( 'Storage Bucket:', ud_get_stateless_media()->domain ); ?>
491 <input type="text" class="widefat urlfield" readonly="readonly" value="gs://<?php echo esc_attr($sm_cloud[ 'bucket' ]); ?>" />
492 </label>
493 </div>
494 <?php
495 }
496
497 if ( current_user_can( 'upload_files' ) ) {
498 if ( $post && 'attachment' == $post->post_type && 'image/' == substr( $post->post_mime_type, 0, 6 ) ) {
499 ?>
500 <a href="javascript:;" data-type="image" data-id="<?php echo $post->ID; ?>"
501 class="button-secondary sm_inline_sync"><?php _e('Regenerate and Sync with GCS', ud_get_stateless_media()->domain); ?></a>
502 <?php
503 }
504
505 if ( $post && 'attachment' == $post->post_type && 'image/' != substr( $post->post_mime_type, 0, 6 ) ) {
506 ?>
507 <a href="javascript:;" data-type="other" data-id="<?php echo $post->ID; ?>"
508 class="button-secondary sm_inline_sync"><?php _e('Sync with GCS', ud_get_stateless_media()->domain); ?></a>
509 <?php
510 }
511 }
512 }
513
514 echo apply_filters( 'sm::attachment::meta', ob_get_clean(), $post->ID );
515 }
516
517 /**
518 * @param $current_path
519 * @return string
520 */
521 public function handle_root_dir( $current_path ) {
522 $root_dir = $this->get( 'sm.root_dir' );
523 $root_dir = trim( $root_dir, '/ ' ); // Remove any forward slash and empty space.
524
525 if ( !empty( $root_dir ) ) {
526 return $root_dir . '/' . $current_path;
527 }
528
529 return $current_path;
530 }
531
532 /**
533 * @param $content
534 * @return mixed
535 */
536 public function the_content_filter( $content ) {
537
538 if ( $upload_data = wp_upload_dir() ) {
539
540 if ( !empty( $upload_data['baseurl'] ) && !empty( $content ) ) {
541 $baseurl = preg_replace('/https?:\/\//','',$upload_data['baseurl']);
542 $root_dir = trim( $this->get( 'sm.root_dir' ), '/ ' ); // Remove any forward slash and empty space.
543 $root_dir = !empty( $root_dir ) ? $root_dir . '/' : false;
544 $image_host = $this->get_gs_host();
545 $file_ext = $this->replaceable_file_types();
546 $content = preg_replace( '/(href|src)=(\'|")(https?:\/\/'.str_replace('/', '\/', $baseurl).')\/(.+?)('.$file_ext.')(\'|")/i',
547 '$1=$2'.$image_host.'/'.($root_dir?$root_dir:'').'$4$5$6', $content);
548 }
549 }
550
551 return $content;
552 }
553
554 /**
555 * Return file types supported by File URL Replacement.
556 *
557 */
558 public function replaceable_file_types(){
559 $types = $this->get('sm.body_rewrite_types');
560
561 // Removing extra space.
562 $types = trim($types);
563 $types = preg_replace("/\s{2,}/", ' ', $types);
564
565 $types_arr = explode(' ', $types);
566 return '\.' . implode('|\.', $types_arr);
567 }
568
569 /**
570 * @param $value null unless other filter hooked in this function.
571 * @param $object_id post id
572 * @param $meta_key
573 * @param $single
574 * @return mixed
575 */
576 public function post_metadata_filter($value, $object_id, $meta_key, $single){
577 if(empty($value)){
578 $meta_type = 'post';
579 $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
580
581 if ( !$meta_cache ) {
582 $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
583 $meta_cache = $meta_cache[$object_id];
584 }
585
586 if ( ! $meta_key ) {
587 return $this->convert_to_gs_link($meta_cache);
588 }
589
590 if ( isset($meta_cache[$meta_key]) ) {
591 return $this->convert_to_gs_link(array_map('maybe_unserialize', $meta_cache[$meta_key]));
592 }
593
594 if ($single)
595 return '';
596 else
597 return array();
598 }
599
600 return $this->convert_to_gs_link($value);
601
602 }
603
604 /**
605 * Rplace all image link with gs link and return only if meta modified.
606 * @param $meta
607 * @return mixed
608 */
609 public function convert_to_gs_link($meta){
610 $updated = $this->_convert_to_gs_link($meta);
611 if($updated == $meta){
612 return null; // Not changed.
613 }
614 return $updated;
615 }
616
617 /**
618 * Rplace all image link with gs link
619 * @param $meta
620 * @return mixed
621 */
622 public function _convert_to_gs_link($meta){
623 if ( $meta && $upload_data = wp_upload_dir() ) {
624 if ( !empty( $upload_data['baseurl'] ) && !empty( $meta ) ) {
625 $baseurl = preg_replace('/https?:\/\//','',$upload_data['baseurl']);
626 $root_dir = trim( $this->get( 'sm.root_dir' ), '/ ' ); // Remove any forward slash and empty space.
627 $root_dir = !empty( $root_dir ) ? $root_dir . '/': false;
628 $image_host = $this->get_gs_host().'/'.($root_dir?$root_dir:'');
629
630 if(is_array($meta)){
631 foreach ($meta as $key => $value) {
632 $meta[$key] = $this->_convert_to_gs_link($value);
633 }
634 return $meta;
635 } elseif (is_object($meta) && $meta instanceof \stdClass ) {
636 foreach (get_object_vars($meta) as $key => $value) {
637 $meta->{$key} = $this->_convert_to_gs_link($value);
638 }
639 return $meta;
640 } elseif(is_string($meta)){
641 return preg_replace( '/(https?:\/\/'.str_replace('/', '\/', $baseurl).')\/(.+?)(\.jpg|\.png|\.gif|\.jpeg|\.pdf)/i', $image_host.'$2$3', $meta);
642 }
643 }
644 }
645
646 return $meta;
647 }
648
649
650 /**
651 * @param $links
652 * @param $file
653 * @return mixed
654 */
655 public function plugin_action_links( $links, $file ) {
656
657 if ($file == plugin_basename( dirname( __DIR__ ) . '/wp-stateless-media.php' ) ) {
658 $settings_link = '<a href="'. '' .'">'.__( 'Settings' , 'ssd').'</a>';
659 array_unshift( $links, $settings_link );
660 }
661
662 if ($file == plugin_basename( dirname( __DIR__ ) . '/wp-stateless.php' ) ) {
663 $settings_link = '<a href="'. '' .'">'.__( 'Settings' , 'ssd').'</a>';
664 array_unshift( $links, $settings_link );
665 }
666
667 return $links;
668 }
669
670 /**
671 * Determines if plugin is loaded via mu-plugins
672 * or Network Enabled.
673 *
674 * @author peshkov@UD
675 */
676 public function is_network_detected() {
677 /* Plugin is loaded via mu-plugins. */
678
679 if( strpos( Utility::normalize_path( $this->root_path ), Utility::normalize_path( WPMU_PLUGIN_DIR ) ) !== false ) {
680 return true;
681 }
682
683 if( is_multisite() ) {
684 /* Looks through network enabled plugins to see if our one is there. */
685 foreach (wp_get_active_network_plugins() as $path) {
686 // Trying again using readlink in case it's a symlink file.
687 // boot_file is already solved.
688 // wp_normalize_path is helpfull in windows.
689 if (wp_normalize_path($this->boot_file) == wp_normalize_path($path) || (is_link($path) AND $this->boot_file == readlink($path))) {
690 return true;
691 }
692 }
693 }
694 return false;
695 }
696
697 /**
698 * Initialization.
699 * Register scripts and styles
700 */
701 public function admin_init() {
702 wp_register_style( 'wp-stateless', $this->path( 'static/styles/wp-stateless.css', 'url' ), array(), self::$version );
703
704 /* Attachment or upload page */
705 wp_register_script( 'wp-stateless-uploads-js', $this->path( 'static/scripts/wp-stateless-uploads.js', 'url' ), array( 'jquery' ), self::$version );
706
707 /* Setup wizard styles and scripts. */
708 wp_register_style( 'wp-stateless-bootstrap', $this->path( 'static/styles/bootstrap.min.css', 'url' ), array(), '3.3.7' );
709 wp_register_style( 'bootstrap-grid-v4', $this->path( 'static/styles/bootstrap-grid.min.css', 'url' ), array(), '3.3.7' );
710 wp_register_style( 'wp-stateless-setup-wizard', $this->path( 'static/styles/wp-stateless-setup-wizard.css', 'url' ), array(), self::$version );
711
712 wp_register_script( 'async.min', ud_get_stateless_media()->path( 'static/scripts/async.js', 'url' ), array(), ud_get_stateless_media()->version );
713 wp_register_script( 'jquery.history', ud_get_stateless_media()->path( 'static/scripts/jquery.history.js', 'url' ), array( 'jquery' ), ud_get_stateless_media()->version, true );
714 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 );
715 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 );
716 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 );
717 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 );
718 wp_localize_script( 'wp-stateless-setup', 'stateless_l10n', $this->get_l10n_data() );
719
720 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 );
721
722
723 /* Stateless settings page */
724 wp_register_script( 'wp-stateless-settings', ud_get_stateless_media()->path( 'static/scripts/wp-stateless-settings.js', 'url' ), array(), ud_get_stateless_media()->version );
725 wp_localize_script( 'wp-stateless-settings', 'stateless_l10n', $this->get_l10n_data() );
726
727 wp_register_style( 'wp-stateless-settings', $this->path( 'static/styles/wp-stateless-settings.css', 'url' ), array(), self::$version );
728
729 // Sync tab
730 if ( wp_script_is( 'jquery-ui-widget', 'registered' ) ){
731 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' );
732 }
733 else{
734 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' );
735 }
736 wp_register_script( 'wp-stateless-angular', ud_get_stateless_media()->path( 'static/scripts/angular.min.js', 'url' ), array(), '1.5.0', true );
737 wp_register_script( 'wp-stateless', ud_get_stateless_media()->path( 'static/scripts/wp-stateless.js', 'url' ), array( 'jquery-ui-core' ), ud_get_stateless_media()->version, true );
738
739 wp_localize_script('wp-stateless', 'wp_stateless_settings', ud_get_stateless_media()->get('sm'));
740 wp_localize_script('wp-stateless', 'wp_stateless_compatibility', Module::get_modules());
741 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' );
742
743 }
744
745 public function get_l10n_data($value=''){
746 include ud_get_stateless_media()->path( 'l10n.php', 'dir');
747 return $l10n;
748 }
749
750 /**
751 *
752 * @todo: it should not be loaded everywhere. peshkov@UD
753 * @param $hook
754 */
755 public function admin_enqueue_scripts( $hook ) {
756
757 wp_enqueue_style( 'wp-stateless');
758
759 switch( $hook ) {
760
761 case 'options-media.php':
762 //wp_enqueue_script( 'wp-api' );
763
764 wp_enqueue_script( 'wp-stateless-setup' );
765 break;
766
767 case 'upload.php':
768
769 wp_enqueue_script( 'wp-stateless-uploads-js' );
770
771 break;
772
773 case 'post.php':
774
775 global $post;
776
777 if ( $post->post_type == 'attachment' ) {
778 wp_enqueue_script( 'wp-stateless-uploads-js' );
779 }
780
781 break;
782
783 case $this->settings->setup_wizard_ui:
784 wp_enqueue_style( 'wp-stateless-bootstrap' );
785 wp_enqueue_style( 'wp-stateless-setup-wizard' );
786
787 wp_enqueue_script( 'async.min' );
788 wp_enqueue_script( 'jquery.history' );
789 wp_enqueue_script( 'wp-stateless-validation' );
790 wp_enqueue_script( 'wp-stateless-loading' );
791 wp_enqueue_script( 'wp-stateless-comboBox' );
792 wp_enqueue_script( 'wp-stateless-setup' );
793 wp_enqueue_script( 'wp-stateless-setup-wizard-js' );
794 break;
795 case $this->settings->stateless_settings:
796 wp_enqueue_script( 'wp-stateless-settings' );
797 wp_enqueue_style( 'bootstrap-grid-v4' );
798 wp_enqueue_style( 'wp-stateless-settings' );
799
800 // Sync tab
801 wp_enqueue_script( 'jquery-ui-progressbar' );
802 wp_enqueue_script( 'wp-stateless-angular' );
803 wp_enqueue_script( 'wp-stateless' );
804 wp_enqueue_style( 'jquery-ui-regenthumbs' );
805 default: break;
806 }
807
808 }
809
810 /**
811 * Add Attributes to media HTML
812 *
813 * @author potanin@UD
814 * @param $attr
815 * @param $attachment
816 * @param $size
817 * @return mixed
818 */
819 public function wp_get_attachment_image_attributes( $attr, $attachment, $size ) {
820
821 $sm_cloud = get_post_meta( $attachment->ID, 'sm_cloud', true );
822 if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'name' ] ) ) {
823 $attr[ 'class' ] = $attr[ 'class' ] . ' wp-stateless-item';
824 $attr[ 'data-image-size' ] = is_array( $size ) ? implode( 'x', $size ) : $size;
825 $attr[ 'data-stateless-media-bucket' ] = isset( $sm_cloud[ 'bucket' ] ) ? $sm_cloud[ 'bucket' ] : false;
826 $attr[ 'data-stateless-media-name' ] = $sm_cloud[ 'name' ];
827 }
828
829 return $attr;
830
831 }
832
833 /**
834 * Adds filter link to Media Library table.
835 *
836 * @param $views
837 * @return mixed
838 */
839 public function views_upload( $views ) {
840 $views['stateless'] = '<a href="#">' . __( 'Stateless Media' ) . '</a>';
841 return $views;
842 }
843
844 /**
845 * Replace media URL
846 *
847 * @param bool $false
848 * @param integer $id
849 * @param string $size
850 * @return mixed $false
851 */
852 public function image_downsize( $false = false, $id, $size ) {
853
854 if ( !isset( $this->client ) || !$this->client || is_wp_error( $this->client ) ) {
855 return $false;
856 }
857
858 /**
859 * Check if enabled
860 */
861 if ( $this->get( 'sm.mode' ) !== 'cdn' && $this->get( 'sm.mode' ) !== 'stateless' ) {
862 return $false;
863 }
864
865 /** Start determine remote file */
866 $img_url = wp_get_attachment_url($id);
867 $meta = wp_get_attachment_metadata($id);
868 $width = $height = 0;
869 $is_intermediate = false;
870
871 //** try for a new style intermediate size */
872 if ( $intermediate = image_get_intermediate_size( $id, $size ) ) {
873 $img_url = !empty( $intermediate['gs_link'] ) ? $intermediate['gs_link'] : $intermediate['url'];
874 $width = $intermediate['width'];
875 $height = $intermediate['height'];
876 $is_intermediate = true;
877 }
878 //die( '<pre>' . print_r( $intermediate, true ) . '</pre>' );
879
880 /**
881 * maybe try to get images info from sm_cloud
882 * this case may happen when no local files
883 * @author korotkov@ud
884 */
885 if ( !$width && !$height ) {
886 $sm_cloud = get_post_meta( $id, 'sm_cloud', true );
887 if ( is_string($size) && !empty( $sm_cloud['sizes'] ) && !empty( $sm_cloud['sizes'][$size] ) ) {
888 global $_wp_additional_image_sizes;
889
890 $img_url = !empty( $sm_cloud['sizes'][$size]['fileLink'] ) ? $sm_cloud['sizes'][$size]['fileLink'] : $img_url;
891
892 if ( !empty( $_wp_additional_image_sizes[ $size ] ) ) {
893 $width = !empty( $_wp_additional_image_sizes[ $size ]['width'] ) ? $_wp_additional_image_sizes[ $size ]['width'] : $width;
894 $height = !empty( $_wp_additional_image_sizes[ $size ]['height'] ) ? $_wp_additional_image_sizes[ $size ]['height'] : $height;
895 }
896
897 $is_intermediate = true;
898 }
899 }
900
901 if ( !$width && !$height && isset( $meta['width'], $meta['height'] ) ) {
902
903 //** any other type: use the real image */
904 $width = $meta['width'];
905 $height = $meta['height'];
906 }
907
908
909 if ( $img_url) {
910
911 //** we have the actual image size, but might need to further constrain it if content_width is narrower */
912 list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
913 $img_url = apply_filters('wp_stateless_bucket_link', $img_url);
914 return array( $img_url, $width, $height, $is_intermediate );
915 }
916
917
918 /**
919 * All other cases work as usually
920 */
921 return $false;
922
923 }
924
925 /**
926 * Extends metadata by adding GS information.
927 * Note: must not be called directly. It's used only on hook
928 *
929 * @action wp_get_attachment_metadata
930 * @param $metadata
931 * @param $attachment_id
932 * @return $metadata
933 */
934 public function wp_get_attachment_metadata( $metadata, $attachment_id ) {
935 /* Determine if the media file has GS data at all. */
936 $sm_cloud = get_post_meta( $attachment_id, 'sm_cloud', true );
937 if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) {
938 $metadata[ 'gs_link' ] = apply_filters('wp_stateless_bucket_link', $sm_cloud[ 'fileLink' ]);
939 $metadata[ 'gs_name' ] = isset( $sm_cloud[ 'name' ] ) ? $sm_cloud[ 'name' ] : false;
940 $metadata[ 'gs_bucket' ] = isset( $sm_cloud[ 'bucket' ] ) ? $sm_cloud[ 'bucket' ] : false;
941 if( !empty( $metadata[ 'sizes' ] ) && is_array( $metadata[ 'sizes' ] ) ) {
942 foreach( $metadata[ 'sizes' ] as $k => $v ) {
943 if( !empty( $sm_cloud[ 'sizes' ][ $k ][ 'name' ] ) ) {
944 $metadata['sizes'][$k]['gs_name'] = $sm_cloud[ 'sizes' ][ $k ][ 'name' ];
945 $metadata['sizes'][$k]['gs_link'] = apply_filters('wp_stateless_bucket_link', $sm_cloud[ 'sizes' ][ $k ][ 'fileLink' ]);
946 }
947 }
948 }
949 }
950
951 return $metadata;
952 }
953
954 /**
955 * Returns client object
956 * or WP_Error on failure.
957 *
958 * @author peshkov@UD
959 * @return object $this->client. \wpCloud\StatelessMedia\GS_Client or \WP_Error
960 */
961 public function get_client() {
962
963 if( null === $this->client ) {
964
965 $key_json = get_site_option( 'sm_key_json' );
966 if ( empty($key_json) ) {
967 $key_json = $this->get( 'sm.key_json' );
968 }
969
970 /* Try to initialize GS Client */
971 $this->client = GS_Client::get_instance( array(
972 'bucket' => $this->get( 'sm.bucket' ),
973 'key_json' => $key_json
974 ) );
975 }
976
977 return $this->client;
978
979 }
980
981 /**
982 * Determines if we can connect to Google Storage Bucket.
983 *
984 * @author peshkov@UD
985 */
986 public function is_connected_to_gs() {
987
988 $trnst = get_transient( 'sm::is_connected_to_gs' );
989
990 if ( empty($trnst) || false === $trnst || !isset( $trnst[ 'hash' ] ) || $trnst[ 'hash' ] != md5( serialize( $this->get( 'sm' ) ) ) ) {
991 $trnst = array(
992 'success' => 'true',
993 'error' => '',
994 'hash' => md5( serialize( $this->get( 'sm' ) ) ),
995 );
996 $client = $this->get_client();
997
998 if ( is_wp_error( $client ) ) {
999 $trnst[ 'success' ] = 'false';
1000 $trnst[ 'error' ] = $client->get_error_message();
1001 } else {
1002 $connected = $client->is_connected();
1003 if( $connected !== true ) {
1004 $trnst[ 'success' ] = 'false';
1005 $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' ) );
1006
1007 if( is_callable(array($connected, 'getErrors')) && $error = $connected->getErrors() ){
1008 $error = reset($error);
1009 if($error['reason'] == 'accessNotConfigured')
1010 $trnst[ 'error' ] .= "<br>" . make_clickable($error['message']);
1011 }
1012 }
1013 }
1014 set_transient( 'sm::is_connected_to_gs', $trnst, 4 * HOUR_IN_SECONDS );
1015 }
1016
1017 if( isset( $trnst[ 'success' ] ) && $trnst[ 'success' ] == 'false' ) {
1018 return new \WP_Error( 'error', ( !empty( $trnst[ 'error' ] ) ? $trnst[ 'error' ] : __( 'There is an Error on connection to Google Storage.', $this->domain ) ) );
1019 }
1020
1021 return true;
1022 }
1023
1024 /**
1025 * Flush all plugin transients
1026 *
1027 */
1028 public function flush_transients() {
1029 delete_transient( 'sm::is_connected_to_gs' );
1030 }
1031
1032 /**
1033 * Plugin Activation
1034 *
1035 */
1036 public function activate() {
1037 add_action( 'activated_plugin', array($this, 'redirect_to_splash') );
1038 }
1039
1040 public function redirect_to_splash($plugin =''){
1041 $this->settings = new Settings();
1042
1043 if(defined( 'WP_CLI' ) || !empty($this->settings->get('key_json')) || isset($_POST['checked']) && count($_POST['checked']) > 1){
1044 return;
1045 }
1046
1047 if(
1048 empty($this->settings->get('key_json')) &&
1049 defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true &&
1050 defined('WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL') && WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL == true
1051 ) {
1052 return;
1053 }
1054
1055 if( empty($this->settings->get('key_json')) && defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true ) {
1056 $url = $this->get_settings_page_url('?page=stateless-settings');
1057 exit( wp_redirect($url));
1058 }
1059
1060 if( $plugin == plugin_basename( $this->boot_file ) ) {
1061 $url = $this->get_settings_page_url('?page=stateless-setup&step=splash-screen');
1062 if(json_decode(get_site_option('sm_key_json'))){
1063 $url = $this->get_settings_page_url('?page=stateless-settings');
1064 }
1065 exit( wp_redirect($url));
1066 }
1067 }
1068
1069 /**
1070 * Plugin Deactivation
1071 *
1072 */
1073 public function deactivate() {}
1074
1075 /**
1076 * Filter for wp_get_attachment_url();
1077 *
1078 * @param string $url
1079 * @param string $post_id
1080 * @return mixed|null|string
1081 */
1082 public function wp_get_attachment_url( $url = '', $post_id = '' ) {
1083 $sm_cloud = get_post_meta( $post_id, 'sm_cloud', 1 );
1084 if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) {
1085 $_url = parse_url($sm_cloud[ 'fileLink' ]);
1086 $url = !isset($_url['scheme']) ? ( 'https:' . $sm_cloud[ 'fileLink' ] ) : $sm_cloud[ 'fileLink' ];
1087 return apply_filters('wp_stateless_bucket_link', $url);
1088 }
1089 return $url;
1090 }
1091
1092 /**
1093 * Filter for attachment_url_to_postid()
1094 *
1095 * @param int|false $post_id originally found post ID (or false if not found)
1096 * @param string $url the URL to find the post ID for
1097 * @return int|false found post ID from cloud storage URL
1098 */
1099 public function attachment_url_to_postid( $post_id, $url ) {
1100 global $wpdb;
1101
1102 if ( ! $post_id ) {
1103 $query = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sm_cloud' AND meta_value LIKE '%s'";
1104
1105 $post_id = $wpdb->get_var( $wpdb->prepare( $query, '%' . $url . '%' ) );
1106 }
1107
1108 return $post_id;
1109 }
1110
1111 /**
1112 * Change Upload BaseURL when CDN Used.
1113 *
1114 * @param $data
1115 * @return mixed
1116 */
1117 public function upload_dir( $data ) {
1118 $data[ 'basedir' ] = $this->get_gs_host();
1119 $data[ 'baseurl' ] = $this->get_gs_host();
1120 $data[ 'url' ] = $data[ 'baseurl' ] . $data[ 'subdir' ];
1121
1122 return $data;
1123
1124 }
1125
1126 /**
1127 * Set Feature Flag constants by parsing composer.json
1128 *
1129 * @todo Make sure settings from DB can override these.
1130 *
1131 * @author potanin@UD
1132 * @return array|mixed|null|object
1133 */
1134 public function parse_feature_flags( ) {
1135
1136 try {
1137
1138 $_raw = file_get_contents( Utility::normalize_path( $this->root_path ) . 'composer.json' );
1139
1140 $_parsed = json_decode( $_raw );
1141
1142 // @todo Catch poorly formatted JSON.
1143 if( !is_object( $_parsed ) ) {
1144 // throw new Error( "unable to parse." );
1145 }
1146
1147 foreach( (array) $_parsed->extra->featureFlags as $_feature ) {
1148
1149 if( !defined( $_feature->constant ) ) {
1150 define( $_feature->constant, $_feature->enabled );
1151
1152 if( $_feature->enabled ) {
1153 Utility::log( 'Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] enabled.' );
1154 } else {
1155 Utility::log( 'Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] disabled.' );
1156 }
1157 }
1158
1159 }
1160
1161 } catch ( Exception $e ) {
1162 Utility::log( 'Unable to parse [composer.json] feature flags. Error: [' . $e->getMessage() . ']' );
1163 // echo 'Caught exception: ', $e->getMessage(), "\n";
1164 }
1165
1166
1167 return isset( $_parsed ) ? $_parsed : null;
1168
1169
1170 }
1171
1172 /**
1173 * Determine if Utility class contains missed function
1174 * in other case, just return NULL to prevent ERRORS
1175 *
1176 * @author peshkov@UD
1177 * @param $name
1178 * @param $arguments
1179 * @return mixed|null
1180 */
1181 public function __call( $name, $arguments ) {
1182 if( is_callable( array( "wpCloud\\StatelessMedia\\Utility", $name ) ) ) {
1183 return call_user_func_array( array( "wpCloud\\StatelessMedia\\Utility", $name ), $arguments );
1184 } else {
1185 return NULL;
1186 }
1187 }
1188
1189 }
1190
1191 }
1192
1193 }
1194