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 +302 -133 3.2.2trunk View file →
@@ -12,13 +12,16 @@
12 12 use Google\Auth\HttpHandler\HttpHandlerFactory;
13 13 use wpCloud\StatelessMedia\Sync\FileSync;
14 14 use wpCloud\StatelessMedia\Sync\ImageSync;
15 15 use wpCloud\StatelessMedia\Sync\NonLibrarySync;
16 + use wpCloud\StatelessMedia\Batch\BatchTaskManager;
16 17
17 18 if (!class_exists('wpCloud\StatelessMedia\Bootstrap')) {
18 19
19 20 final class Bootstrap extends \UsabilityDynamics\WP\Bootstrap_Plugin {
20 21
22 + const REQUIRED_PHP_VERSION = '5.5';
23 +
21 24 /**
22 25 * Google Storage Client
23 26 * Use $this->get_client()
24 27 *
@@ -51,10 +54,19 @@
51 54 * @param $args
52 55 * @author peshkov@UD
53 56 */
54 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 +
55 65 parent::__construct($args);
56 66
67 + self::$version = $args['version'] ?? self::$version;
68 +
57 69 /**
58 70 * Add custom args to api ping request
59 71 */
60 72 add_filter('ud-api-client-ping-args', function ($args, $_, $__) {
@@ -69,10 +81,12 @@
69 81 )));
70 82
71 83 // Initialize compatibility modules.
72 84 add_action('plugins_loaded', function () {
85 + Addons::instance();
73 86 new Module();
74 - });
87 + DynamicImageSupport::instance();
88 + }, 20);
75 89
76 90 /**
77 91 * Define settings and UI.
78 92 *
@@ -84,8 +98,9 @@
84 98 * Manually Update/Add option
85 99 * $this->set( 'sm.client_id', 'zxcvv12adffse' );
86 100 */
87 101 $this->settings = new Settings($this);
102 + Status::instance();
88 103 }
89 104
90 105 /**
91 106 * Prevent loading of textdomain
@@ -96,12 +111,21 @@
96 111 /**
97 112 * Instantiate class.
98 113 */
99 114 public function init() {
100 - // Parse feature falgs, set constants.
115 + // Parse feature flags, set constants.
101 116 $this->parse_feature_flags();
102 117 $sm_mode = $this->get('sm.mode');
103 118
119 + /**
120 + * Send admin email
121 + */
122 + add_action('wp_stateless_send_admin_email', array($this, 'send_admin_email'), 10, 3);
123 +
124 + // Should be created unconditionally and as early as possible to handle batch migration requests
125 + Migrator::instance();
126 + BatchTaskManager::instance();
127 +
104 128 new SyncNonMedia();
105 129
106 130 ImageSync::instance();
107 131 FileSync::instance();
@@ -115,8 +139,11 @@
115 139
116 140 // Register meta boxes and fields for media modal page
117 141 add_filter('attachment_fields_to_edit', array($this, 'attachment_modal_meta_box_callback'), 11, 2);
118 142
143 + // Get the REST API root
144 + add_filter('wp_stateless_rest_api_root', array($this, 'get_rest_api_root'));
145 +
119 146 /**
120 147 * Init hook
121 148 */
122 149 add_action('admin_init', array($this, 'admin_init'));
@@ -155,10 +182,10 @@
155 182
156 183 /**
157 184 * To prevent fatal errors for users who use PHP 5.5 or less.
158 185 */
159 - if (version_compare(PHP_VERSION, '5.5', '<')) {
160 - $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>'));
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>'));
161 188 }
162 189
163 190 /**
164 191 * Add the currently processing nag
@@ -168,9 +195,9 @@
168 195 $this->errors->add([
169 196 'title' => __('Media Library Synchronization Underway', ud_get_stateless_media()->domain),
170 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),
171 198 'button' => __('View Synchronization', ud_get_stateless_media()->domain),
172 - 'button_link' => admin_url('upload.php?page=stateless-settings#stless_sync_tab'),
199 + 'button_link' => admin_url('upload.php?page=stateless-settings&tab=stless_sync_tab'),
173 200 'key' => 'processing-in-progress'
174 201 ], 'message');
175 202 break;
176 203 }
@@ -185,13 +212,13 @@
185 212 */
186 213 $is_connected = $this->is_connected_to_gs();
187 214
188 215 if (is_wp_error($is_connected)) {
189 - $this->errors->add($is_connected->get_error_message(), 'warning');
216 + $this->errors->add($is_connected->get_error_message(), 'warning', false);
190 217 }
191 218
192 219 if ($googleSDKVersionConflictError = get_transient("wp_stateless_google_sdk_conflict")) {
193 - $this->errors->add($googleSDKVersionConflictError, 'warning');
220 + $this->errors->add($googleSDKVersionConflictError, 'warning', false);
194 221 }
195 222
196 223 /**
197 224 * Carry on only if we do not have errors.
@@ -212,8 +239,28 @@
212 239 add_filter('intermediate_image_sizes_advanced', array('wpCloud\StatelessMedia\Utility', 'store_can_delete_attachment'), 10, 3);
213 240 }
214 241 }
215 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 +
216 263 if ($this->get('sm.delete_remote') == 'true') {
217 264 /**
218 265 * On physical file deletion we remove any from GS
219 266 * We need priority grater than default (10) for ShortPixel plugin to work properly.
@@ -227,32 +274,53 @@
227 274 $this->_init_filters('client');
228 275 }
229 276 } elseif ($sm_mode == 'stateless') {
230 277 /**
231 - * Replacing local path to gs:// for using it on StreamWrapper
278 + * Determine if we have issues with connection to Google Storage Bucket
279 + * if SM is not disabled.
232 280 */
233 - add_filter('upload_dir', array($this, 'filter_upload_dir'), 99);
281 + $is_connected = $this->is_connected_to_gs();
234 282
235 - /**
236 - * Stateless mode working only with GD library
237 - */
238 - add_filter('wp_image_editors', array($this, 'select_wp_image_editors'));
283 + if (is_wp_error($is_connected)) {
284 + $this->errors->add($is_connected->get_error_message(), 'warning', false);
285 + }
239 286
240 - //init GS client
241 - global $gs_client;
242 - if ($gs_client = $this->init_gs_client()) {
243 - StreamWrapper::register($gs_client);
287 + if ($googleSDKVersionConflictError = get_transient("wp_stateless_google_sdk_conflict")) {
288 + $this->errors->add($googleSDKVersionConflictError, 'warning', false);
244 289 }
245 290
246 291 /**
247 - * init client's filters
292 + * Carry on only if we do not have errors.
248 293 */
249 - $this->_init_filters('client');
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);
250 299
251 - /**
252 - * init main filters
253 - */
254 - $this->_init_filters('main');
300 + /**
301 + * Stateless mode working only with GD library
302 + */
303 + add_filter('wp_image_editors', array($this, 'select_wp_image_editors'));
304 +
305 + /**
306 + * Init GS client
307 + */
308 + global $gs_client;
309 + if ($gs_client = $this->init_gs_client()) {
310 + StreamWrapper::register($gs_client);
311 + }
312 +
313 + /**
314 + * init client's filters
315 + */
316 + $this->_init_filters('client');
317 +
318 + /**
319 + * init main filters
320 + */
321 + $this->_init_filters('main');
322 + }
255 323 }
256 324 }
257 325
258 326 /**
@@ -290,14 +358,11 @@
290 358 add_filter('sanitize_file_name', array('wpCloud\StatelessMedia\Utility', 'randomize_filename'), 10);
291 359 }
292 360
293 361 /**
294 - * Override Cache Control is option is enabled
362 + * Override Cache Control if option is enabled
295 363 */
296 - $cacheControl = trim($this->get('sm.cache_control'));
297 - if (!empty($cacheControl)) {
298 - add_filter('sm:item:cacheControl', array($this, 'override_cache_control'));
299 - }
364 + add_filter('sm:item:cacheControl', array($this->settings, 'override_cache_control'));
300 365
301 366 add_filter('wp_stateless_file_name', array($this, 'handle_root_dir'), 10, 4);
302 367
303 368 /**
@@ -355,9 +420,9 @@
355 420 * @param callable|null $httpHandler
356 421 * @return StorageClient
357 422 * @throws \Exception
358 423 */
359 - public function init_gs_client(callable $httpHandler = null) {
424 + public function init_gs_client(?callable $httpHandler = null) {
360 425 // May be Loading Google SDK....
361 426 if (!class_exists('HttpHandlerFactory')) {
362 427 include_once(ud_get_stateless_media()->path('lib/Google/vendor/autoload.php', 'dir'));
363 428 }
@@ -497,9 +562,9 @@
497 562 $image['url'] = $image_src;
498 563 }
499 564 }
500 565
501 - return array_filter($sources);
566 + return is_array($sources) ? array_filter($sources) : $sources;
502 567 }
503 568
504 569 /**
505 570 * Return gs host.
@@ -530,8 +595,20 @@
530 595 return apply_filters('get_gs_host', $image_host, $image_host, $sm['bucket'], $is_ssl, $sm);
531 596 }
532 597
533 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 + /**
534 611 * Filter for wp_stateless_bucket_link if custom domain is set.
535 612 * It's get attachment url and remove "storage.googleapis.com" from url.
536 613 * So that custom url can be used.
537 614 *
@@ -568,8 +645,20 @@
568 645 return $url . $path;
569 646 }
570 647
571 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 + /**
572 661 * Get new blog settings once switched blog.
573 662 * @param $new_blog
574 663 * @param $prev_blog_id
575 664 */
@@ -638,9 +727,11 @@
638 727 public function add_custom_row_actions($actions, $post, $detached) {
639 728
640 729 if (!current_user_can('upload_files')) return $actions;
641 730
642 - $sm_cloud = get_post_meta($post->ID, 'sm_cloud', 1);
731 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $post->ID);
732 +
733 +
643 734 $sm_mode = $this->get('sm.mode');
644 735 if (!empty($sm_cloud) && $sm_mode === 'stateless') return $actions;
645 736
646 737 if ($post && 'attachment' == $post->post_type && 'image/' == substr($post->post_mime_type, 0, 6)) {
@@ -704,10 +795,22 @@
704 795 'methods' => \WP_REST_Server::CREATABLE,
705 796 'callback' => array($api_namespace, 'syncStop'),
706 797 'permission_callback' => array($api_namespace, 'authCheck')
707 798 ));
708 - }
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 + ));
709 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 +
710 813 /**
711 814 * Metabox for media modal page
712 815 * @param $form_fields
713 816 * @param $post
@@ -713,11 +816,11 @@
713 816 * @param $post
714 817 * @return array
715 818 */
716 819 public function attachment_modal_meta_box_callback($form_fields, $post) {
717 -
718 - //do not show on media edit page, only on modal
719 - if (isset($_GET['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() ) {
720 823 return $form_fields;
721 824 }
722 825
723 826 $link = get_edit_post_link($post->ID);
@@ -723,9 +826,9 @@
723 826 $link = get_edit_post_link($post->ID);
724 827
725 828 $form_field['label'] = '';
726 829 $form_field['input'] = 'html';
727 - $form_field['html'] = sprintf("<script>jQuery('.actions').prepend('<a href=\"%s#sm-attachment-metabox\">%s</a> | ')</script>", $link, __("View stateless meta", ud_get_stateless_media()->domain));
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));
728 831 $form_field['show_in_modal'] = true;
729 832
730 833 $form_fields['sm_html'] = $form_field;
731 834 return $form_fields;
@@ -758,9 +861,9 @@
758 861 * @return array
759 862 */
760 863 private function _prepare_data_for_metabox($meta_boxes, $post_id) {
761 864 $post = get_post($post_id);
762 - $sm_cloud = get_post_meta($post_id, 'sm_cloud', 1);
865 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $post_id, true);
763 866 $sm_mode = $this->get('sm.mode');
764 867
765 868 if (empty($post)) {
766 869 return $meta_boxes;
@@ -901,14 +1004,12 @@
901 1004 * @param $size
902 1005 * @return string
903 1006 */
904 1007 public function handle_root_dir($current_path, $use_root = true, $attachment_id = '', $size = '') {
905 - global $wpdb;
906 -
907 1008 //non media files
908 1009 if ($use_root === 0) {
909 - $table_name = $wpdb->prefix . 'sm_sync';
910 - $non_media = $wpdb->get_var($wpdb->prepare("SELECT file FROM {$table_name} WHERE file like '%%%s';", $current_path));
1010 + $non_media = ud_stateless_db()->get_non_library_file_name($current_path);
1011 +
911 1012 if ($non_media) {
912 1013 return $non_media;
913 1014 }
914 1015 }
@@ -1099,28 +1200,8 @@
1099 1200 return $meta;
1100 1201 }
1101 1202
1102 1203 /**
1103 - * @param $links
1104 - * @param $file
1105 - * @return mixed
1106 - */
1107 - public function plugin_action_links($links, $file) {
1108 -
1109 - if ($file == plugin_basename(dirname(__DIR__) . '/wp-stateless-media.php')) {
1110 - $settings_link = '<a href="' . '' . '">' . __('Settings', 'ssd') . '</a>';
1111 - array_unshift($links, $settings_link);
1112 - }
1113 -
1114 - if ($file == plugin_basename(dirname(__DIR__) . '/wp-stateless.php')) {
1115 - $settings_link = '<a href="' . '' . '">' . __('Settings', 'ssd') . '</a>';
1116 - array_unshift($links, $settings_link);
1117 - }
1118 -
1119 - return $links;
1120 - }
1121 -
1122 - /**
1123 1204 * Determines if plugin is loaded via mu-plugins
1124 1205 * or Network Enabled.
1125 1206 *
1126 1207 * @param bool $is_multisite
@@ -1163,29 +1244,38 @@
1163 1244 wp_register_style('wp-stateless-select2', $this->path('static/styles/select2.min.css', 'url'), array(), self::$version);
1164 1245
1165 1246 /* Attachment or upload page */
1166 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 + ]);
1167 1251
1168 1252 /* Setup wizard styles. */
1169 - wp_register_style('wp-stateless-setup-wizard', $this->path('static/styles/wp-stateless-setup-wizard.css', 'url'), array(), self::$version);
1253 + // #152
1254 + // wp_register_style('wp-stateless-setup-wizard', $this->path('static/styles/wp-stateless-setup-wizard.css', 'url'), array(), self::$version);
1170 1255
1171 1256 wp_register_script('wp-stateless-select2', ud_get_stateless_media()->path('static/scripts/select2.min.js', 'url'), array('jquery'), self::$version, true);
1172 1257
1173 1258 /* Stateless settings page */
1174 - wp_register_script('wp-stateless-settings', ud_get_stateless_media()->path('static/scripts/wp-stateless-settings.js', 'url'), array(), self::$version);
1259 + wp_register_script('wp-stateless-settings', ud_get_stateless_media()->path('static/scripts/wp-stateless-settings.js', 'url'), array('clipboard'), self::$version);
1175 1260 wp_localize_script('wp-stateless-settings', 'stateless_l10n', $this->get_l10n_data());
1176 1261
1177 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);
1178 1265
1179 1266 // Sync tab
1180 - wp_register_script('wp-stateless-angular', ud_get_stateless_media()->path('static/scripts/angular.min.js', 'url'), array(), '1.8.0', true);
1181 - wp_register_script('wp-stateless-angular-sanitize', ud_get_stateless_media()->path('static/scripts/angular-sanitize.min.js', 'url'), array('wp-stateless-angular'), '1.8.0', true);
1182 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);
1183 1268
1184 1269 wp_localize_script('wp-stateless', 'stateless_l10n', $this->get_l10n_data());
1185 1270 wp_localize_script('wp-stateless', 'wp_stateless_configs', array(
1186 1271 'WP_DEBUG' => defined('WP_DEBUG') ? WP_DEBUG : false,
1187 - 'REST_API_TOKEN' => Utility::generate_jwt_token(['user_id' => get_current_user_id()], DAY_IN_SECONDS)
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),
1188 1278 ));
1189 1279
1190 1280 $settings = ud_get_stateless_media()->get('sm');
1191 1281 $settings['wildcards'] = $this->settings->wildcards;
@@ -1194,9 +1284,16 @@
1194 1284 if (defined('WP_STATELESS_MEDIA_JSON_KEY') && WP_STATELESS_MEDIA_JSON_KEY) {
1195 1285 $settings['key_json'] = "Currently configured via a constant.";
1196 1286 }
1197 1287 wp_localize_script('wp-stateless', 'wp_stateless_settings', $settings);
1198 - wp_localize_script('wp-stateless', 'wp_stateless_compatibility', Module::get_modules());
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 + ));
1199 1296 }
1200 1297
1201 1298 /**
1202 1299 * Get_l10n_data
@@ -1238,12 +1335,14 @@
1238 1335 }
1239 1336
1240 1337 break;
1241 1338
1242 - case 'media_page_stateless-setup':
1339 + // #152
1340 + // case 'media_page_stateless-setup':
1243 1341 case 'settings_page_stateless-setup':
1244 1342 wp_enqueue_style('wp-stateless');
1245 - wp_enqueue_style('wp-stateless-setup-wizard');
1343 + // #152
1344 + // wp_enqueue_style('wp-stateless-setup-wizard');
1246 1345 break;
1247 1346 case 'media_page_stateless-settings':
1248 1347 case 'settings_page_stateless-settings':
1249 1348 wp_enqueue_style('wp-stateless');
@@ -1250,14 +1349,20 @@
1250 1349 wp_enqueue_style('wp-stateless-select2');
1251 1350 wp_enqueue_script('wp-stateless-settings');
1252 1351 wp_enqueue_script('wp-stateless-select2');
1253 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' );
1254 1356
1255 1357 // Sync tab
1256 - wp_enqueue_script('wp-stateless-angular');
1257 - wp_enqueue_script('wp-stateless-angular-sanitize');
1258 1358 wp_enqueue_script('wp-stateless');
1259 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 +
1260 1365 wp_enqueue_style('wp-pointer');
1261 1366 wp_enqueue_script('wp-pointer');
1262 1367
1263 1368 $data = array(
@@ -1263,11 +1368,11 @@
1263 1368 $data = array(
1264 1369 'key' => 'stateless-cache-busting',
1265 1370 'class' => 'notice',
1266 1371 'title' => sprintf(__("Stateless and Ephemeral modes enables and requires the Cache-Busting option.", ud_get_stateless_media()->domain)),
1267 - 'message' => sprintf(__("WordPress looks at local files to prevent files with the same filenames.
1268 - 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.
1269 - Override with the <a href='%s' target='_blank'>%s</a> constant.", ud_get_stateless_media()->domain), "https://wp-stateless.github.io/docs/constants/#wp_stateless_media_cache_busting", "WP_STATELESS_MEDIA_CACHE_BUSTING"),
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"),
1270 1375 );
1271 1376 echo "<script id='template-stateless-cache-busting' type='text/html'>";
1272 1377 include ud_get_stateless_media()->path('/static/views/error-notice.php', 'dir');
1273 1378 echo "</script>";
@@ -1287,10 +1392,10 @@
1287 1392 * @param $size
1288 1393 * @return mixed
1289 1394 */
1290 1395 public function wp_get_attachment_image_attributes($attr, $attachment, $size = null) {
1291 -
1292 - $sm_cloud = get_post_meta($attachment->ID, 'sm_cloud', true);
1396 +
1397 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $attachment->ID);
1293 1398 if (is_array($sm_cloud) && !empty($sm_cloud['name'])) {
1294 1399 $attr['class'] = $attr['class'] . ' wp-stateless-item';
1295 1400 $attr['data-image-size'] = is_array($size) ? implode('x', $size) : $size;
1296 1401 $attr['data-stateless-media-bucket'] = isset($sm_cloud['bucket']) ? $sm_cloud['bucket'] : false;
@@ -1358,9 +1463,9 @@
1358 1463 * this case may happen when no local files
1359 1464 * @author korotkov@ud
1360 1465 */
1361 1466 if (!$width && !$height) {
1362 - $sm_cloud = get_post_meta($id, 'sm_cloud', true);
1467 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $id, true);
1363 1468 if (is_string($size) && !empty($sm_cloud['sizes']) && !empty($sm_cloud['sizes'][$size])) {
1364 1469 global $_wp_additional_image_sizes;
1365 1470
1366 1471 $img_url = !empty($sm_cloud['sizes'][$size]['fileLink']) ? $sm_cloud['sizes'][$size]['fileLink'] : $img_url;
@@ -1408,9 +1513,9 @@
1408 1513 public function wp_get_attachment_metadata($metadata, $attachment_id) {
1409 1514 global $default_dir;
1410 1515 $default_dir = false;
1411 1516 /* Determine if the media file has GS data at all. */
1412 - $sm_cloud = get_post_meta($attachment_id, 'sm_cloud', true);
1517 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $attachment_id, true);
1413 1518 // If metadata not passed the get metadata from post meta.
1414 1519 if (empty($metadata)) {
1415 1520 $metadata = get_post_meta($attachment_id, '_wp_attachment_metadata', true);
1416 1521 }
@@ -1449,9 +1554,9 @@
1449 1554 if (file_exists($file_path_fix)) {
1450 1555 $metadata['file'] = "sites/$blog_id/{$metadata['file']}";
1451 1556 }
1452 1557 }
1453 - } elseif (empty($sm_cloud) && $this->get('sm.mode') == 'stateless') {
1558 + } elseif ( empty($sm_cloud) && $this->get('sm.mode') == 'stateless' && isset($metadata['file']) ) {
1454 1559 $default_dir = true;
1455 1560 $uploads = wp_get_upload_dir();
1456 1561 $default_dir = false;
1457 1562
@@ -1472,9 +1577,9 @@
1472 1577 */
1473 1578 public function get_attached_file($file, $attachment_id) {
1474 1579 global $default_dir;
1475 1580
1476 - $sm_cloud = get_post_meta($attachment_id, 'sm_cloud', true);
1581 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $attachment_id);
1477 1582 $_file = get_post_meta($attachment_id, '_wp_attached_file', true);
1478 1583
1479 1584 /* Determine if the media file has GS data at all. */
1480 1585 if (is_multisite() && $_file) {
@@ -1527,9 +1632,8 @@
1527 1632 *
1528 1633 * @author peshkov@UD
1529 1634 */
1530 1635 public function is_connected_to_gs() {
1531 -
1532 1636 $trnst = get_transient('sm::is_connected_to_gs');
1533 1637
1534 1638 if (empty($trnst) || false === $trnst || !isset($trnst['hash']) || $trnst['hash'] != md5(serialize($this->get('sm')))) {
1535 1639 $trnst = array(
@@ -1545,9 +1649,9 @@
1545 1649 } else {
1546 1650 $connected = $client->is_connected();
1547 1651 if ($connected !== true) {
1548 1652 $trnst['success'] = 'false';
1549 - $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'));
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')));
1550 1654
1551 1655 if (is_callable(array($connected, 'getHandlerContext')) && $handlerContext = $connected->getHandlerContext()) {
1552 1656 if (!empty($handlerContext['error'])) {
1553 1657 $handlerContext['error'];
@@ -1565,9 +1669,9 @@
1565 1669 set_transient('sm::is_connected_to_gs', $trnst, 4 * HOUR_IN_SECONDS);
1566 1670 }
1567 1671
1568 1672 if (isset($trnst['success']) && $trnst['success'] == 'false') {
1569 - return new \WP_Error('error', (!empty($trnst['error']) ? $trnst['error'] : __('There is an Error on connection to Google Storage.', $this->domain)));
1673 + return new \WP_Error('error', (!empty($trnst['error']) ? $trnst['error'] : 'There is an Error on connection to Google Storage.'));
1570 1674 }
1571 1675
1572 1676 return true;
1573 1677 }
@@ -1609,9 +1713,12 @@
1609 1713 * @author alim@UD
1610 1714 */
1611 1715 public function run_upgrade_process() {
1612 1716 // Creating database on new installation.
1613 - $this->create_db();
1717 + ud_stateless_db()->create_db();
1718 +
1719 + Migrator::instance()->migrate();
1720 +
1614 1721 /**
1615 1722 * Maybe Upgrade current Version
1616 1723 */
1617 1724 Upgrader::call($this->args['version']);
@@ -1617,50 +1724,14 @@
1617 1724 Upgrader::call($this->args['version']);
1618 1725 }
1619 1726
1620 1727 /**
1621 - * Create database on plugin activation.
1622 - * @param boolean $force - whether to create db even if option exists. For debug purpose only.
1623 - */
1624 - public function create_db($force = false) {
1625 - global $wpdb;
1626 - $sm_sync_db_version = get_option('sm_sync_db_version');
1627 -
1628 - if ($sm_sync_db_version && $force == false) {
1629 - return;
1630 - }
1631 -
1632 - $table_name = $wpdb->prefix . 'sm_sync';
1633 - $charset_collate = $wpdb->get_charset_collate();
1634 -
1635 - // `expire` timestamp NULL DEFAULT NULL,
1636 - $sql = "CREATE TABLE $table_name (
1637 - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT ,
1638 - `file` varchar(255) NOT NULL ,
1639 - `status` varchar(10) NOT NULL ,
1640 - PRIMARY KEY (`id`)
1641 - ) $charset_collate;";
1642 -
1643 - require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1644 - dbDelta($sql);
1645 -
1646 - add_option('sm_sync_db_version', $this->args['version']);
1647 - }
1648 -
1649 - /**
1650 1728 * Delete table when blog is deleted.
1651 1729 *
1652 1730 * @param $old_site
1653 1731 */
1654 1732 public function wp_delete_site($old_site) {
1655 - global $wpdb;
1656 -
1657 - switch_to_blog($old_site->id);
1658 - $table_name = $wpdb->prefix . 'sm_sync';
1659 -
1660 - $sql = "DROP TABLE IF EXISTS $table_name";
1661 - $wpdb->query($sql);
1662 - restore_current_blog();
1733 + ud_stateless_db()->clear_db($old_site->id);
1663 1734 }
1664 1735
1665 1736 /**
1666 1737 * Redirect_to_splash
@@ -1687,13 +1758,17 @@
1687 1758 exit(wp_redirect($url));
1688 1759 }
1689 1760
1690 1761 if ($plugin == plugin_basename($this->boot_file)) {
1691 - $url = $this->get_settings_page_url('?page=stateless-setup&step=splash-screen');
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 +
1692 1766 if (json_decode($this->settings->get('sm.key_json'))) {
1693 1767 $url = $this->get_settings_page_url('?page=stateless-settings');
1694 1768 }
1695 - exit(wp_redirect($url));
1769 +
1770 + exit( wp_redirect($url) );
1696 1771 }
1697 1772 }
1698 1773
1699 1774 /**
@@ -1712,11 +1787,11 @@
1712 1787 'key' => 'stateless_cache_busting',
1713 1788 'button' => 'View Settings',
1714 1789 'button_link' => admin_url('upload.php?page=stateless-settings'),
1715 1790 'title' => sprintf(__("Stateless mode now requires the Cache-Busting option.", ud_get_stateless_media()->domain)),
1716 - 'message' => sprintf(__("WordPress looks at local files to prevent files with the same filenames.
1717 - 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.
1718 - Override with the <a href='%s' target='_blank'>%s</a> constant.", ud_get_stateless_media()->domain), "https://wp-stateless.github.io/docs/constants/#wp_stateless_media_cache_busting", "WP_STATELESS_MEDIA_CACHE_BUSTING"),
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"),
1719 1794 ), 'notice');
1720 1795 }
1721 1796
1722 1797 /**
@@ -1726,13 +1801,13 @@
1726 1801 * @return mixed|null|string
1727 1802 */
1728 1803 public function wp_get_attachment_url($url = '', $post_id = '') {
1729 1804 global $default_dir;
1730 - $sm_cloud = get_post_meta($post_id, 'sm_cloud', 1);
1805 + $sm_cloud = apply_filters('wp_stateless_get_file', [], $post_id);
1731 1806 if (is_array($sm_cloud) && !empty($sm_cloud['fileLink'])) {
1732 1807 $_url = parse_url($sm_cloud['fileLink']);
1733 1808 $url = !isset($_url['scheme']) ? ('https:' . $sm_cloud['fileLink']) : $sm_cloud['fileLink'];
1734 - return apply_filters('wp_stateless_bucket_link', $url);
1809 + $url = apply_filters('wp_stateless_bucket_link', $url);
1735 1810 } elseif (is_multisite() && empty($sm_cloud)) {
1736 1811 $_file = get_post_meta($post_id, '_wp_attached_file', true);
1737 1812 if ($_file) {
1738 1813 if ($this->get('sm.mode') == 'stateless') {
@@ -1738,9 +1813,9 @@
1738 1813 if ($this->get('sm.mode') == 'stateless') {
1739 1814 $default_dir = true;
1740 1815 $uploads = wp_get_upload_dir();
1741 1816 $default_dir = false;
1742 - return $uploads['baseurl'] . '/' . $_file;
1817 + $url = $uploads['baseurl'] . '/' . $_file;
1743 1818 } else {
1744 1819 $uploads = wp_get_upload_dir();
1745 1820 $default_dir = false;
1746 1821 $_file = apply_filters('wp_stateless_file_name', $_file, false);
@@ -1758,12 +1833,13 @@
1758 1833 $default_dir = true;
1759 1834 $uploads = wp_get_upload_dir();
1760 1835 $default_dir = false;
1761 1836
1762 - return $uploads['baseurl'] . '/' . $_file;
1837 + $url = $uploads['baseurl'] . '/' . $_file;
1763 1838 }
1764 1839 }
1765 - return $url;
1840 +
1841 + return apply_filters('wp_stateless_attachment_url', $url, $post_id);
1766 1842 }
1767 1843
1768 1844 /**
1769 1845 * Filter for attachment_url_to_postid()
@@ -1781,10 +1857,17 @@
1781 1857 if (defined('WP_STATELESS_LEGACY_URL_TO_POSTID')) {
1782 1858 // User can use this constant if they change the Bucket Folder (root_dir) after uploading image.
1783 1859 // This can be little slow at first run.
1784 1860 if (empty($post_id)) {
1785 - $query = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sm_cloud' AND meta_value LIKE '%s'";
1786 - $post_id = $wpdb->get_var($wpdb->prepare($query, '%' . $url . '%'));
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 + }
1787 1870
1788 1871 if ($post_id) {
1789 1872 set_transient("stateless_url_to_postid_" . md5($url), $post_id);
1790 1873 }
@@ -1944,8 +2027,94 @@
1944 2027 unset($sizes[$_size]);
1945 2028 }
1946 2029
1947 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;
1948 2117 }
1949 2118 }
1950 2119 }
1951 2120 }