PluginProbe
WP-Stateless – Google Cloud Storage / trunk
WP-Stateless – Google Cloud Storage vtrunk
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
← All changes | lib/classes/class-bootstrap.php +1258 -628 2.2.2trunk View file →
@@ -1,16 +1,27 @@
1 1 <?php
2 +
2 3 /**
3 4 * Bootstrap
4 5 *
5 6 * @since 0.2.0
6 7 */
8 +
7 9 namespace wpCloud\StatelessMedia {
8 10
9 - if( !class_exists( 'wpCloud\StatelessMedia\Bootstrap' ) ) {
11 + use Google\Cloud\Storage\StorageClient;
12 + use Google\Auth\HttpHandler\HttpHandlerFactory;
13 + use wpCloud\StatelessMedia\Sync\FileSync;
14 + use wpCloud\StatelessMedia\Sync\ImageSync;
15 + use wpCloud\StatelessMedia\Sync\NonLibrarySync;
16 + use wpCloud\StatelessMedia\Batch\BatchTaskManager;
10 17
18 + if (!class_exists('wpCloud\StatelessMedia\Bootstrap')) {
19 +
11 20 final class Bootstrap extends \UsabilityDynamics\WP\Bootstrap_Plugin {
12 21
22 + const REQUIRED_PHP_VERSION = '5.5';
23 +
13 24 /**
14 25 * Google Storage Client
15 26 * Use $this->get_client()
16 27 *
@@ -24,9 +35,9 @@
24 35 * @static
25 36 * @property $version
26 37 * @type {Object}
27 38 */
28 - public static $version = '2.1';
39 + public static $version = '3.0';
29 40
30 41 /**
31 42 * Singleton Instance Reference.
32 43 *
@@ -36,52 +47,48 @@
36 47 * @type \wpCloud\StatelessMedia\Bootstrap object
37 48 */
38 49 protected static $instance = null;
39 50
40 -
41 51 /**
42 52 * Constructor
43 53 * Attention: MUST NOT BE CALLED DIRECTLY! USE get_instance() INSTEAD!
44 - *
54 + * @param $args
45 55 * @author peshkov@UD
46 56 */
47 - protected function __construct( $args ) {
48 - parent::__construct( $args );
49 -
57 + protected function __construct($args) {
58 + /**
59 + * Need to be loaded before plugin initialization.
60 + */
61 + if ( !(defined('WP_STATELESS_COMPATIBILITY_GAE') && !WP_STATELESS_COMPATIBILITY_GAE) ) {
62 + AppEngine::instance();
63 + }
64 +
65 + parent::__construct($args);
66 +
67 + self::$version = $args['version'] ?? self::$version;
68 +
69 + /**
70 + * Add custom args to api ping request
71 + */
72 + add_filter('ud-api-client-ping-args', function ($args, $_, $__) {
73 + $args['multisite'] = is_multisite();
74 + $args['stateless_media'] = Utility::get_stateless_media_data_count();
75 + return $args;
76 + }, 10, 3);
77 +
50 78 //** Define our Admin Notices handler object */
51 - $this->errors = new Errors( array_merge( $args, array(
79 + $this->errors = new Errors(array_merge($args, array(
52 80 'type' => $this->type
53 - ) ) );
81 + )));
54 82
55 83 // Initialize compatibility modules.
56 - add_action( 'plugins_loaded', function(){
84 + add_action('plugins_loaded', function () {
85 + Addons::instance();
57 86 new Module();
58 - });
59 - }
87 + DynamicImageSupport::instance();
88 + }, 20);
60 89
61 - /**
62 - * Instantiate class.
63 - */
64 - public function init() {
65 -
66 90 /**
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 91 * Define settings and UI.
85 92 *
86 93 * Example:
87 94 *
@@ -90,37 +97,67 @@
90 97 *
91 98 * Manually Update/Add option
92 99 * $this->set( 'sm.client_id', 'zxcvv12adffse' );
93 100 */
94 - $this->settings = new Settings();
101 + $this->settings = new Settings($this);
102 + Status::instance();
103 + }
95 104
96 - // Invoke REST API
97 - add_action( 'rest_api_init', array( $this, 'api_init' ) );
105 + /**
106 + * Prevent loading of textdomain
107 + */
108 + public function load_textdomain() {
109 + }
98 110
111 + /**
112 + * Instantiate class.
113 + */
114 + public function init() {
115 + // Parse feature flags, set constants.
116 + $this->parse_feature_flags();
117 + $sm_mode = $this->get('sm.mode');
118 +
99 119 /**
100 - * Register SM metaboxes
120 + * Send admin email
101 121 */
102 - add_action( 'admin_init', array( $this, 'register_metaboxes' ) );
122 + add_action('wp_stateless_send_admin_email', array($this, 'send_admin_email'), 10, 3);
103 123
124 + // Should be created unconditionally and as early as possible to handle batch migration requests
125 + Migrator::instance();
126 + BatchTaskManager::instance();
127 +
128 + new SyncNonMedia();
129 +
130 + ImageSync::instance();
131 + FileSync::instance();
132 + NonLibrarySync::instance();
133 +
134 + // Invoke REST API
135 + add_action('rest_api_init', array($this, 'api_init'));
136 +
137 + // Register meta boxes and fields for media edit page
138 + add_filter('rwmb_meta_boxes', array($this, 'attachment_meta_box_callback'));
139 +
140 + // Register meta boxes and fields for media modal page
141 + add_filter('attachment_fields_to_edit', array($this, 'attachment_modal_meta_box_callback'), 11, 2);
142 +
143 + // Get the REST API root
144 + add_filter('wp_stateless_rest_api_root', array($this, 'get_rest_api_root'));
145 +
104 146 /**
105 147 * Init hook
106 148 */
107 - add_action( 'admin_init', array( $this, 'admin_init' ) );
149 + add_action('admin_init', array($this, 'admin_init'));
108 150
109 151 /**
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 152 * Handle switch blog properly.
116 153 */
117 - add_action( 'switch_blog', array( $this, 'on_switch_blog' ), 10, 2 );
154 + add_action('switch_blog', array($this, 'on_switch_blog'), 10, 2);
118 155
119 156 /**
120 157 * Filter for getting stateless settings
121 158 */
122 - add_filter( 'stateless::get_settings', array( $this, 'get_settings' ), 10);
159 + add_filter('stateless::get_settings', array($this, 'get_settings'), 10);
123 160
124 161 /**
125 162 * Init AJAX jobs
126 163 */
@@ -128,96 +165,211 @@
128 165
129 166 /**
130 167 * Load WP-CLI Commands
131 168 */
132 - if( defined( 'WP_CLI' ) && WP_CLI ) {
169 + if (defined('WP_CLI') && WP_CLI) {
133 170 include_once($this->path('lib/cli/class-sm-cli-command.php', 'dir'));
134 171 }
135 172
136 - $this->is_network_detected();
173 + /**
174 + * Add scripts
175 + */
176 + add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
137 177
138 178 /**
139 - * Add scripts
179 + * Delete table when blog is deleted.
140 180 */
141 - add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
181 + add_action('wp_delete_site', array($this, 'wp_delete_site'));
142 182
143 183 /**
144 - * Hashify file name if option is enabled
184 + * To prevent fatal errors for users who use PHP 5.5 or less.
145 185 */
146 - if ( $this->get( 'sm.hashify_file_name' ) == 'true' ) {
147 - add_filter('sanitize_file_name', array( 'wpCloud\StatelessMedia\Utility', 'randomize_filename' ), 10);
186 + if (version_compare(PHP_VERSION, self::REQUIRED_PHP_VERSION, '<')) {
187 + $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>' . self::REQUIRED_PHP_VERSION . '</b>', '<b>' . PHP_VERSION . '</b>'));
148 188 }
149 189
190 + /**
191 + * Add the currently processing nag
192 + */
193 + foreach (Utility::get_available_sync_classes() as $process) {
194 + if ($process->is_running()) {
195 + $this->errors->add([
196 + 'title' => __('Media Library Synchronization Underway', ud_get_stateless_media()->domain),
197 + 'message' => __('WP-Stateless is synchronizing your media library in accordance with the Mode setting. You can view progress or stop the process via the WP-Stateless Sync settings area.', ud_get_stateless_media()->domain),
198 + 'button' => __('View Synchronization', ud_get_stateless_media()->domain),
199 + 'button_link' => admin_url('upload.php?page=stateless-settings&tab=stless_sync_tab'),
200 + 'key' => 'processing-in-progress'
201 + ], 'message');
202 + break;
203 + }
204 + }
205 +
150 206 /* Initialize plugin only if Mode is not 'disabled'. */
151 - if ( $this->get( 'sm.mode' ) !== 'disabled' ) {
207 + if (($sm_mode !== 'disabled' && $sm_mode !== 'stateless') || ($sm_mode === 'stateless' && (wp_doing_ajax() || wp_doing_cron()))) {
152 208
153 209 /**
154 - * Override Cache Control is option is enabled
210 + * Determine if we have issues with connection to Google Storage Bucket
211 + * if SM is not disabled.
155 212 */
156 - $cacheControl = trim($this->get( 'sm.cache_control' ));
157 - if ( !empty($cacheControl) ) {
158 - add_filter( 'sm:item:cacheControl', array( $this, 'override_cache_control' ) );
213 + $is_connected = $this->is_connected_to_gs();
214 +
215 + if (is_wp_error($is_connected)) {
216 + $this->errors->add($is_connected->get_error_message(), 'warning', false);
159 217 }
160 218
219 + if ($googleSDKVersionConflictError = get_transient("wp_stateless_google_sdk_conflict")) {
220 + $this->errors->add($googleSDKVersionConflictError, 'warning', false);
221 + }
222 +
161 223 /**
224 + * Carry on only if we do not have errors.
225 + */
226 + if (!$this->has_errors()) {
227 +
228 + if (in_array($sm_mode, array('cdn', 'ephemeral', 'stateless'))) {
229 + /**
230 + * init main filters
231 + */
232 + $this->_init_filters('main');
233 + }
234 +
235 + if ($sm_mode === 'ephemeral' || $sm_mode === 'stateless') {
236 + // Store attachment id in a static variable on 'intermediate_image_sizes_advanced' filter.
237 + // Utility::store_can_delete_attachment();
238 + if (function_exists('is_wp_version_compatible') && is_wp_version_compatible('5.3-RC4-46673')) {
239 + add_filter('intermediate_image_sizes_advanced', array('wpCloud\StatelessMedia\Utility', 'store_can_delete_attachment'), 10, 3);
240 + }
241 + }
242 +
243 + if ($sm_mode === 'stateless') {
244 + /**
245 + * Replacing local path to gs:// for using it on StreamWrapper
246 + */
247 + add_filter('upload_dir', array($this, 'filter_upload_dir'), 99);
248 +
249 + /**
250 + * Stateless mode working only with GD library
251 + */
252 + add_filter('wp_image_editors', array($this, 'select_wp_image_editors'));
253 +
254 + /**
255 + * Init GS client
256 + */
257 + global $gs_client;
258 + if ($gs_client = $this->init_gs_client()) {
259 + StreamWrapper::register($gs_client);
260 + }
261 + }
262 +
263 + if ($this->get('sm.delete_remote') == 'true') {
264 + /**
265 + * On physical file deletion we remove any from GS
266 + * We need priority grater than default (10) for ShortPixel plugin to work properly.
267 + */
268 + add_filter('delete_attachment', array($this, 'remove_media'), 11);
269 + }
270 +
271 + /**
272 + * init client's filters
273 + */
274 + $this->_init_filters('client');
275 + }
276 + } elseif ($sm_mode == 'stateless') {
277 + /**
162 278 * Determine if we have issues with connection to Google Storage Bucket
163 279 * if SM is not disabled.
164 280 */
165 281 $is_connected = $this->is_connected_to_gs();
166 282
167 - if ( is_wp_error( $is_connected ) ) {
168 - $this->errors->add( $is_connected->get_error_message() );
283 + if (is_wp_error($is_connected)) {
284 + $this->errors->add($is_connected->get_error_message(), 'warning', false);
169 285 }
170 286
171 - if ( $googleSDKVersionConflictError = get_transient( "wp_stateless_google_sdk_conflict" ) ) {
172 - $this->errors->add( $googleSDKVersionConflictError, 'warning' );
287 + if ($googleSDKVersionConflictError = get_transient("wp_stateless_google_sdk_conflict")) {
288 + $this->errors->add($googleSDKVersionConflictError, 'warning', false);
173 289 }
174 290
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 291 /**
184 292 * Carry on only if we do not have errors.
185 293 */
186 - if( !$this->has_errors() ) {
294 + if (!$this->has_errors()) {
295 + /**
296 + * Replacing local path to gs:// for using it on StreamWrapper
297 + */
298 + add_filter('upload_dir', array($this, 'filter_upload_dir'), 99);
187 299
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 );
300 + /**
301 + * Stateless mode working only with GD library
302 + */
303 + add_filter('wp_image_editors', array($this, 'select_wp_image_editors'));
192 304
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 - }
305 + /**
306 + * Init GS client
307 + */
308 + global $gs_client;
309 + if ($gs_client = $this->init_gs_client()) {
310 + StreamWrapper::register($gs_client);
311 + }
196 312
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 - }
313 + /**
314 + * init client's filters
315 + */
316 + $this->_init_filters('client');
200 317
201 - add_filter( 'wp_stateless_bucket_link', array( $this, 'wp_stateless_bucket_link' ) );
318 + /**
319 + * init main filters
320 + */
321 + $this->_init_filters('main');
322 + }
323 + }
324 + }
325 +
326 + /**
327 + * Init additional filters which uses on all modes
328 + * @param string $type
329 + */
330 + private function _init_filters($type = '') {
331 + switch ($type) {
332 + case 'main':
333 + add_filter('wp_get_attachment_image_attributes', array($this, 'wp_get_attachment_image_attributes'), 20, 3);
334 + add_filter('wp_get_attachment_url', array($this, 'wp_get_attachment_url'), 20, 2);
335 + add_filter('get_attached_file', array($this, 'get_attached_file'), 9, 2);
336 + add_filter('attachment_url_to_postid', array($this, 'attachment_url_to_postid'), 20, 2);
337 +
338 + if ($this->get('sm.body_rewrite') == 'true' || $this->get('sm.body_rewrite') == 'enable_editor') {
339 + add_filter('the_content', array($this, 'the_content_filter'), 99);
202 340 }
203 341
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 - }
342 + if ($this->get('sm.body_rewrite') == 'true' || $this->get('sm.body_rewrite') == 'enable_meta') {
343 + add_filter('get_post_metadata', array($this, 'post_metadata_filter'), 2, 4);
208 344 }
209 345
346 + add_filter('wp_stateless_bucket_link', array($this, 'wp_stateless_bucket_link'));
347 + break;
348 + case 'client':
210 349 /**
211 - * Rewrite Image URLS
350 + * Add custom actions to media rows
212 351 */
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 );
352 + add_filter('media_row_actions', array($this, 'add_custom_row_actions'), 10, 3);
215 353
216 354 /**
355 + * Hashify file name if option is enabled
356 + */
357 + if ($this->get('sm.hashify_file_name') == 'true') {
358 + add_filter('sanitize_file_name', array('wpCloud\StatelessMedia\Utility', 'randomize_filename'), 10);
359 + }
360 +
361 + /**
362 + * Override Cache Control if option is enabled
363 + */
364 + add_filter('sm:item:cacheControl', array($this->settings, 'override_cache_control'));
365 +
366 + add_filter('wp_stateless_file_name', array($this, 'handle_root_dir'), 10, 4);
367 +
368 + /**
217 369 * Extends metadata by adding GS information.
218 370 */
219 - add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
371 + add_filter('wp_get_attachment_metadata', array($this, 'wp_get_attachment_metadata'), 10, 2);
220 372
221 373 /**
222 374 * Add/Edit Media
223 375 *
@@ -223,81 +375,167 @@
223 375 *
224 376 * Once added or edited we can get into Attachment ID then get all image sizes and sync them with GS
225 377 * We can't use this. That's prevent removing this filter.
226 378 */
227 - add_filter( 'wp_update_attachment_metadata', array( 'wpCloud\StatelessMedia\Utility', 'add_media' ), 999, 2 );
228 -
379 + add_filter('wp_update_attachment_metadata', array('wpCloud\StatelessMedia\Utility', 'add_media'), 999, 2);
380 +
229 381 /**
230 - * Upload the full size image first.
231 - *
382 + * Delete original in the end of generating metadata
383 + * it does not work with PDF
232 384 */
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 - }
385 + // add_filter('wp_generate_attachment_metadata', function ($metadata, $attachment_id, $state) {
386 + // if (!in_array(ud_get_stateless_media()->get('sm.mode'), array('ephemeral'))) return $metadata;
387 + // @unlink(get_attached_file($attachment_id, true));
388 + // return $metadata;
389 + // }, 99, 3);
236 390
237 391 /**
238 - * Add Media
239 - *
240 - * Once added we can get into Attachment ID then get all image sizes and sync them with GS
392 + * Rewrite Image URLS
241 393 */
242 - // add_filter( 'wp_generate_attachment_metadata', array( $this, 'add_media' ), 100, 2 );
394 + add_filter('image_downsize', array($this, 'image_downsize'), 99, 3);
395 + add_filter('wp_calculate_image_srcset', array($this, 'wp_calculate_image_srcset'), 10, 5);
243 396
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 - }
397 + /**
398 + * Trigger module initialization and registration.
399 + */
400 + do_action('sm::module::init', $this->get('sm'));
250 401
251 - // Trigger module initialization and registration.
252 - do_action('sm::module::init', $this->get( 'sm' ));
253 - }
402 + break;
403 + case 'default':
404 + break;
405 + }
406 + }
254 407
408 + /**
409 + * The default $editors value:
410 + *
411 + * array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' )
412 + * @param $editors
413 + * @return array
414 + */
415 + public function select_wp_image_editors($editors) {
416 + return array('WP_Image_Editor_GD');
417 + }
418 +
419 + /**
420 + * @param callable|null $httpHandler
421 + * @return StorageClient
422 + * @throws \Exception
423 + */
424 + public function init_gs_client(?callable $httpHandler = null) {
425 + // May be Loading Google SDK....
426 + if (!class_exists('HttpHandlerFactory')) {
427 + include_once(ud_get_stateless_media()->path('lib/Google/vendor/autoload.php', 'dir'));
255 428 }
429 +
430 + $httpHandler = $httpHandler ? $httpHandler : HttpHandlerFactory::build();
431 +
432 + $json_key = json_decode($this->settings->get('sm.key_json'), true);
433 +
434 + if (!empty($json_key)) {
435 + return new StorageClient(
436 + [
437 + 'keyFile' => $json_key,
438 + 'httpHandler' => function ($request, $options) use ($httpHandler) {
439 + $xGoogApiClientHeader = $request->getHeaderLine('x-goog-api-client');
440 + $request = $request->withHeader('x-goog-api-client', $xGoogApiClientHeader);
441 +
442 + return call_user_func_array($httpHandler, [$request, $options]);
443 + },
444 + 'authHttpHandler' => HttpHandlerFactory::build(),
445 + ]
446 + );
447 + }
256 448 }
257 -
449 +
258 450 /**
451 + * Replacing root dir with GCS path
452 + * @param $uploads
453 + * @return array
454 + */
455 + public function filter_upload_dir($uploads) {
456 + global $default_dir;
457 + if ($default_dir) return $uploads;
458 + //Bucket
459 + $bucket = $this->get('sm.bucket');
460 +
461 + //Bucket folder path
462 + $root_dir = $this->get('sm.root_dir');
463 + $root_dir = apply_filters("wp_stateless_handle_root_dir", $root_dir);
464 +
465 + /**
466 + * Subdir not uses on Stateless mode
467 + */
468 + $uploads['subdir'] = '';
469 +
470 + $basedir = rtrim(sprintf('gs://%s/%s', $bucket, $root_dir), '/');
471 + $baseurl = rtrim(sprintf('https://storage.googleapis.com/%s/%s', $bucket, $root_dir), '/');
472 +
473 + $uploads = array(
474 + 'url' => rtrim($baseurl . $uploads['subdir'], '/'),
475 + 'path' => $basedir . $uploads['subdir'],
476 + 'subdir' => $uploads['subdir'],
477 + 'basedir' => $basedir,
478 + 'baseurl' => $baseurl,
479 + 'error' => false,
480 + );
481 + return $uploads;
482 + }
483 +
484 + /**
259 485 * Rebuild srcset from gs_link.
260 486 * Using calculations returned from WordPress wp_calculate_image_srcset()
261 - *
487 + *
488 + * @param $sources
489 + * @param $size_array
490 + * @param $image_src
491 + * @param $image_meta
492 + * @param $attachment_id
493 + * @return array
262 494 */
263 - public function wp_calculate_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id){
495 + public function wp_calculate_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) {
496 + $sm_mode = $this->get('sm.mode');
497 +
498 + /**
499 + * In Backup mode using local URL
500 + */
501 + if ("backup" == $sm_mode) {
502 + return $sources;
503 + }
504 +
264 505 if (empty($image_meta['gs_link'])) {
265 506 $image_meta = wp_get_attachment_metadata($attachment_id);
266 507 }
267 508
268 - if(is_array($sources) && !empty( $image_meta['gs_link'] )){
509 + if (is_array($sources) && !empty($image_meta['gs_link'])) {
269 510 $gs_name = $image_meta['gs_name'];
270 511 // getting position of root_dir in gs_name.
271 512 $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){
513 + // removing rood_dir from gs_name so we can compare to replace url with gs_link.
514 + if ($root_dir_pos !== false) {
274 515 $gs_name = substr($gs_name, $root_dir_pos);
275 516 }
276 517
518 + if (!isset($gs_name) || empty($gs_name)) {
519 + return [];
520 + }
521 +
277 522 foreach ($sources as $width => &$image) {
278 - if (!isset($image_meta['gs_name']) || empty($image_meta['gs_name'])) {
279 - continue;
280 - }
281 523
282 524 // If srcset includes original image src, replace it
283 525 if (substr_compare($image['url'], $gs_name, -strlen($gs_name)) === 0) {
284 526 $image['url'] = $image_meta['gs_link'];
285 - // Replace all sizes
527 + // Replace all sizes
286 528 } elseif (isset($image_meta['sizes']) && is_array($image_meta['sizes'])) {
287 529 $found = false;
288 530 foreach ($image_meta['sizes'] as $key => $meta) {
289 531 if (!isset($meta['gs_name']) || empty($meta['gs_name'])) {
290 - // if mode is stateless and nothing to show for srcset item - unset that item
291 - if ( $this->get( 'sm.mode' ) === 'stateless' ) {
292 - $image = null;
293 - }
294 532 continue;
295 533 }
296 534
297 535 $thumb_gs_name = $meta['gs_name'];
298 536 // removing rood_dir from gs_name
299 - if($root_dir_pos !== false){
537 + if ($root_dir_pos !== false) {
300 538 $thumb_gs_name = substr($thumb_gs_name, $root_dir_pos);
301 539 }
302 540
303 541 if (substr_compare($image['url'], $thumb_gs_name, -strlen($thumb_gs_name)) === 0) {
@@ -306,22 +544,27 @@
306 544 break;
307 545 }
308 546 }
309 547
310 - // if no size found and mode is stateless and nothing to show for srcset item - unset that item
311 - if (!$found && $this->get( 'sm.mode' ) === 'stateless') {
548 + // if no size found and mode is ephemeral or stateless and nothing to show for srcset item - unset that item
549 + if (!$found && ($sm_mode === 'ephemeral' || $sm_mode === 'stateless')) {
312 550 $image = null;
313 551 }
314 552 } else {
315 553 // if mode is stateless and nothing to show for srcset item - unset that item
316 - if ( $this->get( 'sm.mode' ) === 'stateless' ) {
554 + if ($sm_mode === 'ephemeral' || $sm_mode === 'stateless') {
317 555 $image = null;
318 556 }
319 557 }
320 558 }
559 + } elseif (is_array($sources) && $sm_mode === 'stateless') {
560 + foreach ($sources as $width => &$image) {
561 + // Set default src
562 + $image['url'] = $image_src;
563 + }
321 564 }
322 565
323 - return array_filter( $sources );
566 + return is_array($sources) ? array_filter($sources) : $sources;
324 567 }
325 568
326 569 /**
327 570 * Return gs host.
@@ -331,9 +574,9 @@
331 574 * @param array $sm
332 575 * @return mixed|void
333 576 */
334 577 public function get_gs_host($sm = array()) {
335 - $sm = $sm?$sm: $this->get( 'sm');
578 + $sm = $sm ? $sm : $this->get('sm');
336 579 $image_host = 'https://storage.googleapis.com/';
337 580 $image_host .= $sm['bucket'];
338 581
339 582 $custom_domain = $sm['custom_domain'];
@@ -340,17 +583,32 @@
340 583 $is_ssl = strpos($custom_domain, 'https://');
341 584 $custom_domain = str_replace(array('http://', 'https://'), '', $custom_domain);
342 585 $custom_domain = trim($custom_domain, '/');
343 586
344 - if ( !empty($sm['bucket']) && !empty($custom_domain) && $custom_domain !== 'storage.googleapis.com' && ( $is_ssl === 0 || $custom_domain != $sm['bucket'] ) ) {
587 + // checking whether the provided domain is valid.
588 + // if the custom domain is same as the bucket name
589 + // or the custom domain is using https.
590 + if (!empty($sm['bucket']) && !empty($custom_domain) && $custom_domain !== 'storage.googleapis.com' && ($is_ssl === 0 || $custom_domain == $sm['bucket'])) {
345 591 $image_host = $is_ssl === 0 ? 'https://' : 'http://'; // bucketname will be host
346 592 $image_host .= $custom_domain;
347 593 }
348 594
349 - return apply_filters( 'get_gs_host', $image_host, $image_host, $sm['bucket'], $is_ssl, $sm );
595 + return apply_filters('get_gs_host', $image_host, $image_host, $sm['bucket'], $is_ssl, $sm);
350 596 }
351 597
352 598 /**
599 + * Return gs:// path.
600 + *
601 + * @param array $sm
602 + * @return mixed|void
603 + */
604 + public function get_gs_path() {
605 + $path = 'gs://' . $this->get('sm.bucket');
606 +
607 + return apply_filters('get_gs_path', $path);
608 + }
609 +
610 + /**
353 611 * Filter for wp_stateless_bucket_link if custom domain is set.
354 612 * It's get attachment url and remove "storage.googleapis.com" from url.
355 613 * So that custom url can be used.
356 614 *
@@ -357,20 +615,19 @@
357 615 * @param $fileLink
358 616 * @return mixed|string
359 617 */
360 618 public function wp_stateless_bucket_link($fileLink) {
361 - $bucketname = $this->get( 'sm.bucket' );
362 - $custom_domain = $this->get( 'sm.custom_domain' );
619 + $bucketname = $this->get('sm.bucket');
620 + $custom_domain = $this->get('sm.custom_domain');
363 621 $is_ssl = strpos($custom_domain, 'https://') === 0;
364 622 $fileLink_is_ssl = strpos($fileLink, 'https://') === 0;
365 623 $custom_domain = str_replace(array('http://', 'https://'), '', $custom_domain);
366 624 $custom_domain = trim($custom_domain, '/');
367 -
368 - if ( $custom_domain !== 'storage.googleapis.com' && $custom_domain == $bucketname && strpos($fileLink, $bucketname) > 8 ) {
625 +
626 + if (!empty($bucketname) && $custom_domain !== 'storage.googleapis.com' && $custom_domain == $bucketname && strpos($fileLink, $bucketname) > 8) {
369 627 $fileLink = ($is_ssl ? 'https://' : 'http://') . substr($fileLink, strpos($fileLink, $bucketname));
370 - }
371 - elseif( $custom_domain !== 'storage.googleapis.com' && $custom_domain == $bucketname && $fileLink_is_ssl !== $is_ssl){
372 - if($is_ssl)
628 + } elseif ($custom_domain !== 'storage.googleapis.com' && $custom_domain == $bucketname && $fileLink_is_ssl !== $is_ssl) {
629 + if ($is_ssl)
373 630 $fileLink = str_replace(array('http://', 'https://'), 'https://', $fileLink);
374 631 else
375 632 $fileLink = str_replace(array('http://', 'https://'), 'http://', $fileLink);
376 633 }
@@ -382,19 +639,31 @@
382 639 *
383 640 * @param string $path
384 641 * @return string
385 642 */
386 - public function get_settings_page_url( $path = '' ) {
387 - $url = get_admin_url( get_current_blog_id(), ( is_network_admin() ? 'network/settings.php' : 'upload.php' ) );
643 + public function get_settings_page_url($path = '') {
644 + $url = get_admin_url(get_current_blog_id(), (is_network_admin() ? 'network/settings.php' : 'upload.php'));
388 645 return $url . $path;
389 646 }
390 647
391 648 /**
649 + * Return docs page url.
650 + *
651 + * @param string $path
652 + * @return string
653 + */
654 + public function get_docs_page_url($path = '') {
655 + $path = ltrim($path, '/');
656 +
657 + return 'https://stateless.udx.io/' . $path;
658 + }
659 +
660 + /**
392 661 * Get new blog settings once switched blog.
393 662 * @param $new_blog
394 663 * @param $prev_blog_id
395 664 */
396 - public function on_switch_blog( $new_blog, $prev_blog_id ) {
665 + public function on_switch_blog($new_blog, $prev_blog_id) {
397 666 $this->settings->refresh();
398 667 }
399 668
400 669 /**
@@ -406,37 +675,37 @@
406 675 */
407 676 public function get_settings($settings) {
408 677
409 678 $settings_list = array(
410 - 'mode',
411 - 'body_rewrite',
412 - 'body_rewrite_types',
413 - 'bucket',
414 - 'root_dir',
415 - 'key_json',
416 - 'cache_control',
417 - 'delete_remote',
418 - 'custom_domain',
419 - 'organize_media',
420 - 'hashify_file_name'
679 + 'mode',
680 + 'body_rewrite',
681 + 'body_rewrite_types',
682 + 'bucket',
683 + 'root_dir',
684 + 'key_json',
685 + 'cache_control',
686 + 'delete_remote',
687 + 'custom_domain',
688 + 'organize_media',
689 + 'hashify_file_name'
421 690 );
422 691
423 692 foreach ($settings_list as $setting) {
424 693
425 694 /** If setting is already exist, just skip it */
426 - if( isset( $settings[ $setting ] ) ) {
695 + if (isset($settings[$setting])) {
427 696 continue;
428 697 }
429 698
430 - $value = $this->get( 'sm.' . $setting );
699 + $value = $this->get('sm.' . $setting);
431 700
432 701 /** Decode json to array */
433 - if( $value && is_string( $value ) && $setting === 'key_json' ) {
434 - $value = json_decode( $value, true );
702 + if ($value && is_string($value) && $setting === 'key_json') {
703 + $value = json_decode($value, true);
435 704 $setting = 'key';
436 705 }
437 706
438 - $settings[ $setting ] = $value;
707 + $settings[$setting] = $value;
439 708 }
440 709
441 710 return $settings;
442 711 }
@@ -442,8 +711,9 @@
442 711 }
443 712
444 713 /**
445 714 * Remove all settings.
715 + * @param bool $network
446 716 */
447 717 public function reset($network = false) {
448 718 $this->settings->reset($network);
449 719 }
@@ -453,44 +723,33 @@
453 723 * @param $post
454 724 * @param $detached
455 725 * @return mixed
456 726 */
457 - public function add_custom_row_actions( $actions, $post, $detached ) {
727 + public function add_custom_row_actions($actions, $post, $detached) {
458 728
459 - if ( !current_user_can( 'upload_files' ) ) return $actions;
729 + if (!current_user_can('upload_files')) return $actions;
460 730
461 - if ( $post && 'attachment' == $post->post_type && 'image/' == substr( $post->post_mime_type, 0, 6 ) ) {
462 - $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>';
731 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $post->ID);
732 +
733 +
734 + $sm_mode = $this->get('sm.mode');
735 + if (!empty($sm_cloud) && $sm_mode === 'stateless') return $actions;
736 +
737 + if ($post && 'attachment' == $post->post_type && 'image/' == substr($post->post_mime_type, 0, 6)) {
738 + $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>';
463 739 }
464 740
465 - if ( $post && 'attachment' == $post->post_type && 'image/' != substr( $post->post_mime_type, 0, 6 ) ) {
466 - $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>';
741 + if ($post && 'attachment' == $post->post_type && 'image/' != substr($post->post_mime_type, 0, 6)) {
742 + $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>';
467 743 }
468 744
469 745 return $actions;
470 -
471 746 }
472 747
473 748 /**
474 - * Register metaboxes
475 - */
476 - public function register_metaboxes() {
477 - add_meta_box(
478 - 'sm-attachment-metabox',
479 - __( 'Google Cloud Storage', ud_get_stateless_media()->domain ),
480 - array($this, 'attachment_meta_box_callback'),
481 - 'attachment',
482 - 'side',
483 - 'low'
484 - );
485 - }
486 -
487 - /**
488 749 * Define REST API.
489 750 *
490 - * // https://usabilitydynamics-sandbox-uds-io-stateless-testing.c.rabbit.ci/wp-json/wp-stateless/v1
491 - *
492 - * @author potanin@UD
751 + * @author korotkov@UD
493 752 */
494 753 public function api_init() {
495 754
496 755 $route_namespace = 'wp-stateless/v1';
@@ -495,127 +754,307 @@
495 754
496 755 $route_namespace = 'wp-stateless/v1';
497 756 $api_namespace = 'wpCloud\StatelessMedia\API';
498 757
499 - register_rest_route( $route_namespace, '/status', array(
500 - 'methods' => 'GET',
501 - 'callback' => array( $api_namespace, 'status' ),
502 - ) );
758 + register_rest_route($route_namespace, '/status', array(
759 + 'methods' => \WP_REST_Server::READABLE,
760 + 'callback' => array($api_namespace, 'status'),
761 + 'permission_callback' => '__return_true'
762 + ));
503 763
504 - register_rest_route( $route_namespace, '/jobs', array(
505 - 'methods' => 'GET',
506 - 'callback' => array( $api_namespace, 'jobs' ),
507 - ) );
764 + register_rest_route($route_namespace, '/getSettings', array(
765 + 'methods' => \WP_REST_Server::READABLE,
766 + 'callback' => array($api_namespace, 'getSettings'),
767 + 'permission_callback' => array($api_namespace, 'authCheck')
768 + ));
508 769
509 - /**
510 - * Return stateless settings.
511 - *
512 - * Request parameter: none
513 - *
514 - * Response:
515 - * ok: Whether API is up or not
516 - * message: Describe what is done or error message on error.
517 - * settings: array of stateless settings
518 - *
519 - */
520 - register_rest_route( $route_namespace, '/getSettings', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getSettings' ), ) );
770 + register_rest_route($route_namespace, '/updateSettings', array(
771 + 'methods' => \WP_REST_Server::CREATABLE,
772 + 'callback' => array($api_namespace, 'updateSettings'),
773 + 'permission_callback' => array($api_namespace, 'authCheck')
774 + ));
521 775
522 - /**
523 - * Essentially for scrolling through media library to build our index.
524 - *
525 - * Request parameter: none
526 - *
527 - * Response:
528 - * ok: Whether API is up or not
529 - * message: Describe what is done or error message on error.
530 - * settings: array of media files
531 - *
532 - */
533 - register_rest_route( $route_namespace, '/getMediaLibrary', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getMediaLibrary' ), ) );
776 + register_rest_route($route_namespace, '/sync/getProcesses', array(
777 + 'methods' => \WP_REST_Server::READABLE,
778 + 'callback' => array($api_namespace, 'syncGetProcesses'),
779 + 'permission_callback' => array($api_namespace, 'authCheck')
780 + ));
534 781
535 - /**
536 - * Get detailed information of media file
537 - *
538 - * Request parameter: none
539 - *
540 - * Response:
541 - * ok: Whether API is up or not
542 - * message: Describe what is done or error message on error.
543 - * settings: media file array
544 - *
545 - */
546 - register_rest_route( $route_namespace, '/getMediaItem', array( 'methods' => 'GET', 'callback' => array( $api_namespace, 'getMediaItem' ), ) );
782 + register_rest_route($route_namespace, '/sync/getProcess/(?P<id>\S+)', array(
783 + 'methods' => \WP_REST_Server::READABLE,
784 + 'callback' => array($api_namespace, 'syncGetProcess'),
785 + 'permission_callback' => array($api_namespace, 'authCheck')
786 + ));
547 787
788 + register_rest_route($route_namespace, '/sync/run', array(
789 + 'methods' => \WP_REST_Server::CREATABLE,
790 + 'callback' => array($api_namespace, 'syncRun'),
791 + 'permission_callback' => array($api_namespace, 'authCheck')
792 + ));
793 +
794 + register_rest_route($route_namespace, '/sync/stop', array(
795 + 'methods' => \WP_REST_Server::CREATABLE,
796 + 'callback' => array($api_namespace, 'syncStop'),
797 + 'permission_callback' => array($api_namespace, 'authCheck')
798 + ));
799 +
800 + register_rest_route($route_namespace, '/batch/state', array(
801 + 'methods' => \WP_REST_Server::READABLE,
802 + 'callback' => array($api_namespace, 'batchState'),
803 + 'permission_callback' => array($api_namespace, 'authCheck')
804 + ));
805 +
806 + register_rest_route($route_namespace, '/batch/action', array(
807 + 'methods' => \WP_REST_Server::CREATABLE,
808 + 'callback' => array($api_namespace, 'batchAction'),
809 + 'permission_callback' => array($api_namespace, 'authCheck')
810 + ));
811 + }
812 +
813 + /**
814 + * Metabox for media modal page
815 + * @param $form_fields
816 + * @param $post
817 + * @return array
818 + */
819 + public function attachment_modal_meta_box_callback($form_fields, $post) {
820 + // Do not show on media edit page, only on modal
821 + // Do not show if we are not in Media Library
822 + if ( isset($_GET['post']) || wp_doing_ajax() ) {
823 + return $form_fields;
824 + }
825 +
826 + $link = get_edit_post_link($post->ID);
827 +
828 + $form_field['label'] = '';
829 + $form_field['input'] = 'html';
830 + $form_field['html'] = sprintf("<script>jQuery('.sm_edit_links').remove();jQuery('.actions').prepend('<a class=\"sm_edit_links\" href=\"%s#sm-attachment-metabox\">%s</a> | ')</script>", $link, __("View stateless meta", ud_get_stateless_media()->domain));
831 + $form_field['show_in_modal'] = true;
832 +
833 + $form_fields['sm_html'] = $form_field;
834 + return $form_fields;
548 835 }
549 836
550 837 /**
551 - * @param $post
838 + * Metabox for media edit page
839 + * @param $meta_boxes
840 + * @return array
552 841 */
553 - public function attachment_meta_box_callback( $post ) {
554 - ob_start();
842 + public function attachment_meta_box_callback($meta_boxes) {
843 + $post_id = false;
844 + if (isset($_GET['post'])) {
845 + $post_id = intval($_GET['post']);
846 + } elseif (isset($_POST['post_ID'])) {
847 + $post_id = intval($_POST['post_ID']);
848 + } elseif (isset($_GET['item'])) {
849 + $post_id = intval($_GET['item']);
850 + } elseif (isset($_POST['item'])) {
851 + $post_id = intval($_POST['item']);
852 + }
555 853
556 - $sm_cloud = get_post_meta( $post->ID, 'sm_cloud', 1 );
854 + return $this->_prepare_data_for_metabox($meta_boxes, $post_id);
855 + }
557 856
558 - if ( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) { ?>
857 + /**
858 + * Prepare data for metabox fields
859 + * @param $meta_boxes
860 + * @param $post_id
861 + * @return array
862 + */
863 + private function _prepare_data_for_metabox($meta_boxes, $post_id) {
864 + $post = get_post($post_id);
865 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $post_id, true);
866 + $sm_mode = $this->get('sm.mode');
559 867
560 - <?php if( !empty( $sm_cloud[ 'cacheControl' ] ) ) { ?>
561 - <div class="misc-pub-cache-control hidden">
562 - <?php _e( 'Cache Control:', ud_get_stateless_media()->domain ); ?> <strong><span><?php echo $sm_cloud[ 'cacheControl' ]; ?></span> </strong>
563 - </div>
564 - <?php } ?>
868 + if (empty($post)) {
869 + return $meta_boxes;
870 + }
565 871
566 - <div class="misc-pub-gs-file-link" style="margin-bottom: 15px;">
567 - <label>
568 - <?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>
569 - <input type="text" class="widefat urlfield" readonly="readonly" value="<?php echo esc_attr($sm_cloud[ 'fileLink' ]); ?>" />
570 - </label>
571 - </div>
872 + $sizes = $this->get_image_sizes();
572 873
573 - <?php
874 + $fields = array();
574 875
575 - if ( !empty( $sm_cloud[ 'bucket' ] ) ) {
576 - ?>
577 - <div class="misc-pub-gs-bucket" style="margin-bottom: 15px;">
578 - <label>
579 - <?php _e( 'Storage Bucket:', ud_get_stateless_media()->domain ); ?>
580 - <input type="text" class="widefat urlfield" readonly="readonly" value="gs://<?php echo esc_attr($sm_cloud[ 'bucket' ]); ?>" />
581 - </label>
582 - </div>
583 - <?php
876 + if ($sm_mode !== 'stateless' || empty($sm_cloud)) {
877 + $fields[] = array(
878 + 'name' => __('Regenerate', ud_get_stateless_media()->domain),
879 + 'id' => 'storage_bucket_url',
880 + 'type' => 'custom_html',
881 + 'std' => $this->_prepare_generate_link($post, false, '', true, $sm_cloud),
882 + 'tab' => 'thumbnails',
883 + );
884 + }
885 +
886 + if (is_array($sm_cloud) && !empty($sm_cloud['fileLink'])) {
887 +
888 + $fields[] = array(
889 + 'type' => 'heading',
890 + 'name' => 'Files',
891 + 'tab' => 'thumbnails',
892 + );
893 +
894 + $fields[] = array(
895 + 'name' => __('Original', ud_get_stateless_media()->domain),
896 + 'id' => 'storage_bucket_url',
897 + 'type' => 'custom_html',
898 + 'media_modal' => true,
899 + 'std' => '<label><input type="text" class="widefat urlfield" readonly="readonly" value="' . esc_attr($sm_cloud['fileLink']) . '" />
900 + <a href="' . $sm_cloud['fileLink'] . '" target="_blank" class="sm-view-link"><i class="dashicons dashicons-external"></i></a>&nbsp;&nbsp;&nbsp;' . $this->_prepare_generate_link($post, true, '', false, $sm_cloud) . ' </label>',
901 + 'tab' => 'thumbnails',
902 + );
903 +
904 + if (!empty($sm_cloud['sizes']) && is_array($sm_cloud['sizes'])) {
905 + foreach ($sm_cloud['sizes'] as $size_label => $size) {
906 +
907 + $fields[] = array(
908 + 'name' => __(sprintf("%s x %s", ($size['width'] ?: $sizes[$size_label]['width']), ($size['height'] ?: $sizes[$size_label]['height'])), ud_get_stateless_media()->domain),
909 + 'id' => 'storage_bucket_url' . $size_label,
910 + 'type' => 'custom_html',
911 + 'media_modal' => true,
912 + 'std' => '<label><input type="text" class="widefat urlfield" readonly="readonly" value="' . esc_attr($size['fileLink']) . '" />
913 + <a href="' . $size['fileLink'] . '" target="_blank" class="sm-view-link"><i class="dashicons dashicons-external"></i></a>&nbsp;&nbsp;&nbsp;' . $this->_prepare_generate_link($post, true, $size_label, false, $sm_cloud) . ' </label>',
914 + 'tab' => 'thumbnails',
915 + );
916 + }
584 917 }
585 918
586 - if ( current_user_can( 'upload_files' ) ) {
587 - if ( $post && 'attachment' == $post->post_type && 'image/' == substr( $post->post_mime_type, 0, 6 ) ) {
588 - ?>
589 - <a href="javascript:;" data-type="image" data-id="<?php echo $post->ID; ?>"
590 - class="button-secondary sm_inline_sync"><?php _e('Regenerate and Sync with GCS', ud_get_stateless_media()->domain); ?></a>
591 - <?php
592 - }
919 + if (!empty($sm_cloud['cacheControl'])) {
920 + $fields[] = array(
921 + 'name' => __('Cache Control', ud_get_stateless_media()->domain),
922 + 'id' => 'cache_control',
923 + 'type' => 'custom_html',
924 + 'std' => '<label><input type="text" class="widefat urlfield" readonly="readonly" value="' . $sm_cloud['cacheControl'] . '" /></label>',
925 + 'tab' => 'meta',
926 + );
927 + }
593 928
594 - if ( $post && 'attachment' == $post->post_type && 'image/' != substr( $post->post_mime_type, 0, 6 ) ) {
595 - ?>
596 - <a href="javascript:;" data-type="other" data-id="<?php echo $post->ID; ?>"
597 - class="button-secondary sm_inline_sync"><?php _e('Sync with GCS', ud_get_stateless_media()->domain); ?></a>
598 - <?php
599 - }
929 + if (!empty($sm_cloud['bucket'])) {
930 + $fields[] = array(
931 + 'name' => __('Storage Bucket', ud_get_stateless_media()->domain),
932 + 'id' => 'storage_bukcet',
933 + 'type' => 'custom_html',
934 + 'std' => '<label><input type="text" class="widefat urlfield" readonly="readonly" value="gs://' . esc_attr($sm_cloud['bucket']) . '" />
935 + <a href="https://console.cloud.google.com/storage/browser/' . esc_attr($sm_cloud['bucket']) . '" target="_blank" class="sm-view-link"><i class="dashicons dashicons-external"></i></a></label>',
936 + 'tab' => 'meta',
937 + );
600 938 }
601 939 }
602 940
603 - echo apply_filters( 'sm::attachment::meta', ob_get_clean(), $post->ID );
941 + $meta_boxes[] = apply_filters('sm::attachment::meta', array(
942 + 'id' => 'sm-attachment-metabox',
943 + 'title' => __('Stateless', ud_get_stateless_media()->domain),
944 + 'post_types' => 'attachment',
945 + //'media_modal' => true,
946 + //set context `side` for left column
947 + 'context' => 'normal',
948 + 'priority' => 'low',
949 + 'tabs' => array(
950 + 'thumbnails' => array(
951 + 'label' => __('Thumbnails', ud_get_stateless_media()->domain),
952 + 'icon' => 'dashicons-format-gallery',
953 + ),
954 + 'meta' => array(
955 + 'label' => __('Meta', ud_get_stateless_media()->domain),
956 + 'icon' => 'dashicons-admin-site',
957 + ),
958 + ),
959 + // Tab style: 'default', 'box' or 'left'. Optional
960 + 'tab_style' => 'left',
961 + // Show meta box wrapper around tabs? true (default) or false. Optional
962 + 'tab_wrapper' => true,
963 + 'fields' => $fields
964 + ), $post->ID);
965 +
966 + return $meta_boxes;
604 967 }
605 968
606 969 /**
970 + * Preparing link for sync
971 + * @param $post
972 + * @param bool $use_icon
973 + * @param string $size
974 + * @param bool $button
975 + * @param array $sm_cloud
976 + * @return string
977 + */
978 + private function _prepare_generate_link($post, $use_icon = false, $size = '', $button = false, $sm_cloud = array()) {
979 + $sync = '';
980 +
981 + $sm_mode = $this->get('sm.mode');
982 +
983 + if (current_user_can('upload_files') && $sm_mode !== 'disabled' && ($sm_mode !== 'stateless' || empty($sm_cloud))) {
984 + if ($post && 'attachment' == $post->post_type && 'image/' == substr($post->post_mime_type, 0, 6)) {
985 + $sync = '<a href="javascript:;" data-type="image" data-id="' . $post->ID . '" data-size="' . $size . '" data-reload_page="' . $button . '"
986 + class="sm_inline_sync ' . ($button ? 'button button-primary button-large' : '') . '">' . ($use_icon ? "<i class='dashicons dashicons-image-rotate'></i>" : __('Regenerate and Sync with GCS', ud_get_stateless_media()->domain)) . '</a>';
987 + }
988 + if ($post && 'attachment' == $post->post_type && 'image/' != substr($post->post_mime_type, 0, 6)) {
989 + $sync = '<a href="javascript:;" data-type="other" data-id="' . $post->ID . '" data-size="' . $size . '" data-reload_page="' . $button . '"
990 + class="sm_inline_sync ' . ($button ? 'button button-primary button-large' : '') . '">' . ($use_icon ? "<i class='dashicons dashicons-image-rotate'></i>" : __('Sync with GCS', ud_get_stateless_media()->domain)) . '</a>';
991 + }
992 + } elseif ($button && $sm_mode !== 'stateless') {
993 + $sync = __('You do not have access to sync or Stateless mode is Disabled', ud_get_stateless_media()->domain);
994 + }
995 + return $sync;
996 + }
997 +
998 + /**
607 999 * @param $current_path
1000 + * @param $use_root boolean: whether to use the root dir or not.
1001 + * 0 will be passed from various compatibilities so that the root dir is not used.
1002 + * false will passed from some compatibilities to use the value as local path.
1003 + * @param $attachment_id
1004 + * @param $size
608 1005 * @return string
609 1006 */
610 - public function handle_root_dir( $current_path ) {
611 - $root_dir = $this->get( 'sm.root_dir' );
612 - $root_dir = trim( $root_dir, '/ ' ); // Remove any forward slash and empty space.
1007 + public function handle_root_dir($current_path, $use_root = true, $attachment_id = '', $size = '') {
1008 + //non media files
1009 + if ($use_root === 0) {
1010 + $non_media = ud_stateless_db()->get_non_library_file_name($current_path);
613 1011
614 - if ( !empty( $root_dir ) ) {
615 - return $root_dir . '/' . $current_path;
1012 + if ($non_media) {
1013 + return $non_media;
1014 + }
616 1015 }
617 1016
1017 + $root_dir = $this->get('sm.root_dir');
1018 + $root_dir_regex = '~^' . apply_filters("wp_stateless_handle_root_dir", $root_dir, true) . '/~';
1019 + /**
1020 + * Retrieve Y/M and other tags from current path
1021 + */
1022 + $path_elements = apply_filters('wp_stateless_unhandle_root_dir', $current_path);
1023 + $root_dir = apply_filters("wp_stateless_handle_root_dir", $root_dir, false, $path_elements);
1024 +
1025 + $upload_dir = wp_upload_dir();
1026 + $current_path = str_replace(wp_normalize_path(trailingslashit($upload_dir['basedir'])), '', wp_normalize_path($current_path));
1027 + $current_path = str_replace(wp_normalize_path(trailingslashit($upload_dir['baseurl'])), '', wp_normalize_path($current_path));
1028 + $current_path = str_replace(trailingslashit($this->get_gs_host()), '', $current_path);
1029 +
1030 + /**
1031 + * Using only filename. Other parts of path included to $root_dir.
1032 + * excluding compatibility.
1033 + */
1034 + if ($use_root) {
1035 + $current_path = basename($current_path);
1036 + }
1037 +
1038 + if (!$use_root) {
1039 + // removing the root dir if already exists in the beginning.
1040 + $raw_name = preg_replace($root_dir_regex, '', $current_path);
1041 +
1042 + if ($raw_name && is_multisite() && ($blog_id = get_current_blog_id()) != 1) {
1043 + $folder = "sites/{$blog_id}/";
1044 + if (strpos($raw_name, $folder) === 0) return $raw_name;
1045 +
1046 + return "$folder$raw_name";
1047 + }
1048 +
1049 + return $raw_name;
1050 + }
1051 +
1052 + // skip adding root dir if it's already added.
1053 + if (!empty($root_dir) && !preg_match($root_dir_regex, $current_path)) {
1054 + return $root_dir . '/' . trim($current_path, '/ ');
1055 + }
1056 +
618 1057 return $current_path;
619 1058 }
620 1059
621 1060 /**
@@ -621,20 +1060,25 @@
621 1060 /**
622 1061 * @param $content
623 1062 * @return mixed
624 1063 */
625 - public function the_content_filter( $content ) {
1064 + public function the_content_filter($content) {
626 1065
627 - if ( $upload_data = wp_upload_dir() ) {
1066 + if ($upload_data = wp_upload_dir()) {
628 1067
629 - if ( !empty( $upload_data['baseurl'] ) && !empty( $content ) ) {
630 - $baseurl = preg_replace('/https?:\/\//','',$upload_data['baseurl']);
631 - $root_dir = trim( $this->get( 'sm.root_dir' ), '/ ' ); // Remove any forward slash and empty space.
632 - $root_dir = !empty( $root_dir ) ? $root_dir . '/' : false;
1068 + if (!empty($upload_data['url']) && !empty($content)) {
1069 + $url = preg_replace('/https?:\/\//', '', $upload_data['url']);
1070 +
1071 + $root_dir = trim($this->get('sm.root_dir'), '/ '); // Remove any forward slash and empty space.
1072 + $root_dir = apply_filters("wp_stateless_handle_root_dir", $root_dir);
1073 + $root_dir = !empty($root_dir) ? $root_dir . '/' : false;
633 1074 $image_host = $this->get_gs_host();
634 1075 $file_ext = $this->replaceable_file_types();
635 - $content = preg_replace( '/(href|src)=(\'|")(https?:\/\/'.str_replace('/', '\/', $baseurl).')\/(.+?)('.$file_ext.')(\'|")/i',
636 - '$1=$2'.$image_host.'/'.($root_dir?$root_dir:'').'$4$5$6', $content);
1076 + $content = preg_replace(
1077 + '/(href|src)=(\'|")(https?:\/\/' . str_replace('/', '\/', $url) . ')\/(.+?)(' . $file_ext . ')(\'|")/i',
1078 + '$1=$2' . $image_host . '/' . ($root_dir ? $root_dir : '') . '$4$5$6',
1079 + $content
1080 + );
637 1081 }
638 1082 }
639 1083
640 1084 return $content;
@@ -641,11 +1085,11 @@
641 1085 }
642 1086
643 1087 /**
644 1088 * Return file types supported by File URL Replacement.
645 - *
1089 + * @return string
646 1090 */
647 - public function replaceable_file_types(){
1091 + public function replaceable_file_types() {
648 1092 $types = $this->get('sm.body_rewrite_types');
649 1093
650 1094 // Removing extra space.
651 1095 $types = trim($types);
@@ -656,30 +1100,30 @@
656 1100 }
657 1101
658 1102 /**
659 1103 * Copied from https://developer.wordpress.org/reference/functions/get_metadata/
660 - *
1104 + *
661 1105 * @param $value null unless other filter hooked in this function.
662 1106 * @param $object_id post id
663 - * @param $meta_key
1107 + * @param $meta_key
664 1108 * @param $single
665 1109 * @return mixed
666 1110 */
667 - public function post_metadata_filter($value, $object_id, $meta_key, $single){
668 - if(empty($value)){
1111 + public function post_metadata_filter($value, $object_id, $meta_key, $single) {
1112 + if (empty($value)) {
669 1113 $meta_type = 'post';
670 1114 $transient_key = "stateless_{$meta_type}_meta";
671 -
1115 +
672 1116 $meta_cache = wp_cache_get($object_id, $transient_key);
673 - if(empty($meta_cache)){
1117 + if (empty($meta_cache)) {
674 1118 $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
675 1119
676 - if ( !$meta_cache ) {
677 - $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
1120 + if (!$meta_cache) {
1121 + $meta_cache = update_meta_cache($meta_type, array($object_id));
678 1122 $meta_cache = $meta_cache[$object_id];
679 1123 }
680 -
681 - foreach($meta_cache as $key => $meta){
1124 +
1125 + foreach ($meta_cache as $key => $meta) {
682 1126 $meta_cache[$key] = array_map('maybe_unserialize', $meta_cache[$key]);
683 1127 }
684 1128
685 1129 $meta_cache = $this->convert_to_gs_link($meta_cache);
@@ -684,27 +1128,23 @@
684 1128
685 1129 $meta_cache = $this->convert_to_gs_link($meta_cache);
686 1130 wp_cache_set($object_id, $meta_cache, $transient_key);
687 1131 }
688 -
689 - if ( ! $meta_key ) {
690 - return $meta_cache;
1132 +
1133 + if (!$meta_key) {
1134 + return $meta_cache;
691 1135 }
692 -
693 - if ( isset($meta_cache[$meta_key]) ) {
694 - if ( $single )
695 - return $meta_cache[$meta_key][0];
696 - else
697 - return $meta_cache[$meta_key];
1136 +
1137 + if (isset($meta_cache[$meta_key])) {
1138 + return $meta_cache[$meta_key];
698 1139 }
699 -
700 - // in case no metadata is found return what was passed in $value.
1140 +
1141 + // in case no metadata is found return what was passed in $value.
701 1142 // $value most of the time is null.
702 1143 return $value;
703 1144 }
704 1145
705 1146 return $this->convert_to_gs_link($value);
706 -
707 1147 }
708 1148
709 1149 /**
710 1150 * Replace all image link with gs link and return only if meta modified.
@@ -709,24 +1149,26 @@
709 1149 /**
710 1150 * Replace all image link with gs link and return only if meta modified.
711 1151 *
712 1152 * @param $meta
1153 + * @param $return
713 1154 * @return mixed or null when not changed.
714 1155 */
715 - public function convert_to_gs_link($meta){
1156 + public function convert_to_gs_link($meta, $return = false) {
716 1157 $updated = $meta;
717 - if ( $meta && $upload_data = wp_upload_dir() ) {
718 - if ( !empty( $upload_data['baseurl'] ) && !empty( $meta ) ) {
719 - $baseurl = preg_replace('/https?:\/\//','',$upload_data['baseurl']);
720 - $root_dir = trim( $this->get( 'sm.root_dir' ), '/ ' ); // Remove any forward slash and empty space.
721 - $root_dir = !empty( $root_dir ) ? $root_dir . '/': false;
722 - $image_host = $this->get_gs_host().'/'.($root_dir?$root_dir:'');
1158 + if ($meta && $upload_data = wp_upload_dir()) {
1159 + if (!empty($upload_data['url']) && !empty($meta)) {
1160 + $url = preg_replace('/https?:\/\//', '', $upload_data['url']);
1161 + $root_dir = trim($this->get('sm.root_dir'), '/ '); // Remove any forward slash and empty space.
1162 + $root_dir = apply_filters("wp_stateless_handle_root_dir", $root_dir);
1163 + $root_dir = !empty($root_dir) ? $root_dir . '/' : false;
1164 + $image_host = $this->get_gs_host() . '/' . ($root_dir ? $root_dir : '');
723 1165 $file_ext = $this->replaceable_file_types();
724 - $updated = $this->_convert_to_gs_link($meta, $image_host, $baseurl, $file_ext);
1166 + $updated = $this->_convert_to_gs_link($meta, $image_host, $url, $file_ext);
725 1167 }
726 1168 }
727 1169
728 - if($updated == $meta){
1170 + if ($updated == $meta && !$return) {
729 1171 return null; // Not changed.
730 1172 }
731 1173 return $updated;
732 1174 }
@@ -734,69 +1176,54 @@
734 1176 /**
735 1177 * Replace all image link with gs link
736 1178 *
737 1179 * @param $meta
738 - * @return mixed
1180 + * @param $image_host
1181 + * @param $url
1182 + * @param $file_ext
1183 + * @return array|null|string|string[]
739 1184 */
740 - public function _convert_to_gs_link($meta, $image_host, $baseurl, $file_ext){
741 - if(is_array($meta)){
1185 + public function _convert_to_gs_link($meta, $image_host, $url, $file_ext) {
1186 + if (is_array($meta)) {
742 1187 foreach ($meta as $key => $value) {
743 - $meta[$key] = $this->_convert_to_gs_link($value, $image_host, $baseurl, $file_ext);
1188 + $meta[$key] = $this->_convert_to_gs_link($value, $image_host, $url, $file_ext);
744 1189 }
745 1190 return $meta;
746 - } elseif (is_object($meta) && $meta instanceof \stdClass ) {
1191 + } elseif (is_object($meta) && $meta instanceof \stdClass) {
747 1192 foreach (get_object_vars($meta) as $key => $value) {
748 - $meta->{$key} = $this->_convert_to_gs_link($value, $image_host, $baseurl, $file_ext);
1193 + $meta->{$key} = $this->_convert_to_gs_link($value, $image_host, $url, $file_ext);
749 1194 }
750 1195 return $meta;
751 - } elseif(is_string($meta)){
752 - return preg_replace( '/(https?:\/\/'.str_replace('/', '\/', $baseurl).')\/(.+?)(' . $file_ext . ')/i', $image_host.'$2$3', $meta);
1196 + } elseif (is_string($meta)) {
1197 + return preg_replace('/(https?:\/\/' . str_replace('/', '\/', $url) . ')\/(.+?)(' . $file_ext . ')/i', $image_host . '$2$3', $meta);
753 1198 }
754 1199
755 1200 return $meta;
756 1201 }
757 1202
758 -
759 1203 /**
760 - * @param $links
761 - * @param $file
762 - * @return mixed
763 - */
764 - public function plugin_action_links( $links, $file ) {
765 -
766 - if ($file == plugin_basename( dirname( __DIR__ ) . '/wp-stateless-media.php' ) ) {
767 - $settings_link = '<a href="'. '' .'">'.__( 'Settings' , 'ssd').'</a>';
768 - array_unshift( $links, $settings_link );
769 - }
770 -
771 - if ($file == plugin_basename( dirname( __DIR__ ) . '/wp-stateless.php' ) ) {
772 - $settings_link = '<a href="'. '' .'">'.__( 'Settings' , 'ssd').'</a>';
773 - array_unshift( $links, $settings_link );
774 - }
775 -
776 - return $links;
777 - }
778 -
779 - /**
780 1204 * Determines if plugin is loaded via mu-plugins
781 1205 * or Network Enabled.
782 1206 *
1207 + * @param bool $is_multisite
1208 + * @return bool
783 1209 * @author peshkov@UD
784 1210 */
785 - public function is_network_detected() {
1211 + public function is_network_detected($is_multisite = false) {
786 1212 /* Plugin is loaded via mu-plugins. */
787 1213
788 - if( strpos( Utility::normalize_path( $this->root_path ), Utility::normalize_path( WPMU_PLUGIN_DIR ) ) !== false ) {
1214 + if (strpos(Utility::normalize_path($this->root_path), Utility::normalize_path(WPMU_PLUGIN_DIR)) !== false) {
789 1215 return true;
790 1216 }
791 1217
792 - if( is_multisite() ) {
1218 + if (is_multisite()) {
1219 + if ($is_multisite) return true;
793 1220 /* Looks through network enabled plugins to see if our one is there. */
794 1221 foreach (wp_get_active_network_plugins() as $path) {
795 1222 // Trying again using readlink in case it's a symlink file.
796 1223 // boot_file is already solved.
797 1224 // wp_normalize_path is helpfull in windows.
798 - if (wp_normalize_path($this->boot_file) == wp_normalize_path($path) || (is_link($path) AND $this->boot_file == readlink($path))) {
1225 + if (wp_normalize_path($this->boot_file) == wp_normalize_path($path) || (is_link($path) && $this->boot_file == readlink($path))) {
799 1226 return true;
800 1227 }
801 1228 }
802 1229 }
@@ -808,58 +1235,65 @@
808 1235 * Register scripts and styles
809 1236 */
810 1237 public function admin_init() {
811 1238 $this->show_notice_stateless_cache_busting();
812 - wp_register_style( 'wp-stateless', $this->path( 'static/styles/wp-stateless.css', 'url' ), array(), self::$version );
1239 + wp_register_style('wp-stateless', $this->path('static/styles/wp-stateless.css', 'url'), array(), self::$version);
813 1240
1241 + /**
1242 + * select2 styles
1243 + */
1244 + wp_register_style('wp-stateless-select2', $this->path('static/styles/select2.min.css', 'url'), array(), self::$version);
1245 +
814 1246 /* Attachment or upload page */
815 - wp_register_script( 'wp-stateless-uploads-js', $this->path( 'static/scripts/wp-stateless-uploads.js', 'url' ), array( 'jquery' ), self::$version );
1247 + wp_register_script('wp-stateless-uploads-js', $this->path('static/scripts/wp-stateless-uploads.js', 'url'), array('jquery'), self::$version);
1248 + wp_localize_script('wp-stateless-uploads-js', 'stateless_upload', [
1249 + 'inline_sync_nonce' => wp_create_nonce('sm_inline_sync'),
1250 + ]);
816 1251
817 - /* Setup wizard styles and scripts. */
818 - wp_register_style( 'wp-stateless-bootstrap', $this->path( 'static/styles/bootstrap.min.css', 'url' ), array(), '3.3.7' );
819 - wp_register_style( 'bootstrap-grid-v4', $this->path( 'static/styles/bootstrap-grid.min.css', 'url' ), array(), '3.3.7' );
820 - wp_register_style( 'wp-stateless-setup-wizard', $this->path( 'static/styles/wp-stateless-setup-wizard.css', 'url' ), array(), self::$version );
1252 + /* Setup wizard styles. */
1253 + // #152
1254 + // wp_register_style('wp-stateless-setup-wizard', $this->path('static/styles/wp-stateless-setup-wizard.css', 'url'), array(), self::$version);
821 1255
822 - wp_register_script( 'async.min', ud_get_stateless_media()->path( 'static/scripts/async.js', 'url' ), array(), ud_get_stateless_media()->version );
823 - wp_register_script( 'jquery.history', ud_get_stateless_media()->path( 'static/scripts/jquery.history.js', 'url' ), array( 'jquery' ), ud_get_stateless_media()->version, true );
824 - 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 );
825 - 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 );
826 - 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 );
827 - 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 );
828 - wp_localize_script( 'wp-stateless-setup', 'stateless_l10n', $this->get_l10n_data() );
1256 + wp_register_script('wp-stateless-select2', ud_get_stateless_media()->path('static/scripts/select2.min.js', 'url'), array('jquery'), self::$version, true);
829 1257
830 - 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 );
1258 + /* Stateless settings page */
1259 + wp_register_script('wp-stateless-settings', ud_get_stateless_media()->path('static/scripts/wp-stateless-settings.js', 'url'), array('clipboard'), self::$version);
1260 + wp_localize_script('wp-stateless-settings', 'stateless_l10n', $this->get_l10n_data());
831 1261
1262 + wp_register_style('wp-stateless-settings', $this->path('static/styles/wp-stateless-settings.css', 'url'), array(), self::$version);
1263 + wp_register_style('wp-stateless-addons', $this->path('static/styles/wp-stateless-addons.css', 'url'), array(), self::$version);
1264 + wp_register_style('wp-stateless-status', $this->path('static/styles/wp-stateless-status.css', 'url'), array(), self::$version);
832 1265
833 - /* Stateless settings page */
834 - wp_register_script( 'wp-stateless-settings', ud_get_stateless_media()->path( 'static/scripts/wp-stateless-settings.js', 'url' ), array(), ud_get_stateless_media()->version );
835 - wp_localize_script( 'wp-stateless-settings', 'stateless_l10n', $this->get_l10n_data() );
836 -
837 - wp_register_style( 'wp-stateless-settings', $this->path( 'static/styles/wp-stateless-settings.css', 'url' ), array(), self::$version );
1266 + // Sync tab
1267 + wp_register_script('wp-stateless', ud_get_stateless_media()->path('static/scripts/wp-stateless.js', 'url'), array('jquery-ui-core', 'wp-stateless-settings', 'wp-api-request'), self::$version, true);
838 1268
839 - // Sync tab
840 - if ( wp_script_is( 'jquery-ui-widget', 'registered' ) ){
841 - 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' );
842 - }
843 - else{
844 - 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' );
845 - }
846 - wp_register_script( 'wp-stateless-angular', ud_get_stateless_media()->path( 'static/scripts/angular.min.js', 'url' ), array(), '1.5.0', true );
847 - 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 );
848 -
849 - wp_localize_script( 'wp-stateless', 'stateless_l10n', $this->get_l10n_data() );
1269 + wp_localize_script('wp-stateless', 'stateless_l10n', $this->get_l10n_data());
850 1270 wp_localize_script('wp-stateless', 'wp_stateless_configs', array(
851 1271 'WP_DEBUG' => defined('WP_DEBUG') ? WP_DEBUG : false,
1272 + 'REST_API_TOKEN' => Utility::generate_jwt_token(['user_id' => get_current_user_id()], DAY_IN_SECONDS),
1273 + 'api_root' => apply_filters( 'wp_stateless_rest_api_root', '' ),
1274 + 'ajaxurl' => admin_url( 'admin-ajax.php' ),
1275 + 'stateless_check_ajax_nonce' => wp_create_nonce('stateless_check_ajax'),
1276 + 'text_ok' => __('Ok', ud_get_stateless_media()->domain),
1277 + 'text_fail' => __('Fail', ud_get_stateless_media()->domain),
852 1278 ));
853 -
1279 +
854 1280 $settings = ud_get_stateless_media()->get('sm');
855 - if(defined('WP_STATELESS_MEDIA_JSON_KEY') && WP_STATELESS_MEDIA_JSON_KEY){
1281 + $settings['wildcards'] = $this->settings->wildcards;
1282 + $settings['network_admin'] = is_network_admin();
1283 + $settings['is_multisite'] = is_multisite();
1284 + if (defined('WP_STATELESS_MEDIA_JSON_KEY') && WP_STATELESS_MEDIA_JSON_KEY) {
856 1285 $settings['key_json'] = "Currently configured via a constant.";
857 1286 }
858 1287 wp_localize_script('wp-stateless', 'wp_stateless_settings', $settings);
859 - wp_localize_script('wp-stateless', 'wp_stateless_compatibility', Module::get_modules());
860 - 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' );
861 1288
1289 + // Batch processes tab
1290 + wp_register_script('wp-stateless-batch', ud_get_stateless_media()->path('static/scripts/wp-stateless-batch.js', 'url'), array('wp-api-request'), self::$version, true);
1291 + wp_localize_script('wp-stateless-batch', 'wp_stateless_batch', array(
1292 + 'REST_API_TOKEN' => Utility::generate_jwt_token(['user_id' => get_current_user_id()], DAY_IN_SECONDS),
1293 + 'is_running' => BatchTaskManager::instance()->is_running(),
1294 + 'api_root' => apply_filters( 'wp_stateless_rest_api_root', '' ),
1295 + ));
862 1296 }
863 1297
864 1298 /**
865 1299 * Get_l10n_data
@@ -866,10 +1300,10 @@
866 1300 *
867 1301 * @param string $value
868 1302 * @return mixed
869 1303 */
870 - public function get_l10n_data($value=''){
871 - include ud_get_stateless_media()->path( 'l10n.php', 'dir');
1304 + public function get_l10n_data($value = '') {
1305 + include ud_get_stateless_media()->path('l10n.php', 'dir');
872 1306 return $l10n;
873 1307 }
874 1308
875 1309 /**
@@ -876,24 +1310,20 @@
876 1310 * Admin Scripts
877 1311 *
878 1312 * @param $hook
879 1313 */
880 - public function admin_enqueue_scripts( $hook ) {
1314 + public function admin_enqueue_scripts($hook) {
881 1315
882 - switch( $hook ) {
1316 + switch ($hook) {
883 1317
884 1318 case 'options-media.php':
885 - //wp_enqueue_script( 'wp-api' );
1319 + wp_enqueue_style('wp-stateless');
1320 + break;
886 1321
887 - wp_enqueue_style( 'wp-stateless');
888 - wp_enqueue_script( 'wp-stateless-setup' );
889 - break;
890 -
891 1322 case 'upload.php':
1323 + wp_enqueue_style('wp-stateless');
1324 + wp_enqueue_script('wp-stateless-uploads-js');
892 1325
893 - wp_enqueue_style( 'wp-stateless');
894 - wp_enqueue_script( 'wp-stateless-uploads-js' );
895 -
896 1326 break;
897 1327
898 1328 case 'post.php':
899 1329
@@ -898,54 +1328,60 @@
898 1328 case 'post.php':
899 1329
900 1330 global $post;
901 1331
902 - if ( $post->post_type == 'attachment' ) {
903 - wp_enqueue_style( 'wp-stateless');
904 - wp_enqueue_script( 'wp-stateless-uploads-js' );
1332 + if ($post->post_type == 'attachment') {
1333 + wp_enqueue_style('wp-stateless');
1334 + wp_enqueue_script('wp-stateless-uploads-js');
905 1335 }
906 1336
907 1337 break;
908 1338
909 - case 'media_page_stateless-setup':
910 - wp_enqueue_style( 'wp-stateless');
911 - wp_enqueue_style( 'wp-stateless-bootstrap' );
912 - wp_enqueue_style( 'wp-stateless-setup-wizard' );
913 -
914 - wp_enqueue_script( 'async.min' );
915 - wp_enqueue_script( 'jquery.history' );
916 - wp_enqueue_script( 'wp-stateless-validation' );
917 - wp_enqueue_script( 'wp-stateless-loading' );
918 - wp_enqueue_script( 'wp-stateless-comboBox' );
919 - wp_enqueue_script( 'wp-stateless-setup' );
920 - wp_enqueue_script( 'wp-stateless-setup-wizard-js' );
1339 + // #152
1340 + // case 'media_page_stateless-setup':
1341 + case 'settings_page_stateless-setup':
1342 + wp_enqueue_style('wp-stateless');
1343 + // #152
1344 + // wp_enqueue_style('wp-stateless-setup-wizard');
921 1345 break;
922 1346 case 'media_page_stateless-settings':
923 - wp_enqueue_style( 'wp-stateless');
924 - wp_enqueue_script( 'wp-stateless-settings' );
925 - wp_enqueue_style( 'bootstrap-grid-v4' );
926 - wp_enqueue_style( 'wp-stateless-settings' );
927 -
1347 + case 'settings_page_stateless-settings':
1348 + wp_enqueue_style('wp-stateless');
1349 + wp_enqueue_style('wp-stateless-select2');
1350 + wp_enqueue_script('wp-stateless-settings');
1351 + wp_enqueue_script('wp-stateless-select2');
1352 + wp_enqueue_style('wp-stateless-settings');
1353 + wp_enqueue_style('wp-stateless-addons');
1354 + wp_enqueue_style('wp-stateless-status');
1355 + wp_enqueue_style( 'media-views' );
1356 +
928 1357 // Sync tab
929 - wp_enqueue_script( 'jquery-ui-progressbar' );
930 - wp_enqueue_script( 'wp-stateless-angular' );
931 - wp_enqueue_script( 'wp-stateless' );
932 - wp_enqueue_style( 'jquery-ui-regenthumbs' );
1358 + wp_enqueue_script('wp-stateless');
933 1359
1360 + // Data updates
1361 + wp_enqueue_script('jquery-ui-dialog');
1362 + wp_enqueue_style('wp-jquery-ui-dialog');
1363 + wp_enqueue_script('wp-stateless-batch');
1364 +
1365 + wp_enqueue_style('wp-pointer');
1366 + wp_enqueue_script('wp-pointer');
1367 +
934 1368 $data = array(
935 - 'key' => 'stateless-cache-busting',
936 - 'class' => 'notice',
937 - 'title' => sprintf( __( "Stateless mode enables and requires the Cache-Busting option.", ud_get_stateless_media()->domain ) ),
938 - 'message' => sprintf( __("WordPress looks at local files to prevent files with the same filenames.
939 - 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.
940 - 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" ),
941 - );
1369 + 'key' => 'stateless-cache-busting',
1370 + 'class' => 'notice',
1371 + 'title' => sprintf(__("Stateless and Ephemeral modes enables and requires the Cache-Busting option.", ud_get_stateless_media()->domain)),
1372 + 'message' => sprintf(__("WordPress looks at local files to prevent files with the same filenames.
1373 + 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.
1374 + Override with the <a href='%s' target='_blank'>%s</a> constant.", ud_get_stateless_media()->domain), ud_get_stateless_media()->get_docs_page_url('docs/constants/#wpstatelessmediacachebusting'), "WP_STATELESS_MEDIA_CACHE_BUSTING"),
1375 + );
942 1376 echo "<script id='template-stateless-cache-busting' type='text/html'>";
943 - include ud_get_stateless_media()->path( '/static/views/error-notice.php', 'dir' );
1377 + include ud_get_stateless_media()->path('/static/views/error-notice.php', 'dir');
944 1378 echo "</script>";
945 - default: break;
1379 +
1380 + break;
1381 + default:
1382 + break;
946 1383 }
947 -
948 1384 }
949 1385
950 1386 /**
951 1387 * Add Attributes to media HTML
@@ -955,20 +1391,19 @@
955 1391 * @param $attachment
956 1392 * @param $size
957 1393 * @return mixed
958 1394 */
959 - public function wp_get_attachment_image_attributes( $attr, $attachment, $size = null ) {
960 -
961 - $sm_cloud = get_post_meta( $attachment->ID, 'sm_cloud', true );
962 - if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'name' ] ) ) {
963 - $attr[ 'class' ] = $attr[ 'class' ] . ' wp-stateless-item';
964 - $attr[ 'data-image-size' ] = is_array( $size ) ? implode( 'x', $size ) : $size;
965 - $attr[ 'data-stateless-media-bucket' ] = isset( $sm_cloud[ 'bucket' ] ) ? $sm_cloud[ 'bucket' ] : false;
966 - $attr[ 'data-stateless-media-name' ] = $sm_cloud[ 'name' ];
1395 + public function wp_get_attachment_image_attributes($attr, $attachment, $size = null) {
1396 +
1397 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $attachment->ID);
1398 + if (is_array($sm_cloud) && !empty($sm_cloud['name'])) {
1399 + $attr['class'] = $attr['class'] . ' wp-stateless-item';
1400 + $attr['data-image-size'] = is_array($size) ? implode('x', $size) : $size;
1401 + $attr['data-stateless-media-bucket'] = isset($sm_cloud['bucket']) ? $sm_cloud['bucket'] : false;
1402 + $attr['data-stateless-media-name'] = $sm_cloud['name'];
967 1403 }
968 1404
969 1405 return $attr;
970 -
971 1406 }
972 1407
973 1408 /**
974 1409 * Adds filter link to Media Library table.
@@ -975,10 +1410,10 @@
975 1410 *
976 1411 * @param $views
977 1412 * @return mixed
978 1413 */
979 - public function views_upload( $views ) {
980 - $views['stateless'] = '<a href="#">' . __( 'Stateless Media' ) . '</a>';
1414 + public function views_upload($views) {
1415 + $views['stateless'] = '<a href="#">' . __('Stateless Media') . '</a>';
981 1416 return $views;
982 1417 }
983 1418
984 1419 /**
@@ -988,11 +1423,11 @@
988 1423 * @param integer $id
989 1424 * @param string $size
990 1425 * @return mixed $false
991 1426 */
992 - public function image_downsize( $false = false, $id, $size ) {
1427 + public function image_downsize($false, $id, $size) {
993 1428
994 - if ( !isset( $this->client ) || !$this->client || is_wp_error( $this->client ) ) {
1429 + if ((!isset($this->client) || !$this->client || is_wp_error($this->client)) && $this->get('sm.mode') !== 'stateless') {
995 1430 return $false;
996 1431 }
997 1432
998 1433 /**
@@ -997,9 +1432,9 @@
997 1432
998 1433 /**
999 1434 * Check if enabled
1000 1435 */
1001 - if ( $this->get( 'sm.mode' ) !== 'cdn' && $this->get( 'sm.mode' ) !== 'stateless' ) {
1436 + if (!in_array($this->get('sm.mode'), array('cdn', 'stateless', 'ephemeral'))) {
1002 1437 return $false;
1003 1438 }
1004 1439
1005 1440 /** Start determine remote file */
@@ -1008,19 +1443,17 @@
1008 1443 $width = $height = 0;
1009 1444 $is_intermediate = false;
1010 1445
1011 1446 //** try for a new style intermediate size */
1012 - if ( $intermediate = image_get_intermediate_size( $id, $size ) ) {
1013 - if(!empty( $intermediate['gs_link'] )){
1447 + if ($intermediate = image_get_intermediate_size($id, $size)) {
1448 + if (!empty($intermediate['gs_link'])) {
1014 1449 $img_url = $intermediate['gs_link'];
1015 - }
1016 - else if(!empty( $intermediate['url'] )){
1450 + } else if (!empty($intermediate['url'])) {
1017 1451 $img_url = $intermediate['url'];
1018 - }
1019 - else{
1452 + } else {
1020 1453 $img_url = dirname($img_url) . $intermediate['file'];
1021 1454 }
1022 -
1455 +
1023 1456 $width = $intermediate['width'];
1024 1457 $height = $intermediate['height'];
1025 1458 $is_intermediate = true;
1026 1459 }
@@ -1029,18 +1462,18 @@
1029 1462 * maybe try to get images info from sm_cloud
1030 1463 * this case may happen when no local files
1031 1464 * @author korotkov@ud
1032 1465 */
1033 - if ( !$width && !$height ) {
1034 - $sm_cloud = get_post_meta( $id, 'sm_cloud', true );
1035 - if ( is_string($size) && !empty( $sm_cloud['sizes'] ) && !empty( $sm_cloud['sizes'][$size] ) ) {
1466 + if (!$width && !$height) {
1467 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $id, true);
1468 + if (is_string($size) && !empty($sm_cloud['sizes']) && !empty($sm_cloud['sizes'][$size])) {
1036 1469 global $_wp_additional_image_sizes;
1037 1470
1038 - $img_url = !empty( $sm_cloud['sizes'][$size]['fileLink'] ) ? $sm_cloud['sizes'][$size]['fileLink'] : $img_url;
1471 + $img_url = !empty($sm_cloud['sizes'][$size]['fileLink']) ? $sm_cloud['sizes'][$size]['fileLink'] : $img_url;
1039 1472
1040 - if ( !empty( $_wp_additional_image_sizes[ $size ] ) ) {
1041 - $width = !empty( $_wp_additional_image_sizes[ $size ]['width'] ) ? $_wp_additional_image_sizes[ $size ]['width'] : $width;
1042 - $height = !empty( $_wp_additional_image_sizes[ $size ]['height'] ) ? $_wp_additional_image_sizes[ $size ]['height'] : $height;
1473 + if (!empty($_wp_additional_image_sizes[$size])) {
1474 + $width = !empty($_wp_additional_image_sizes[$size]['width']) ? $_wp_additional_image_sizes[$size]['width'] : $width;
1475 + $height = !empty($_wp_additional_image_sizes[$size]['height']) ? $_wp_additional_image_sizes[$size]['height'] : $height;
1043 1476 }
1044 1477
1045 1478 $is_intermediate = true;
1046 1479 }
@@ -1045,9 +1478,9 @@
1045 1478 $is_intermediate = true;
1046 1479 }
1047 1480 }
1048 1481
1049 - if ( !$width && !$height && isset( $meta['width'], $meta['height'] ) ) {
1482 + if (!$width && !$height && isset($meta['width'], $meta['height'])) {
1050 1483
1051 1484 //** any other type: use the real image */
1052 1485 $width = $meta['width'];
1053 1486 $height = $meta['height'];
@@ -1053,14 +1486,14 @@
1053 1486 $height = $meta['height'];
1054 1487 }
1055 1488
1056 1489
1057 - if ( $img_url) {
1490 + if ($img_url) {
1058 1491
1059 1492 //** we have the actual image size, but might need to further constrain it if content_width is narrower */
1060 - list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
1493 + list($width, $height) = image_constrain_size_for_editor($width, $height, $size);
1061 1494 $img_url = apply_filters('wp_stateless_bucket_link', $img_url);
1062 - return array( $img_url, $width, $height, $is_intermediate );
1495 + return array($img_url, $width, $height, $is_intermediate);
1063 1496 }
1064 1497
1065 1498
1066 1499 /**
@@ -1066,9 +1499,8 @@
1066 1499 /**
1067 1500 * All other cases work as usually
1068 1501 */
1069 1502 return $false;
1070 -
1071 1503 }
1072 1504
1073 1505 /**
1074 1506 * Extends metadata by adding GS information.
@@ -1077,34 +1509,100 @@
1077 1509 * @param $metadata
1078 1510 * @param $attachment_id
1079 1511 * @return array|mixed
1080 1512 */
1081 - public function wp_get_attachment_metadata( $metadata, $attachment_id ) {
1513 + public function wp_get_attachment_metadata($metadata, $attachment_id) {
1514 + global $default_dir;
1515 + $default_dir = false;
1082 1516 /* Determine if the media file has GS data at all. */
1083 - $sm_cloud = get_post_meta( $attachment_id, 'sm_cloud', true );
1517 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $attachment_id, true);
1084 1518 // If metadata not passed the get metadata from post meta.
1085 - if(empty($metadata)){
1086 - $metadata = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
1519 + if (empty($metadata)) {
1520 + $metadata = get_post_meta($attachment_id, '_wp_attachment_metadata', true);
1087 1521 }
1088 1522
1089 - if( is_array( $metadata ) && is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) {
1090 - $metadata[ 'gs_link' ] = apply_filters('wp_stateless_bucket_link', $sm_cloud[ 'fileLink' ]);
1091 - $metadata[ 'gs_name' ] = isset( $sm_cloud[ 'name' ] ) ? $sm_cloud[ 'name' ] : false;
1092 - $metadata[ 'gs_bucket' ] = isset( $sm_cloud[ 'bucket' ] ) ? $sm_cloud[ 'bucket' ] : false;
1093 - if( !empty( $metadata[ 'sizes' ] ) && is_array( $metadata[ 'sizes' ] ) ) {
1094 - foreach( $metadata[ 'sizes' ] as $k => $v ) {
1095 - if( !empty( $sm_cloud[ 'sizes' ][ $k ][ 'name' ] ) ) {
1096 - $metadata['sizes'][$k]['gs_name'] = $sm_cloud[ 'sizes' ][ $k ][ 'name' ];
1097 - $metadata['sizes'][$k]['gs_link'] = apply_filters('wp_stateless_bucket_link', $sm_cloud[ 'sizes' ][ $k ][ 'fileLink' ]);
1523 + if (empty($metadata)) {
1524 + $metadata = [];
1525 + }
1526 +
1527 + if (is_array($metadata) && is_array($sm_cloud) && !empty($sm_cloud['fileLink'])) {
1528 + $metadata['gs_link'] = apply_filters('wp_stateless_bucket_link', $sm_cloud['fileLink']);
1529 + $metadata['gs_name'] = isset($sm_cloud['name']) ? $sm_cloud['name'] : false;
1530 + $metadata['gs_bucket'] = isset($sm_cloud['bucket']) ? $sm_cloud['bucket'] : false;
1531 + if (!empty($metadata['sizes']) && is_array($metadata['sizes'])) {
1532 + foreach ($metadata['sizes'] as $k => $v) {
1533 + if (!empty($sm_cloud['sizes'][$k]['name'])) {
1534 + $metadata['sizes'][$k]['gs_name'] = $sm_cloud['sizes'][$k]['name'];
1535 + $metadata['sizes'][$k]['gs_link'] = apply_filters('wp_stateless_bucket_link', $sm_cloud['sizes'][$k]['fileLink']);
1098 1536 }
1099 1537 }
1100 1538 }
1101 1539 }
1540 + if (is_multisite() && !empty($metadata['file'])) {
1541 + if ($this->get('sm.mode') == 'stateless') {
1542 + $default_dir = true;
1543 + $uploads = wp_get_upload_dir();
1544 + $default_dir = false;
1102 1545
1546 + $file_path_fix = $uploads['basedir'] . "/{$metadata['file']}";
1547 + if (file_exists($file_path_fix)) {
1548 + $metadata['file'] = "{$metadata['file']}";
1549 + }
1550 + } else {
1551 + $uploads = wp_get_upload_dir();
1552 + $blog_id = get_current_blog_id();
1553 + $file_path_fix = $uploads['basedir'] . "/sites/$blog_id/{$metadata['file']}";
1554 + if (file_exists($file_path_fix)) {
1555 + $metadata['file'] = "sites/$blog_id/{$metadata['file']}";
1556 + }
1557 + }
1558 + } elseif ( empty($sm_cloud) && $this->get('sm.mode') == 'stateless' && isset($metadata['file']) ) {
1559 + $default_dir = true;
1560 + $uploads = wp_get_upload_dir();
1561 + $default_dir = false;
1562 +
1563 + $file_path_fix = $uploads['basedir'] . "/{$metadata['file']}";
1564 + if (file_exists($file_path_fix)) {
1565 + $metadata['file'] = "{$metadata['file']}";
1566 + }
1567 + }
1568 +
1103 1569 return $metadata;
1104 1570 }
1105 1571
1106 1572 /**
1573 + *
1574 + * @param $file
1575 + * @param $attachment_id
1576 + * @return string
1577 + */
1578 + public function get_attached_file($file, $attachment_id) {
1579 + global $default_dir;
1580 +
1581 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $attachment_id);
1582 + $_file = get_post_meta($attachment_id, '_wp_attached_file', true);
1583 +
1584 + /* Determine if the media file has GS data at all. */
1585 + if (is_multisite() && $_file) {
1586 + $blog_id = get_current_blog_id();
1587 + $uploads = wp_get_upload_dir();
1588 + $_file = apply_filters('wp_stateless_file_name', $_file, false);
1589 + $file_path_fix = $uploads['basedir'] . "/sites/$blog_id/$_file";
1590 +
1591 + if (file_exists($file_path_fix)) {
1592 + return $file_path_fix;
1593 + }
1594 + } elseif (empty($sm_cloud) && $this->get('sm.mode') == 'stateless' && $_file) {
1595 + $default_dir = true;
1596 + $uploads = wp_get_upload_dir();
1597 + $default_dir = false;
1598 + return $uploads['basedir'] . '/' . $_file;
1599 + }
1600 +
1601 + return $file;
1602 + }
1603 +
1604 + /**
1107 1605 * Returns client object
1108 1606 * or WP_Error on failure.
1109 1607 *
1110 1608 * @author peshkov@UD
@@ -1111,24 +1609,23 @@
1111 1609 * @return object $this->client. \wpCloud\StatelessMedia\GS_Client or \WP_Error
1112 1610 */
1113 1611 public function get_client() {
1114 1612
1115 - if( null === $this->client ) {
1613 + if (null === $this->client) {
1116 1614
1117 - $key_json = get_site_option( 'sm_key_json' );
1118 - if ( empty($key_json) ) {
1119 - $key_json = $this->get( 'sm.key_json' );
1615 + $key_json = $this->get('sm.key_json');
1616 + if (empty($key_json)) {
1617 + $key_json = get_site_option('sm_key_json');
1120 1618 }
1121 1619
1122 1620 /* Try to initialize GS Client */
1123 - $this->client = GS_Client::get_instance( array(
1124 - 'bucket' => $this->get( 'sm.bucket' ),
1621 + $this->client = GS_Client::get_instance(array(
1622 + 'bucket' => $this->get('sm.bucket'),
1125 1623 'key_json' => $key_json
1126 - ) );
1624 + ));
1127 1625 }
1128 1626
1129 1627 return $this->client;
1130 -
1131 1628 }
1132 1629
1133 1630 /**
1134 1631 * Determines if we can connect to Google Storage Bucket.
@@ -1135,47 +1632,46 @@
1135 1632 *
1136 1633 * @author peshkov@UD
1137 1634 */
1138 1635 public function is_connected_to_gs() {
1636 + $trnst = get_transient('sm::is_connected_to_gs');
1139 1637
1140 - $trnst = get_transient( 'sm::is_connected_to_gs' );
1141 -
1142 - if ( empty($trnst) || false === $trnst || !isset( $trnst[ 'hash' ] ) || $trnst[ 'hash' ] != md5( serialize( $this->get( 'sm' ) ) ) ) {
1638 + if (empty($trnst) || false === $trnst || !isset($trnst['hash']) || $trnst['hash'] != md5(serialize($this->get('sm')))) {
1143 1639 $trnst = array(
1144 1640 'success' => 'true',
1145 1641 'error' => '',
1146 - 'hash' => md5( serialize( $this->get( 'sm' ) ) ),
1642 + 'hash' => md5(serialize($this->get('sm'))),
1147 1643 );
1148 1644 $client = $this->get_client();
1149 1645
1150 - if ( is_wp_error( $client ) ) {
1151 - $trnst[ 'success' ] = 'false';
1152 - $trnst[ 'error' ] = $client->get_error_message();
1646 + if (is_wp_error($client)) {
1647 + $trnst['success'] = 'false';
1648 + $trnst['error'] = $client->get_error_message();
1153 1649 } else {
1154 1650 $connected = $client->is_connected();
1155 - if( $connected !== true ) {
1156 - $trnst[ 'success' ] = 'false';
1157 - $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' ) );
1651 + if ($connected !== true) {
1652 + $trnst['success'] = 'false';
1653 + $trnst['error'] = sprintf('Could not connect to Google Storage bucket. Please be sure that bucket with name <b>%s</b> exists and the access credentials are correct.', esc_html($this->get('sm.bucket')));
1158 1654
1159 - if( is_callable(array($connected, 'getHandlerContext')) && $handlerContext = $connected->getHandlerContext() ){
1160 - if(!empty($handlerContext['error'])){
1655 + if (is_callable(array($connected, 'getHandlerContext')) && $handlerContext = $connected->getHandlerContext()) {
1656 + if (!empty($handlerContext['error'])) {
1161 1657 $handlerContext['error'];
1162 - $trnst[ 'error' ] = "Could not connect to Google Storage bucket. " . make_clickable($handlerContext['error']);
1658 + $trnst['error'] = "Could not connect to Google Storage bucket. " . make_clickable($handlerContext['error']);
1163 1659 }
1164 1660 }
1165 1661
1166 - if( is_callable(array($connected, 'getErrors')) && $error = $connected->getErrors() ){
1662 + if (is_callable(array($connected, 'getErrors')) && $error = $connected->getErrors()) {
1167 1663 $error = reset($error);
1168 - if($error['reason'] == 'accessNotConfigured')
1169 - $trnst[ 'error' ] = "Could not connect to Google Storage bucket. " . make_clickable($error['message']);
1664 + if ($error['reason'] == 'accessNotConfigured')
1665 + $trnst['error'] = "Could not connect to Google Storage bucket. " . make_clickable($error['message']);
1170 1666 }
1171 1667 }
1172 1668 }
1173 - set_transient( 'sm::is_connected_to_gs', $trnst, 4 * HOUR_IN_SECONDS );
1669 + set_transient('sm::is_connected_to_gs', $trnst, 4 * HOUR_IN_SECONDS);
1174 1670 }
1175 1671
1176 - if( isset( $trnst[ 'success' ] ) && $trnst[ 'success' ] == 'false' ) {
1177 - return new \WP_Error( 'error', ( !empty( $trnst[ 'error' ] ) ? $trnst[ 'error' ] : __( 'There is an Error on connection to Google Storage.', $this->domain ) ) );
1672 + if (isset($trnst['success']) && $trnst['success'] == 'false') {
1673 + return new \WP_Error('error', (!empty($trnst['error']) ? $trnst['error'] : 'There is an Error on connection to Google Storage.'));
1178 1674 }
1179 1675
1180 1676 return true;
1181 1677 }
@@ -1184,9 +1680,9 @@
1184 1680 * Flush all plugin transients
1185 1681 *
1186 1682 */
1187 1683 public function flush_transients() {
1188 - delete_transient( 'sm::is_connected_to_gs' );
1684 + delete_transient('sm::is_connected_to_gs');
1189 1685 }
1190 1686
1191 1687 /**
1192 1688 * Plugin Activation
@@ -1192,9 +1688,9 @@
1192 1688 * Plugin Activation
1193 1689 *
1194 1690 */
1195 1691 public function activate() {
1196 - add_action( 'activated_plugin', array($this, 'redirect_to_splash'), 99 );
1692 + add_action('activated_plugin', array($this, 'redirect_to_splash'), 99);
1197 1693 $this->run_upgrade_process();
1198 1694 }
1199 1695
1200 1696 /**
@@ -1203,10 +1699,9 @@
1203 1699 * Works on even manual plugin update.
1204 1700 *
1205 1701 * @author alim@UD
1206 1702 */
1207 - public function run_install_process()
1208 - {
1703 + public function run_install_process() {
1209 1704 // calling the upgrade function because it's same as this point for fresh install or updates.
1210 1705 $this->run_upgrade_process();
1211 1706 }
1212 1707
@@ -1216,45 +1711,27 @@
1216 1711 * Works on even manual plugin update.
1217 1712 *
1218 1713 * @author alim@UD
1219 1714 */
1220 - public function run_upgrade_process()
1221 - {
1715 + public function run_upgrade_process() {
1222 1716 // Creating database on new installation.
1223 - $this->create_db();
1717 + ud_stateless_db()->create_db();
1718 +
1719 + Migrator::instance()->migrate();
1720 +
1224 1721 /**
1225 1722 * Maybe Upgrade current Version
1226 1723 */
1227 - Upgrader::call( $this->args[ 'version' ] );
1724 + Upgrader::call($this->args['version']);
1228 1725 }
1229 1726
1230 1727 /**
1231 - * Create database on plugin activation.
1232 - * @param boolean $force - whether to create db even if option exists. For debug purpose only.
1728 + * Delete table when blog is deleted.
1729 + *
1730 + * @param $old_site
1233 1731 */
1234 - public function create_db($force = false) {
1235 - global $wpdb;
1236 - $sm_sync_db_version = get_option( 'sm_sync_db_version' );
1237 -
1238 - if( $sm_sync_db_version && $force == false ) {
1239 - return;
1240 - }
1241 -
1242 - $table_name = $wpdb->prefix . 'sm_sync';
1243 - $charset_collate = $wpdb->get_charset_collate();
1244 -
1245 - // `expire` timestamp NULL DEFAULT NULL,
1246 - $sql = "CREATE TABLE $table_name (
1247 - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT ,
1248 - `file` varchar(255) NOT NULL ,
1249 - `status` varchar(10) NOT NULL ,
1250 - PRIMARY KEY (`id`)
1251 - ) $charset_collate;";
1252 -
1253 - require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1254 - dbDelta( $sql );
1255 -
1256 - add_option( 'sm_sync_db_version', $this->args[ 'version' ] );
1732 + public function wp_delete_site($old_site) {
1733 + ud_stateless_db()->clear_db($old_site->id);
1257 1734 }
1258 1735
1259 1736 /**
1260 1737 * Redirect_to_splash
@@ -1260,34 +1737,38 @@
1260 1737 * Redirect_to_splash
1261 1738 *
1262 1739 * @param string $plugin
1263 1740 */
1264 - public function redirect_to_splash($plugin =''){
1265 - $this->settings = new Settings();
1741 + public function redirect_to_splash($plugin = '') {
1742 + // $this->settings = new Settings();
1266 1743
1267 - if(defined( 'WP_CLI' ) || $this->settings->get('sm.key_json') || isset($_POST['checked']) && count($_POST['checked']) > 1){
1744 + if (defined('WP_CLI') || $this->settings->get('sm.key_json') || isset($_POST['checked']) && count($_POST['checked']) > 1) {
1268 1745 return;
1269 1746 }
1270 -
1271 - if(
1272 - !$this->settings->get('sm.key_json') &&
1273 - defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true &&
1274 - defined('WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL') && WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL == true
1747 +
1748 + if (
1749 + !$this->settings->get('sm.key_json') &&
1750 + defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true &&
1751 + defined('WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL') && WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL == true
1275 1752 ) {
1276 1753 return;
1277 1754 }
1278 -
1279 - if( !$this->settings->get('sm.key_json') && defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true ) {
1755 +
1756 + if (!$this->settings->get('sm.key_json') && defined('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT') && WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT == true) {
1280 1757 $url = $this->get_settings_page_url('?page=stateless-settings');
1281 - exit( wp_redirect($url));
1758 + exit(wp_redirect($url));
1282 1759 }
1283 -
1284 - if( $plugin == plugin_basename( $this->boot_file ) ) {
1285 - $url = $this->get_settings_page_url('?page=stateless-setup&step=splash-screen');
1286 - if(json_decode($this->settings->get('sm.key_json'))){
1760 +
1761 + if ($plugin == plugin_basename($this->boot_file)) {
1762 + // #152
1763 + // $url = $this->get_settings_page_url('?page=stateless-setup&step=splash-screen');
1764 + $url = $this->get_settings_page_url('?page=stateless-settings');
1765 +
1766 + if (json_decode($this->settings->get('sm.key_json'))) {
1287 1767 $url = $this->get_settings_page_url('?page=stateless-settings');
1288 1768 }
1289 - exit( wp_redirect($url));
1769 +
1770 + exit( wp_redirect($url) );
1290 1771 }
1291 1772 }
1292 1773
1293 1774 /**
@@ -1293,64 +1774,102 @@
1293 1774 /**
1294 1775 * Plugin Deactivation
1295 1776 *
1296 1777 */
1297 - public function deactivate() {}
1778 + public function deactivate() {
1779 + }
1298 1780
1299 1781 /**
1300 1782 * Show_notice_stateless_cache_busting
1301 1783 *
1302 1784 */
1303 - public function show_notice_stateless_cache_busting(){
1304 - $this->errors->add( array(
1785 + public function show_notice_stateless_cache_busting() {
1786 + $this->errors->add(array(
1305 1787 'key' => 'stateless_cache_busting',
1306 1788 'button' => 'View Settings',
1307 1789 'button_link' => admin_url('upload.php?page=stateless-settings'),
1308 - 'title' => sprintf( __( "Stateless mode now requires the Cache-Busting option.", ud_get_stateless_media()->domain ) ),
1309 - 'message' => sprintf( __("WordPress looks at local files to prevent files with the same filenames.
1310 - 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.
1311 - 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" ),
1312 - ), 'notice' );
1790 + 'title' => sprintf(__("Stateless mode now requires the Cache-Busting option.", ud_get_stateless_media()->domain)),
1791 + 'message' => sprintf(__("WordPress looks at local files to prevent files with the same filenames.
1792 + 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.
1793 + Override with the <a href='%s' target='_blank'>%s</a> constant.", ud_get_stateless_media()->domain), ud_get_stateless_media()->get_docs_page_url('docs/constants/#wpstatelessmediacachebusting'), "WP_STATELESS_MEDIA_CACHE_BUSTING"),
1794 + ), 'notice');
1313 1795 }
1314 1796
1315 1797 /**
1316 1798 * Filter for wp_get_attachment_url();
1317 - *
1318 1799 * @param string $url
1319 1800 * @param string $post_id
1320 1801 * @return mixed|null|string
1321 1802 */
1322 - public function wp_get_attachment_url( $url = '', $post_id = '' ) {
1323 - $sm_cloud = get_post_meta( $post_id, 'sm_cloud', 1 );
1324 - if( is_array( $sm_cloud ) && !empty( $sm_cloud[ 'fileLink' ] ) ) {
1325 - $_url = parse_url($sm_cloud[ 'fileLink' ]);
1326 - $url = !isset($_url['scheme']) ? ( 'https:' . $sm_cloud[ 'fileLink' ] ) : $sm_cloud[ 'fileLink' ];
1327 - return apply_filters('wp_stateless_bucket_link', $url);
1803 + public function wp_get_attachment_url($url = '', $post_id = '') {
1804 + global $default_dir;
1805 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $post_id);
1806 + if (is_array($sm_cloud) && !empty($sm_cloud['fileLink'])) {
1807 + $_url = parse_url($sm_cloud['fileLink']);
1808 + $url = !isset($_url['scheme']) ? ('https:' . $sm_cloud['fileLink']) : $sm_cloud['fileLink'];
1809 + $url = apply_filters('wp_stateless_bucket_link', $url);
1810 + } elseif (is_multisite() && empty($sm_cloud)) {
1811 + $_file = get_post_meta($post_id, '_wp_attached_file', true);
1812 + if ($_file) {
1813 + if ($this->get('sm.mode') == 'stateless') {
1814 + $default_dir = true;
1815 + $uploads = wp_get_upload_dir();
1816 + $default_dir = false;
1817 + $url = $uploads['baseurl'] . '/' . $_file;
1818 + } else {
1819 + $uploads = wp_get_upload_dir();
1820 + $default_dir = false;
1821 + $_file = apply_filters('wp_stateless_file_name', $_file, false);
1822 + $blog_id = get_current_blog_id();
1823 + $file_path_fix = $uploads['basedir'] . "/sites/$blog_id/$_file";
1824 +
1825 + if (file_exists($file_path_fix)) {
1826 + $url = $uploads['baseurl'] . "/sites/$blog_id/$_file";
1827 + }
1828 + }
1829 + }
1830 + } elseif (empty($sm_cloud) && $this->get('sm.mode') == 'stateless') {
1831 + $_file = get_post_meta($post_id, '_wp_attached_file', true);
1832 + if ($_file) {
1833 + $default_dir = true;
1834 + $uploads = wp_get_upload_dir();
1835 + $default_dir = false;
1836 +
1837 + $url = $uploads['baseurl'] . '/' . $_file;
1838 + }
1328 1839 }
1329 - return $url;
1840 +
1841 + return apply_filters('wp_stateless_attachment_url', $url, $post_id);
1330 1842 }
1331 1843
1332 1844 /**
1333 1845 * Filter for attachment_url_to_postid()
1334 - *
1846 + *
1335 1847 * @param int|false $post_id originally found post ID (or false if not found)
1336 1848 * @param string $url the URL to find the post ID for
1337 1849 * @return int|false found post ID from cloud storage URL
1338 1850 */
1339 - public function attachment_url_to_postid( $post_id, $url ) {
1851 + public function attachment_url_to_postid($post_id, $url) {
1340 1852 global $wpdb;
1341 1853
1342 - if ( ! $post_id ) {
1854 + if (!$post_id) {
1343 1855 $post_id = get_transient("stateless_url_to_postid_" . md5($url));
1344 1856
1345 - if(defined('WP_STATELESS_LEGACY_URL_TO_POSTID')){
1857 + if (defined('WP_STATELESS_LEGACY_URL_TO_POSTID')) {
1346 1858 // User can use this constant if they change the Bucket Folder (root_dir) after uploading image.
1347 1859 // This can be little slow at first run.
1348 - if(empty($post_id)){
1349 - $query = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sm_cloud' AND meta_value LIKE '%s'";
1350 - $post_id = $wpdb->get_var( $wpdb->prepare( $query, '%' . $url . '%' ) );
1860 + if (empty($post_id)) {
1861 + if ( !$this->get('sm.use_postmeta') ) {
1862 + $query = 'SELECT post_id FROM ' . ud_stateless_db()->files . ' WHERE file_link = %s';
1863 + $post_id = $wpdb->get_var($wpdb->prepare($query, $url));
1864 + }
1865 +
1866 + if (empty($post_id)) {
1867 + $query = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sm_cloud' AND meta_value LIKE '%s'";
1868 + $post_id = $wpdb->get_var($wpdb->prepare($query, '%' . $url . '%'));
1869 + }
1351 1870
1352 - if($post_id){
1871 + if ($post_id) {
1353 1872 set_transient("stateless_url_to_postid_" . md5($url), $post_id);
1354 1873 }
1355 1874 }
1356 1875 return $post_id;
@@ -1355,34 +1874,46 @@
1355 1874 }
1356 1875 return $post_id;
1357 1876 }
1358 1877
1359 - if(empty($post_id)){
1878 + if (empty($post_id)) {
1360 1879 $gs_base_url = $this->get_gs_host();
1361 - $gs_url = $this->get_gs_host() . '/' . $this->get( 'sm.root_dir' );
1880 + $root_dir = $this->get('sm.root_dir');
1881 + $path_elements = apply_filters('wp_stateless_unhandle_root_dir', $url);
1882 + $root_dir = apply_filters("wp_stateless_handle_root_dir", $root_dir, false, $path_elements);
1883 + $gs_url = $this->get_gs_host() . '/' . $root_dir;
1362 1884 $site_url = parse_url($gs_url);
1363 - $image_path = parse_url( $url );
1885 + $image_path = parse_url($url);
1364 1886
1365 1887 //force the protocols to match if needed
1366 - if( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) {
1367 - $url = str_replace( $image_path['scheme'], $site_url['scheme'], $url );
1888 + if (isset($image_path['scheme']) && ($image_path['scheme'] !== $site_url['scheme'])) {
1889 + $url = str_replace($image_path['scheme'], $site_url['scheme'], $url);
1368 1890 }
1369 1891
1370 - if( 0 === strpos( $url, $gs_url . '/' ) ) {
1371 - $url = substr( $url, strlen( $gs_url . '/' ) );
1892 + if (0 === strpos($url, $gs_url . '/')) {
1893 + $url = substr($url, strlen($gs_url . '/'));
1894 + } else if (0 === strpos($url, $gs_base_url . '/')) {
1895 + // In case user added Bucket Folder (root_dir) after uploading image.
1896 + $url = substr($url, strlen($gs_base_url . '/'));
1372 1897 }
1373 - else if( 0 === strpos( $url, $gs_base_url . '/' ) ) {
1374 - // In case user added Bucket Folder (root_dir) after uploading image.
1375 - $url = substr( $url, strlen( $gs_base_url . '/' ) );
1898 +
1899 + /**
1900 + * If `uploads_use_yearmonth_folders` is set - adding year and month to url
1901 + */
1902 + $organize_media = get_option('uploads_use_yearmonth_folders');
1903 + $path = '';
1904 + if ($organize_media == '1' && isset($path_elements['%date_year/date_month%'])) {
1905 + $path .= $path_elements['%date_year/date_month%'] . '/';
1376 1906 }
1907 + $url = $path . $url;
1377 1908
1378 1909 $sql = $wpdb->prepare(
1379 1910 "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
1380 1911 $url
1381 1912 );
1382 - $post_id = $wpdb->get_var( $sql );
1913 + $post_id = $wpdb->get_var($sql);
1383 1914
1384 - if($post_id){
1915 + if ($post_id) {
1385 1916 set_transient("stateless_url_to_postid_" . md5($url), $post_id);
1386 1917 }
1387 1918 }
1388 1919 }
@@ -1390,40 +1921,19 @@
1390 1921 return $post_id;
1391 1922 }
1392 1923
1393 1924 /**
1394 - * Upload the full size image first.
1395 - *
1396 - */
1397 - public function before_intermediate_image_sizes($sizes, $metadata = array()){
1398 - if(empty($metadata)){
1399 - return $sizes;
1400 - }
1401 -
1402 - try{
1403 - $attachment_id = attachment_url_to_postid($metadata['file']);
1404 - $this->add_media(null, $attachment_id, false, array('no_thumb' => true));
1405 - }
1406 - catch(Exception $e){
1407 -
1408 - }
1409 -
1410 - return $sizes;
1411 - }
1412 -
1413 - /**
1414 1925 * Change Upload BaseURL when CDN Used.
1415 1926 *
1416 1927 * @param $data
1417 1928 * @return mixed
1418 1929 */
1419 - public function upload_dir( $data ) {
1420 - $data[ 'basedir' ] = $this->get_gs_host();
1421 - $data[ 'baseurl' ] = $this->get_gs_host();
1422 - $data[ 'url' ] = $data[ 'baseurl' ] . $data[ 'subdir' ];
1930 + public function upload_dir($data) {
1931 + $data['basedir'] = $this->get_gs_host();
1932 + $data['baseurl'] = $this->get_gs_host();
1933 + $data['url'] = $data['baseurl'] . $data['subdir'];
1423 1934
1424 1935 return $data;
1425 -
1426 1936 }
1427 1937
1428 1938 /**
1429 1939 * Set Feature Flag constants by parsing composer.json
@@ -1432,42 +1942,39 @@
1432 1942 *
1433 1943 * @author potanin@UD
1434 1944 * @return array|mixed|null|object
1435 1945 */
1436 - public function parse_feature_flags( ) {
1946 + public function parse_feature_flags() {
1437 1947
1438 1948 try {
1439 1949
1440 - $_raw = file_get_contents( Utility::normalize_path( $this->root_path ) . 'composer.json' );
1950 + $_raw = file_get_contents(Utility::normalize_path($this->root_path) . 'composer.json');
1441 1951
1442 - $_parsed = json_decode( $_raw );
1952 + $_parsed = json_decode($_raw);
1443 1953
1444 1954 // @todo Catch poorly formatted JSON.
1445 - if( !is_object( $_parsed ) ) {
1955 + if (!is_object($_parsed)) {
1446 1956 // throw new Error( "unable to parse." );
1447 1957 }
1448 1958
1449 - foreach( (array) $_parsed->extra->featureFlags as $_feature ) {
1959 + foreach ((array) $_parsed->extra->featureFlags as $_feature) {
1450 1960
1451 - if( !defined( $_feature->constant ) ) {
1452 - define( $_feature->constant, $_feature->enabled );
1961 + if (!defined($_feature->constant)) {
1962 + define($_feature->constant, $_feature->enabled);
1453 1963
1454 - if( $_feature->enabled ) {
1455 - Utility::log( 'Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] enabled.' );
1964 + if ($_feature->enabled) {
1965 + Utility::log('Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] enabled.');
1456 1966 } else {
1457 - Utility::log( 'Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] disabled.' );
1967 + Utility::log('Feature flag ' . $_feature->name . ', [' . $_feature->constant . '] disabled.');
1458 1968 }
1459 1969 }
1460 -
1461 1970 }
1462 -
1463 - } catch ( Exception $e ) {
1464 - Utility::log( 'Unable to parse [composer.json] feature flags. Error: [' . $e->getMessage() . ']' );
1971 + } catch (\Exception $e) {
1972 + Utility::log('Unable to parse [composer.json] feature flags. Error: [' . $e->getMessage() . ']');
1465 1973 // echo 'Caught exception: ', $e->getMessage(), "\n";
1466 1974 }
1467 1975
1468 - return isset( $_parsed ) ? $_parsed : null;
1469 -
1976 + return isset($_parsed) ? $_parsed : null;
1470 1977 }
1471 1978
1472 1979 /**
1473 1980 * Determine if Utility class contains missed function
@@ -1477,14 +1984,137 @@
1477 1984 * @param $name
1478 1985 * @param $arguments
1479 1986 * @return mixed|null
1480 1987 */
1481 - public function __call( $name, $arguments ) {
1482 - if( is_callable( array( "wpCloud\\StatelessMedia\\Utility", $name ) ) ) {
1483 - return call_user_func_array( array( "wpCloud\\StatelessMedia\\Utility", $name ), $arguments );
1988 + public function __call($name, $arguments) {
1989 + if (is_callable(array("wpCloud\\StatelessMedia\\Utility", $name))) {
1990 + return call_user_func_array(array("wpCloud\\StatelessMedia\\Utility", $name), $arguments);
1484 1991 } else {
1485 1992 return NULL;
1486 1993 }
1994 + }
1995 +
1996 + /**
1997 + * Get all thumbnail sizes
1998 + *
1999 + * @global $_wp_additional_image_sizes
2000 + * @uses get_intermediate_image_sizes()
2001 + *
2002 + * @param boolean [$unset_disabled = true]
2003 + * @return array
2004 + */
2005 + function get_image_sizes($unset_disabled = true) {
2006 + $wais = &$GLOBALS['_wp_additional_image_sizes'];
2007 +
2008 + $sizes = array();
2009 +
2010 + foreach (get_intermediate_image_sizes() as $_size) {
2011 + if (in_array($_size, array('thumbnail', 'medium', 'medium_large', 'large'))) {
2012 + $sizes[$_size] = array(
2013 + 'width' => get_option("{$_size}_size_w"),
2014 + 'height' => get_option("{$_size}_size_h"),
2015 + 'crop' => (bool) get_option("{$_size}_crop"),
2016 + );
2017 + } elseif (isset($wais[$_size])) {
2018 + $sizes[$_size] = array(
2019 + 'width' => $wais[$_size]['width'],
2020 + 'height' => $wais[$_size]['height'],
2021 + 'crop' => $wais[$_size]['crop'],
2022 + );
2023 + }
2024 +
2025 + // size registered, but has 0 width and height
2026 + if ($unset_disabled && ($sizes[$_size]['width'] == 0) && ($sizes[$_size]['height'] == 0))
2027 + unset($sizes[$_size]);
2028 + }
2029 +
2030 + return $sizes;
2031 + }
2032 +
2033 + /**
2034 + * Get the email for notifications
2035 + *
2036 + * @return string
2037 + */
2038 + public function get_notification_email() {
2039 + $type = $this->get( 'sm.status_email_type' );
2040 +
2041 + switch ($type) {
2042 + case 'true':
2043 + // Emails to Admin
2044 + return get_site_option('admin_email');
2045 + case 'custom':
2046 + // Emails to Custom Email
2047 + return $this->get( 'sm.status_email_address' );
2048 + }
2049 +
2050 + // Emails disabled
2051 + return '';
2052 + }
2053 +
2054 + /**
2055 + * Send admin email
2056 + *
2057 + * @param string $subject
2058 + * @param string $message
2059 + * @param string $email
2060 + */
2061 + public function send_admin_email($subject, $message, $email = '') {
2062 + $admin_email = empty($email) ? $this->get_notification_email() : $email;
2063 +
2064 + $admin_email = explode(',', $admin_email);
2065 + $admin_email = array_map( function($item) {
2066 + return strip_tags( trim($item) );
2067 + }, $admin_email);
2068 +
2069 + if ( empty($admin_email) || empty($subject) || empty($message) ) {
2070 + return;
2071 + }
2072 +
2073 + wp_mail( $admin_email, $subject, $message);
2074 + }
2075 +
2076 + /**
2077 + * Check if we are in specific mode
2078 + *
2079 + * @param string|array $mode
2080 + * @return bool
2081 + */
2082 + public function is_mode($mode) {
2083 + if ( !is_array($mode) ) {
2084 + $mode = [$mode];
2085 + }
2086 +
2087 + return in_array($this->get('sm.mode'), $mode);
2088 + }
2089 +
2090 + /**
2091 + * Get default cache control value
2092 + *
2093 + * @return string
2094 + */
2095 + public function get_default_cache_control() {
2096 + return Settings::DEFAULT_CACHE_CONTROL;
2097 + }
2098 +
2099 + /**
2100 + * Override REST API root for headless CMS
2101 + *
2102 + * @return string
2103 + */
2104 + public function get_rest_api_root($rest_api_root) {
2105 + $rest_api_root = sanitize_url( get_rest_url() );
2106 + $rest_api_root .= 'wp-stateless/v1/';
2107 +
2108 + if ( $this->get('sm.use_api_siteurl') == 'WP_SITEURL' ) {
2109 + $home = get_home_url();
2110 + $site = get_site_url();
2111 +
2112 + return str_replace( $home, $site, $rest_api_root );
2113 + }
2114 +
2115 +
2116 + return $rest_api_root;
1487 2117 }
1488 2118 }
1489 2119 }
1490 2120 }