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

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