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

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

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