diff --git a/README.md b/README.md index bcefea57..92090551 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **Requires at least:** 4.9 **Tested up to:** 5.5 **Requires PHP:** 5.5 -**Stable tag:** 2.4.4 +**Stable tag:** 2.5 **License:** GPLv3 Copies files to Amazon S3, DigitalOcean Spaces or Google Cloud Storage as they are uploaded to the Media Library. Optionally configure Amazon CloudFront or another CDN for faster delivery. @@ -89,6 +89,13 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin ## Changelog ## +### WP Offload Media Lite 2.5 - 2020-11-11 ### +* [Release Summary Blog Post](https://deliciousbrains.com/wp-offload-media-2-5-released/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting) +* Improvement: [Error notice shown](https://deliciousbrains.com/wp-offload-media/doc/missing-table-error-notice/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting) when plugin's required custom table(s) missing +* Improvement: [Diagnostic Info](https://deliciousbrains.com/wp-offload-media/doc/missing-table-error-notice/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting#diagnostic-info) shows status of plugin's required custom tables +* Bug fix: wp_get_original_image_path function does not return provider URL when local files removed +* Bug fix: File missing notices recorded in debug.log when regenerating thumbnails and Remove Files From Server turned on + ### WP Offload Media Lite 2.4.4 - 2020-09-08 ### * Improvement: Updated AWS PHP SDK to v3.151.6 * Bug fix: Files for duplicate thumbnail sizes not removed from server after initial offload diff --git a/assets/css/attachment.css b/assets/css/attachment.css index b998bae9..e2fe7253 100644 --- a/assets/css/attachment.css +++ b/assets/css/attachment.css @@ -1 +1 @@ -#s3-actions.postbox .inside{margin:0;padding:0}#s3-actions.postbox a,#s3-actions.postbox a:hover{text-decoration:none}#s3-actions.postbox .s3-details{padding:6px 0}#s3-actions.postbox .s3-details .misc-pub-section{clear:both;float:left;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}#s3-actions.postbox .s3-details .misc-pub-section .s3-key{float:left;width:20%;white-space:nowrap}#s3-actions.postbox .s3-details .misc-pub-section .s3-value{font-weight:bold;float:left;width:80%}#s3-actions.postbox .s3-details .misc-pub-section input.error{color:#a00}#s3-actions.postbox .s3-details .not-copied{color:#666}#s3-actions.postbox .s3-actions{padding:10px;clear:both;border-top:1px solid #ddd;border-bottom:1px solid #ddd;background:#f5f5f5}#s3-actions.postbox .s3-actions .copy-action{text-align:right;float:right;line-height:23px}#s3-actions.postbox .s3-actions .remove-action{line-height:28px;vertical-align:middle;text-align:left;float:left}#s3-actions.postbox .s3-actions .remove-action a.local-warning{color:#a00}#s3-actions.postbox .s3-actions .remove-action a.local-warning:hover{color:#f00} +#s3-actions.postbox .inside{margin:0;padding:0}#s3-actions.postbox a,#s3-actions.postbox a:hover{text-decoration:none}#s3-actions.postbox .s3-details{padding:6px 0}#s3-actions.postbox .s3-details .misc-pub-section{clear:both;float:left;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}#s3-actions.postbox .s3-details .misc-pub-section .s3-key{float:left;width:20%;white-space:nowrap}#s3-actions.postbox .s3-details .misc-pub-section .s3-value{font-weight:bold;float:left;width:80%}#s3-actions.postbox .s3-details .misc-pub-section .s3-value .more-info{font-weight:lighter}#s3-actions.postbox .s3-details .misc-pub-section input.error{color:#a00}#s3-actions.postbox .s3-details .not-copied{color:#666}#s3-actions.postbox .s3-actions{padding:10px;clear:both;border-top:1px solid #ddd;border-bottom:1px solid #ddd;background:#f5f5f5}#s3-actions.postbox .s3-actions .copy-action{text-align:right;float:right;line-height:23px}#s3-actions.postbox .s3-actions .remove-action{line-height:28px;vertical-align:middle;text-align:left;float:left}#s3-actions.postbox .s3-actions .remove-action a.local-warning{color:#a00}#s3-actions.postbox .s3-actions .remove-action a.local-warning:hover{color:#f00} diff --git a/assets/js/media.js b/assets/js/media.js index 45aef6c0..9a7e9185 100644 --- a/assets/js/media.js +++ b/assets/js/media.js @@ -63,7 +63,7 @@ var test = {}; return; } var $detailsHtml = this.$el.find( '.attachment-info .details' ); - var html = this.generateDetails( response, [ 'provider_name', 'region', 'bucket', 'key', 'acl' ] ); + var html = this.generateDetails( response, [ 'provider_name', 'region', 'bucket', 'key', 'acl', 'is_verified' ] ); $detailsHtml.append( html ); }, @@ -89,6 +89,16 @@ var test = {}; } } + if ( 'is_verified' === key ) { + value = Boolean( parseInt( value ) ); + + if ( value ) { + return; + } + + value = as3cf_media.strings[ 'not_verified' ]; + } + html += template( { key: key, label: as3cf_media.strings[ key ], diff --git a/assets/js/media.min.js b/assets/js/media.min.js index 195e0b87..60db9f8f 100644 --- a/assets/js/media.min.js +++ b/assets/js/media.min.js @@ -1 +1 @@ -var test={};!function(a,b){var c=wp.media,d=c.view.Attachment.Details.TwoColumn;c.view.Attachment.Details.TwoColumn=d.extend({events:function(){return b.extend({},d.prototype.events,{"click .local-warning":"confirmS3Removal","click #as3cfpro-toggle-acl":"toggleACL"})},render:function(){this.fetchS3Details(this.model.get("id"))},fetchS3Details:function(a){wp.ajax.send("as3cf_get_attachment_provider_details",{data:{_nonce:as3cf_media.nonces.get_attachment_provider_details,id:a}}).done(b.bind(this.renderView,this))},renderView:function(a){d.prototype.render.apply(this),this.renderActionLinks(a),this.renderS3Details(a)},renderActionLinks:function(c){var d=c&&c.links||[],e=this.$el.find(".actions"),f=a("
",{"class":"s3-actions"}),g=[];b(d).each(function(a){g.push(a)}),f.append(g.join(" | ")),e.append(f)},renderS3Details:function(a){if(a&&a.provider_object){var b=this.$el.find(".attachment-info .details"),c=this.generateDetails(a,["provider_name","region","bucket","key","acl"]);b.append(c)}},generateDetails:function(a,c){var d="",e=b.template('
<%= label %>: <%= value %>
');return b(c).each(function(c){if(a.provider_object[c]){var f=a.provider_object[c];if("acl"===c&&(f=a.provider_object[c].name,a.acl_toggle)){var g=b.template('<%= value %>');f=g({title:a.provider_object[c].title,acl:a.provider_object[c].acl,value:f})}d+=e({key:c,label:as3cf_media.strings[c],value:f})}}),d},confirmS3Removal:function(a){if(!confirm(as3cfpro_media.strings.local_warning))return a.preventDefault(),a.stopImmediatePropagation(),!1},toggleACL:function(c){c.preventDefault();var d=a("#as3cfpro-toggle-acl"),e=d.data("currentacl"),f=as3cfpro_media.settings.private_acl;d.hide(),d.after(''+as3cfpro_media.strings.updating_acl+""),e===as3cfpro_media.settings.private_acl&&(f=as3cfpro_media.settings.default_acl),wp.ajax.send("as3cfpro_update_acl",{data:{_ajax_nonce:as3cfpro_media.nonces.singular_update_acl,id:this.model.get("id"),acl:f}}).done(b.bind(this.updateACL,this)).fail(b.bind(this.renderACLError,this))},renderACLError:function(){a("#as3cfpro-updating").remove(),a("#as3cfpro-toggle-acl").show(),alert(as3cfpro_media.strings.change_acl_error)},updateACL:function(b){if(null==b.acl_display||null==b.title||null==b.acl||null==b.url)return void this.renderACLError();this.model.set("url",b.url),this.render();var c=a("#as3cfpro-toggle-acl");a("#as3cfpro-updating").remove(),c.text(b.acl_display),c.attr("title",b.title),c.data("currentacl",b.acl),c.show()}})}(jQuery,_); \ No newline at end of file +var test={};!function(a,b){var c=wp.media,d=c.view.Attachment.Details.TwoColumn;c.view.Attachment.Details.TwoColumn=d.extend({events:function(){return b.extend({},d.prototype.events,{"click .local-warning":"confirmS3Removal","click #as3cfpro-toggle-acl":"toggleACL"})},render:function(){this.fetchS3Details(this.model.get("id"))},fetchS3Details:function(a){wp.ajax.send("as3cf_get_attachment_provider_details",{data:{_nonce:as3cf_media.nonces.get_attachment_provider_details,id:a}}).done(b.bind(this.renderView,this))},renderView:function(a){d.prototype.render.apply(this),this.renderActionLinks(a),this.renderS3Details(a)},renderActionLinks:function(c){var d=c&&c.links||[],e=this.$el.find(".actions"),f=a("
",{"class":"s3-actions"}),g=[];b(d).each(function(a){g.push(a)}),f.append(g.join(" | ")),e.append(f)},renderS3Details:function(a){if(a&&a.provider_object){var b=this.$el.find(".attachment-info .details"),c=this.generateDetails(a,["provider_name","region","bucket","key","acl","is_verified"]);b.append(c)}},generateDetails:function(a,c){var d="",e=b.template('
<%= label %>: <%= value %>
');return b(c).each(function(c){if(a.provider_object[c]){var f=a.provider_object[c];if("acl"===c&&(f=a.provider_object[c].name,a.acl_toggle)){var g=b.template('<%= value %>');f=g({title:a.provider_object[c].title,acl:a.provider_object[c].acl,value:f})}if("is_verified"===c){if(f=Boolean(parseInt(f)))return;f=as3cf_media.strings.not_verified}d+=e({key:c,label:as3cf_media.strings[c],value:f})}}),d},confirmS3Removal:function(a){if(!confirm(as3cfpro_media.strings.local_warning))return a.preventDefault(),a.stopImmediatePropagation(),!1},toggleACL:function(c){c.preventDefault();var d=a("#as3cfpro-toggle-acl"),e=d.data("currentacl"),f=as3cfpro_media.settings.private_acl;d.hide(),d.after(''+as3cfpro_media.strings.updating_acl+""),e===as3cfpro_media.settings.private_acl&&(f=as3cfpro_media.settings.default_acl),wp.ajax.send("as3cfpro_update_acl",{data:{_ajax_nonce:as3cfpro_media.nonces.singular_update_acl,id:this.model.get("id"),acl:f}}).done(b.bind(this.updateACL,this)).fail(b.bind(this.renderACLError,this))},renderACLError:function(){a("#as3cfpro-updating").remove(),a("#as3cfpro-toggle-acl").show(),alert(as3cfpro_media.strings.change_acl_error)},updateACL:function(b){if(null==b.acl_display||null==b.title||null==b.acl||null==b.url)return void this.renderACLError();this.model.set("url",b.url),this.render();var c=a("#as3cfpro-toggle-acl");a("#as3cfpro-updating").remove(),c.text(b.acl_display),c.attr("title",b.title),c.data("currentacl",b.acl),c.show()}})}(jQuery,_); \ No newline at end of file diff --git a/assets/sass/attachment.scss b/assets/sass/attachment.scss index 7de9a845..fd3be86b 100644 --- a/assets/sass/attachment.scss +++ b/assets/sass/attachment.scss @@ -3,9 +3,11 @@ margin: 0; padding: 0; } + a, a:hover { text-decoration: none; } + .s3-details { padding: 6px 0; @@ -22,19 +24,27 @@ width: 20%; white-space: nowrap; } + .s3-value { font-weight: bold; float: left; width: 80%; + + .more-info { + font-weight: lighter; + } } + input.error { color: #a00; } } + .not-copied { color: #666; } } + .s3-actions { padding: 10px; clear: both; @@ -56,6 +66,7 @@ a.local-warning { color: #a00; + &:hover { color: #f00; } diff --git a/classes/amazon-s3-and-cloudfront.php b/classes/amazon-s3-and-cloudfront.php index 2c340ecc..08afe974 100644 --- a/classes/amazon-s3-and-cloudfront.php +++ b/classes/amazon-s3-and-cloudfront.php @@ -1,6 +1,7 @@ get_object_prefix() ); $prefix .= AS3CF_Utils::trailingslash_prefix( $this->get_dynamic_prefix( $time ) ); - if ( $this->get_setting( 'object-versioning' ) ) { + if ( ! empty( $object_versioning_allowed ) && $this->get_setting( 'object-versioning' ) ) { $prefix .= AS3CF_Utils::trailingslash_prefix( $this->get_object_version_string() ); } @@ -2339,19 +2349,20 @@ public function get_file_prefix( $time = null ) { /** * Get attachment's new public prefix path for current settings. * - * @param int $post_id Attachment ID - * @param array $metadata Optional attachment metadata + * @param int $post_id Attachment ID + * @param array $metadata Optional attachment metadata + * @param bool $object_versioning_allowed Can an Object Versioning string be appended if setting turned on? Default true. * * @return string */ - public function get_new_attachment_prefix( $post_id, $metadata = null ) { + public function get_new_attachment_prefix( $post_id, $metadata = null, $object_versioning_allowed = true ) { if ( empty( $metadata ) ) { $metadata = wp_get_attachment_metadata( $post_id, true ); } $time = $this->get_attachment_folder_year_month( $post_id, $metadata ); - return $this->get_file_prefix( $time ); + return $this->get_file_prefix( $time, $object_versioning_allowed ); } /** @@ -2459,15 +2470,9 @@ public function get_attachment_provider_url( $post_id, Media_Library_Item $as3cf // Is a signed expiring URL required for the requested object? if ( is_null( $expires ) ) { - if ( is_null( $size ) && $as3cf_item->is_private() ) { - // Full size URL private - $expires = self::DEFAULT_EXPIRES; - } - - if ( ! is_null( $size ) && $as3cf_item->is_private_size( $size ) ) { - // Alternative size URL private - $expires = self::DEFAULT_EXPIRES; - } + $expires = $as3cf_item->is_private_size( $size ) ? self::DEFAULT_EXPIRES : null; + } else { + $expires = $as3cf_item->is_private_size( $size ) ? $expires : null; } $item_path = $as3cf_item->path(); @@ -2734,10 +2739,11 @@ protected function maybe_sign_intermediate_size( $url, $attachment_id, $size, $a * @param bool $skip_rewrite_check Still check if offloaded even if not currently rewriting URLs? Default: false * @param bool $skip_current_provider_check Skip checking if offloaded to current provider. Default: false, negated if $provider supplied * @param Storage_Provider|null $provider Provider where attachment expected to be offloaded to. Default: currently configured provider + * @param bool $check_is_verified Check that metadata is verified, has no effect if $skip_rewrite_check is true. Default: false * * @return bool|Media_Library_Item */ - public function is_attachment_served_by_provider( $attachment_id, $skip_rewrite_check = false, $skip_current_provider_check = false, Storage_Provider $provider = null ) { + public function is_attachment_served_by_provider( $attachment_id, $skip_rewrite_check = false, $skip_current_provider_check = false, Storage_Provider $provider = null, $check_is_verified = false ) { if ( ! $skip_rewrite_check && ! $this->get_setting( 'serve-from-s3' ) ) { // Not serving provider URLs return false; @@ -2750,6 +2756,11 @@ public function is_attachment_served_by_provider( $attachment_id, $skip_rewrite_ return false; } + if ( ! $skip_rewrite_check && ! empty( $check_is_verified ) && ! $as3cf_item->is_verified() ) { + // Offload not verified, treat as not offloaded. + return false; + } + if ( ! $skip_current_provider_check && empty( $provider ) ) { $provider = $this->get_storage_provider(); } @@ -2791,6 +2802,9 @@ function update_attached_file( $file, $attachment_id ) { * unless we know who the calling process is and we are happy * to copy the file back to the server to be used. * + * @handles get_attached_file + * @handles wp_get_original_image_path + * * @param string $file * @param int $attachment_id * @@ -3404,6 +3418,7 @@ public function plugin_load() { $this->handle_post_request(); $this->http_prepare_download_log(); $this->check_for_gd_imagick(); + $this->check_for_items_table(); do_action( 'as3cf_plugin_load' ); } @@ -4127,10 +4142,11 @@ function is_pro() { * Make admin notice for when object ACL has changed * * @param Media_Library_Item $as3cf_item + * @param string|null $size */ - function make_acl_admin_notice( Media_Library_Item $as3cf_item ) { - $filename = wp_basename( $as3cf_item->path() ); - $acl = $as3cf_item->is_private() ? $this->get_storage_provider()->get_private_acl() : $this->get_storage_provider()->get_default_acl(); + function make_acl_admin_notice( Media_Library_Item $as3cf_item, $size = null ) { + $filename = wp_basename( $as3cf_item->path( $size ) ); + $acl = $as3cf_item->is_private_size( $size ) ? $this->get_storage_provider()->get_private_acl() : $this->get_storage_provider()->get_default_acl(); $acl_name = $this->get_acl_display_name( $acl ); $text = sprintf( __( 'WP Offload Media — The file %s has been given %s permissions in the bucket.', 'amazon-s3-and-cloudfront' ), "{$filename}", "{$acl_name}" ); @@ -4157,6 +4173,44 @@ function check_for_gd_imagick() { } } + /** + * Ensure items table(s) exists in the database + */ + private function check_for_items_table() { + if ( ! $this->is_plugin_setup( true ) ) { + // No notice until plugin is setup + return; + } + + if ( is_multisite() && ! is_network_admin() ) { + return; + } + + $missing_tables = $this->get_db_init_status( false ); + + if ( count( $missing_tables ) !== 0 ) { + $this->notices->add_notice( + sprintf( + __( 'Missing Table — One or more required database tables are missing, please check the Diagnostic Info in the Support tab for details. %s', 'amazon-s3-and-cloudfront' ), + $this->more_info_link( + '/wp-offload-media/doc/missing-table-error-notice', + 'missing-table' + ) + ), + array( + 'custom_id' => 'items_table_error', + 'type' => 'error', + 'dismissible' => false, + 'flash' => false, + 'only_show_to_user' => false, + 'only_show_in_settings' => true, + ) + ); + } else { + $this->notices->remove_notice_by_id( 'items_table_error' ); + } + } + /** * Output image size names and dimensions to a string * @@ -4498,6 +4552,32 @@ function output_diagnostic_info( $escape = true ) { $output .= esc_html( $this->get_setting( 'post_meta_version' ) ); $output .= "\r\n\r\n"; + /* + * Items db tables status + */ + + $db_init_statuses = $this->get_db_init_status( true ); + $missing_tables = $this->get_db_init_status( false ); + + $output .= "Custom tables:\r\n"; + if ( count( $missing_tables ) === 0 ) { + $output .= $db_init_statuses[1]['name'] . ': Ok'; + $output .= "\r\n"; + } else { + // Output the first 5 missing tables + $table_count = 0; + foreach ( $missing_tables as $missing_table ) { + $table_count++; + if ( $table_count > 5 ) { + break; + } + $output .= $missing_table['name'] . ': '; + $output .= $missing_table['status'] ? 'Ok' : 'Missing'; + $output .= "\r\n"; + } + } + $output .= "\r\n"; + $storage_provider = $this->get_storage_provider(); if ( empty( $storage_provider ) ) { @@ -5064,6 +5144,48 @@ public function media_counts( $skip_transient = false, $force = false ) { return $attachment_counts; } + /** + * Check the existence of the items table (as3cf_items). Returns an array with one row per + * possible database prefix (multisite support). + * + * @param bool $all Return all tables or just missing tables. Defaults to all/true. + * @param bool $skip_transient Whether to force database query and skip transient, default false. + * + * @return array + */ + private function get_db_init_status( $all = true, $skip_transient = false ) { + global $wpdb; + + if ( $skip_transient || false === ( $db_init_status = get_site_transient( 'as3cf_db_init_status' ) ) ) { + $table_prefixes = $this->get_all_blog_table_prefixes(); + + $db_init_status = array(); + + foreach ( $table_prefixes as $blog_id => $table_prefix ) { + $table_name = $table_prefix . Item::ITEMS_TABLE; + + $db_init_status[ $blog_id ] = array( + 'name' => $table_name, + 'status' => false, + ); + + if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name ) { + $db_init_status[ $blog_id ]['status'] = true; + } + } + + set_site_transient( 'as3cf_db_init_status', $db_init_status, 5 * MINUTE_IN_SECONDS ); + } + + if ( ! $all ) { + $db_init_status = array_filter( $db_init_status, function ( $table ) { + return false === $table['status']; + } ); + } + + return $db_init_status; + } + /** * Display a notice after either lite or pro plugin has been auto deactivated */ @@ -5110,13 +5232,16 @@ protected function get_utm_source() { /** * More info link. * - * @param string $path - * @param string $utm_content - * @param string $hash + * @param string $path Relative path on DBI site + * @param string $utm_content Optional utm_content value. + * @param string $hash Optional hash anchor value without the '#'. + * @param string $text Optional override of link text. + * @param string $prefix Optional non-linked prefix text. + * @param string $suffix Optional non-linked suffix text. * * @return string */ - public function more_info_link( $path, $utm_content = '', $hash = '' ) { + public function more_info_link( $path, $utm_content = '', $hash = '', $text = '', $prefix = '', $suffix = '' ) { $args = array( 'utm_campaign' => 'support+docs', ); @@ -5125,11 +5250,14 @@ public function more_info_link( $path, $utm_content = '', $hash = '' ) { $args['utm_content'] = $utm_content; } + $text = empty( $text ) ? __( 'More info »', 'amazon-s3-and-cloudfront' ) : $text; + $prefix = empty( $prefix ) ? '' : $prefix; + $suffix = empty( $suffix ) ? '' : $suffix; + $url = $this->dbrains_url( $path, $args, $hash ); - $text = __( 'More info »', 'amazon-s3-and-cloudfront' ); $link = AS3CF_Utils::dbrains_link( $url, $text ); - return sprintf( '%s', $link ); + return sprintf( '%s%s%s', $prefix, $link, $suffix ); } /** @@ -5344,6 +5472,10 @@ public function get_formatted_provider_info( $id ) { * @return array|string */ public function get_media_action_strings( $string = null ) { + $not_verified_value = __( 'No', 'amazon-s3-and-cloudfront' ); + $not_verified_value .= ' '; + $not_verified_value .= $this->more_info_link( '/wp-offload-media/doc/add-metadata-tool/', 'os3+attachment+metabox', 'analyze-and-repair', 'More Info', '(', ')' ); + $strings = apply_filters( 'as3cf_media_action_strings', array( 'provider' => _x( 'Storage Provider', 'Storage provider key name', 'amazon-s3-and-cloudfront' ), 'provider_name' => _x( 'Storage Provider', 'Storage provider name', 'amazon-s3-and-cloudfront' ), @@ -5352,6 +5484,8 @@ public function get_media_action_strings( $string = null ) { 'region' => _x( 'Region', 'Location of bucket', 'amazon-s3-and-cloudfront' ), 'acl' => _x( 'Access', 'Access control list of the file in bucket', 'amazon-s3-and-cloudfront' ), 'url' => __( 'URL', 'amazon-s3-and-cloudfront' ), + 'is_verified' => _x( 'Verified', 'Whether or not metadata has been verified', 'amazon-s3-and-cloudfront' ), + 'not_verified' => $not_verified_value, ) ); if ( ! is_null( $string ) ) { diff --git a/classes/as3cf-filter.php b/classes/as3cf-filter.php index 41e62991..13ae845d 100644 --- a/classes/as3cf-filter.php +++ b/classes/as3cf-filter.php @@ -582,7 +582,7 @@ protected function push_to_url_pairs( &$url_pairs, $attachment_id, $find, &$to_c * * @return null|string */ - protected function get_size_string_from_url( $attachment_id, $url ) { + public function get_size_string_from_url( $attachment_id, $url ) { $meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); if ( empty( $meta['sizes'] ) ) { @@ -848,8 +848,8 @@ protected function should_filter_content() { * * @return string */ - protected function remove_aws_query_strings( $content, $base_url = '' ) { - $pattern = '\?[^\s"<\?]*(?:X-Amz-Algorithm|AWSAccessKeyId)=[^\s"<\?]+'; + public static function remove_aws_query_strings( $content, $base_url = '' ) { + $pattern = '\?[^\s"<\?]*(?:X-Amz-Algorithm|AWSAccessKeyId|Key-Pair-Id)=[^\s"<\?]+'; $group = 0; if ( ! is_string( $content ) ) { diff --git a/classes/as3cf-plugin-compatibility.php b/classes/as3cf-plugin-compatibility.php index 61c6362a..16454811 100644 --- a/classes/as3cf-plugin-compatibility.php +++ b/classes/as3cf-plugin-compatibility.php @@ -46,6 +46,16 @@ class AS3CF_Plugin_Compatibility { */ private $removed_files = array(); + /** + * @var bool + */ + protected $generate_attachment_metadata_done = false; + + /** + * @var bool + */ + protected $wait_for_generate_attachment_metadata = false; + /** * @param Amazon_S3_And_CloudFront $as3cf */ @@ -118,6 +128,7 @@ function compatibility_init_if_setup() { * Regenerate Thumbnails v3+ and other REST-API using plugins that need a local file. */ add_filter( 'rest_dispatch_request', array( $this, 'rest_dispatch_request_copy_back_to_local' ), 10, 4 ); + add_filter( 'as3cf_wait_for_generate_attachment_metadata', array( $this, 'wait_for_generate_attachment_metadata' ) ); /* * WP-CLI Compatibility @@ -171,6 +182,8 @@ public function enable_get_attached_file_copy_back_to_local() { $this, 'prevent_copy_back_to_local_after_remove', ), 10, 4 ); + + add_filter( 'wp_generate_attachment_metadata', array( $this, 'wp_generate_attachment_metadata' ) ); } /** @@ -219,6 +232,37 @@ function is_ajax() { return false; } + /** + * Handler for wp_generate_attachment_metadata. Updates class + * member variable when the filter has fired. + * + * @handles wp_generate_attachment_metadata + * + * @param $metadata + * + * @return mixed + */ + public function wp_generate_attachment_metadata( $metadata ) { + $this->generate_attachment_metadata_done = true; + return $metadata; + } + + /** + * Are we waiting for the wp_generate_attachment_metadata filter and + * if so, has it run yet? + * + * @handles as3cf_wait_for_generate_attachment_metadata + * + * @return bool + */ + public function wait_for_generate_attachment_metadata() { + if ( ! $this->wait_for_generate_attachment_metadata ) { + return false; + } + + return ! $this->generate_attachment_metadata_done; + } + /** * Check the current request is a specific one based on action and * optional context @@ -983,6 +1027,8 @@ public function rest_dispatch_request_copy_back_to_local( $dispatch_result, $req foreach ( $routes as $match_route ) { if ( preg_match( '@' . $match_route . '@i', $route ) ) { $this->enable_get_attached_file_copy_back_to_local(); + $this->wait_for_generate_attachment_metadata = true; + $this->generate_attachment_metadata_done = false; break; } } diff --git a/classes/as3cf-utils.php b/classes/as3cf-utils.php index abd06766..ad0f8b0a 100644 --- a/classes/as3cf-utils.php +++ b/classes/as3cf-utils.php @@ -111,6 +111,21 @@ public static function remove_size_from_filename( $url, $remove_extension = fals return $url; } + /** + * Is the given size recognized as the full sized image? + * + * @param string|null $size + * + * @return bool + */ + public static function is_full_size( $size ) { + if ( empty( $size ) || in_array( $size, array( 'full', 'original' ) ) ) { + return true; + } + + return false; + } + /** * Reduce the given URL down to the simplest version of itself. * diff --git a/classes/filters/as3cf-local-to-s3.php b/classes/filters/as3cf-local-to-s3.php index 609e3cc0..6aa83366 100644 --- a/classes/filters/as3cf-local-to-s3.php +++ b/classes/filters/as3cf-local-to-s3.php @@ -202,7 +202,7 @@ protected function get_base_url( $attachment_id ) { * * @return bool|int */ - protected function get_attachment_id_from_url( $url ) { + public function get_attachment_id_from_url( $url ) { $results = $this->get_attachment_ids_from_urls( array( $url ) ); if ( empty( $results ) ) { diff --git a/classes/items/item.php b/classes/items/item.php index 3f7a6c26..cff0576f 100644 --- a/classes/items/item.php +++ b/classes/items/item.php @@ -8,6 +8,10 @@ abstract class Item { const ITEMS_TABLE = 'as3cf_items'; + const ORIGINATORS = array( + 'standard' => 0, + 'metadata-tool' => 1, + ); protected static $source_type = 'media-library'; protected static $source_table = 'posts'; @@ -41,6 +45,8 @@ abstract class Item { private $source_path; private $original_source_path; private $extra_info; + private $originator; + private $is_verified; /** * Item constructor. @@ -54,9 +60,24 @@ abstract class Item { * @param string $source_path Path that source uses, could be relative or absolute depending on source. * @param string $original_filename An optional filename with no path that was previously used for the item. * @param array $extra_info An optional array of extra data specific to the source type. - * @param null $id Optional Item record ID. + * @param int $id Optional Item record ID. + * @param int $originator Optional originator of record from ORIGINATORS const. + * @param bool $is_verified Optional flag as to whether Item's objects are known to exist. */ - public function __construct( $provider, $region, $bucket, $path, $is_private, $source_id, $source_path, $original_filename = null, $extra_info = array(), $id = null ) { + public function __construct( + $provider, + $region, + $bucket, + $path, + $is_private, + $source_id, + $source_path, + $original_filename = null, + $extra_info = array(), + $id = null, + $originator = 0, + $is_verified = true + ) { $this->provider = $provider; $this->region = $region; $this->bucket = $bucket; @@ -65,6 +86,8 @@ public function __construct( $provider, $region, $bucket, $path, $is_private, $s $this->source_id = $source_id; $this->source_path = $source_path; $this->extra_info = serialize( $extra_info ); + $this->originator = $originator; + $this->is_verified = $is_verified; if ( empty( $original_filename ) ) { $this->original_path = $path; @@ -394,7 +417,7 @@ private static function install_table( $table_name ) { $sql = " CREATE TABLE {$table_name} ( - id BIGINT(20) NOT NULL AUTO_INCREMENT, + id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, provider VARCHAR(18) NOT NULL, region VARCHAR(255) NOT NULL, bucket VARCHAR(255) NOT NULL, @@ -402,17 +425,20 @@ private static function install_table( $table_name ) { original_path VARCHAR(1024) NOT NULL, is_private BOOLEAN NOT NULL DEFAULT 0, source_type VARCHAR(18) NOT NULL, - source_id BIGINT(20) NOT NULL, + source_id BIGINT(20) UNSIGNED NOT NULL, source_path VARCHAR(1024) NOT NULL, original_source_path VARCHAR(1024) NOT NULL, extra_info LONGTEXT, + originator TINYINT UNSIGNED NOT NULL DEFAULT 0, + is_verified BOOLEAN NOT NULL DEFAULT 1, PRIMARY KEY (id), UNIQUE KEY uidx_path (path(190), id), UNIQUE KEY uidx_original_path (original_path(190), id), UNIQUE KEY uidx_source_path (source_path(190), id), UNIQUE KEY uidx_original_source_path (original_source_path(190), id), UNIQUE KEY uidx_source (source_type, source_id), - UNIQUE KEY uidx_provider_bucket (provider, bucket(190), id) + UNIQUE KEY uidx_provider_bucket (provider, bucket(190), id), + UNIQUE KEY uidx_is_verified_originator (is_verified, originator, id) ) $charset_collate; "; dbDelta( $sql ); @@ -438,6 +464,8 @@ public function key_values( $include_id = false ) { 'source_path' => $this->source_path, 'original_source_path' => $this->original_source_path, 'extra_info' => $this->extra_info, + 'originator' => $this->originator, + 'is_verified' => $this->is_verified, ); if ( $include_id && ! empty( $this->id ) ) { @@ -491,6 +519,8 @@ private function key_formats( $include_id = false ) { 'source_path' => '%s', 'original_source_path' => '%s', 'extra_info' => '%s', + 'originator' => '%d', + 'is_verified' => '%d', ); if ( $include_id && ! empty( $this->id ) ) { @@ -600,7 +630,9 @@ protected static function create( $object, $add_to_object_cache = false ) { $object->source_path, wp_basename( $object->original_source_path ), $extra_info, - $object->id + $object->id, + $object->originator, + $object->is_verified ); if ( $add_to_object_cache ) { @@ -743,6 +775,15 @@ public function is_private() { return (bool) $this->is_private; } + /** + * Setter for item's is_private value + * + * @param bool $private + */ + public function set_is_private( $private ) { + $this->is_private = (bool) $private; + } + /** * Getter for item's source_id value. * @@ -779,6 +820,42 @@ public function extra_info() { return unserialize( $this->extra_info ); } + /** + * Setter for extra_info value + * + * @param array $extra_info + */ + protected function set_extra_info( $extra_info ) { + $this->extra_info = serialize( $extra_info ); + } + + /** + * Getter for item's originator value. + * + * @return integer + */ + public function originator() { + return $this->originator; + } + + /** + * Getter for item's is_verified value. + * + * @return bool + */ + public function is_verified() { + return (bool) $this->is_verified; + } + + /** + * Setter for item's is_verified value + * + * @param bool $is_verified + */ + public function set_is_verified( $is_verified ) { + $this->is_verified = (bool) $is_verified; + } + /** * Get normalized object path dir. * @@ -890,7 +967,7 @@ public static function get_source_id_by_remote_url( $url ) { } // Regardless of whether 1 or many items found, must validate match. - $path = ltrim( $parts['path'], '/' ); + $path = AS3CF_Utils::decode_filename_in_path( ltrim( $parts['path'], '/' ) ); foreach ( $results as $result ) { $as3cf_item = static::create( $result ); @@ -924,10 +1001,12 @@ public static function get_source_id_by_remote_url( $url ) { * @param integer $upper_bound Returned source_ids should be lower than this, use null/0 for no upper bound. * @param integer $limit Maximum number of source_ids to return. Required if not counting. * @param bool $count Just return a count of matching source_ids? Negates $limit, default false. + * @param int $originator Optionally restrict to only records with given originator type from ORIGINATORS const. + * @param bool $is_verified Optionally restrict to only records that either are or are not verified. * * @return array|int */ - public static function get_source_ids( $upper_bound, $limit, $count = false ) { + public static function get_source_ids( $upper_bound, $limit, $count = false, $originator = null, $is_verified = null ) { global $wpdb; $args = array( static::$source_type ); @@ -945,6 +1024,30 @@ public static function get_source_ids( $upper_bound, $limit, $count = false ) { $args[] = $upper_bound; } + // If an originator type given, check that it is valid before continuing and using. + if ( null !== $originator ) { + if ( is_int( $originator ) && in_array( $originator, self::ORIGINATORS ) ) { + $sql .= ' AND originator = %d'; + $args[] = $originator; + } else { + \AS3CF_Error::log( __METHOD__ . ' called with invalid originator: ' . $originator ); + + return $count ? 0 : array(); + } + } + + // If an is_verified value given, check that it is valid before continuing and using. + if ( null !== $is_verified ) { + if ( is_bool( $is_verified ) ) { + $sql .= ' AND is_verified = %d'; + $args[] = (int) $is_verified; + } else { + \AS3CF_Error::log( __METHOD__ . ' called with invalid is_verified: ' . $is_verified ); + + return $count ? 0 : array(); + } + } + if ( ! $count ) { $sql .= ' ORDER BY source_id DESC LIMIT %d'; $args[] = $limit; diff --git a/classes/items/media-library-item.php b/classes/items/media-library-item.php index fb601ab4..78354791 100644 --- a/classes/items/media-library-item.php +++ b/classes/items/media-library-item.php @@ -27,9 +27,24 @@ class Media_Library_Item extends Item { * 'private_prefix' => 'private/' * For backwards compatibility, if a simple array is supplied it is treated as * private thumbnail sizes that should be private objects in the bucket. - * @param null $id Optional Item record ID. + * @param int $id Optional Item record ID. + * @param int $originator Optional originator of record from ORIGINATORS const. + * @param bool $is_verified Optional flag as to whether Item's objects are known to exist. */ - public function __construct( $provider, $region, $bucket, $path, $is_private, $source_id, $source_path, $original_filename = null, $extra_info = array(), $id = null ) { + public function __construct( + $provider, + $region, + $bucket, + $path, + $is_private, + $source_id, + $source_path, + $original_filename = null, + $extra_info = array(), + $id = null, + $originator = 0, + $is_verified = true + ) { // For Media Library items, the source path should be relative to the Media Library's uploads directory. $uploads = wp_upload_dir(); @@ -60,7 +75,130 @@ public function __construct( $provider, $region, $bucket, $path, $is_private, $s 'private_prefix' => $private_prefix, ); - parent::__construct( $provider, $region, $bucket, $path, $is_private, $source_id, $source_path, $original_filename, $extra_info, $id ); + parent::__construct( $provider, $region, $bucket, $path, $is_private, $source_id, $source_path, $original_filename, $extra_info, $id, $originator, $is_verified ); + } + + /** + * Get a new Media_Library_Item with all data derived from attachment data and current settings. + * + * @param int $attachment_id Attachment ID to construct record from. + * @param bool $object_versioning_allowed Can an Object Versioning string be appended if setting turned on? Default true. + * @param int $originator Originator of new record. Optional, default standard (0). + * + * @return Media_Library_Item|WP_Error + */ + public static function create_from_attachment( $attachment_id, $object_versioning_allowed = true, $originator = 0 ) { + /** @var Amazon_S3_And_CloudFront $as3cf */ + global $as3cf; + + if ( empty( $attachment_id ) ) { + return new WP_Error( + 'exception', + __( 'Empty Attachment ID passed to ' . __FUNCTION__, 'amazon-s3-and-cloudfront' ) + ); + } + + $object_versioning_allowed = empty( $object_versioning_allowed ) ? false : true; + + if ( ! in_array( $originator, self::ORIGINATORS ) ) { + return new WP_Error( + 'exception', + __( 'Invalid Originator passed to ' . __FUNCTION__, 'amazon-s3-and-cloudfront' ) + ); + } + + // If we ever expand originators to include more pre-verified versions, this will need changing. + $is_verified = 0 === $originator; + + /* + * Provider basics. + */ + + $provider = $as3cf->get_storage_provider()->get_provider_key_name(); + $region = $as3cf->get_setting( 'region' ); + if ( is_wp_error( $region ) ) { + $region = ''; + } + $bucket = $as3cf->get_setting( 'bucket' ); + + /* + * Derive local and remote paths. + */ + + // Verify that get_attached_file will not blow up as it does not check the data it manipulates. + $attached_file_meta = get_post_meta( $attachment_id, '_wp_attached_file', true ); + if ( ! is_string( $attached_file_meta ) ) { + return new WP_Error( + 'exception', + sprintf( __( 'Media Library item with ID %d has damaged meta data', 'amazon-s3-and-cloudfront' ), $attachment_id ) + ); + } + unset( $attached_file_meta ); + + $source_path = get_attached_file( $attachment_id, true ); + + // Check for valid "full" file path otherwise we'll not be able to create offload path or download in the future. + if ( empty( $source_path ) ) { + return new WP_Error( + 'exception', + sprintf( __( 'Media Library item with ID %d does not have a valid file path', 'amazon-s3-and-cloudfront' ), $attachment_id ) + ); + } + + $attachment_metadata = wp_get_attachment_metadata( $attachment_id, true ); + + if ( is_wp_error( $attachment_metadata ) ) { + return $attachment_metadata; + } + + $prefix = $as3cf->get_new_attachment_prefix( $attachment_id, $attachment_metadata, $object_versioning_allowed ); + $path = $prefix . wp_basename( $source_path ); + + // There may be an original image that can override the default original filename. + $original_filename = empty( $attachment_metadata['original_image'] ) ? null : $attachment_metadata['original_image']; + + /* + * Private file handling. + */ + + $acl = apply_filters( 'as3cf_upload_acl', $as3cf->get_storage_provider()->get_default_acl(), $attachment_metadata, $attachment_id ); + $is_private = ! empty( $acl ) && $as3cf->get_storage_provider()->get_private_acl() === $acl; + + // Maybe set private sizes and private prefix. + $extra_info = array( + 'private_sizes' => array(), + 'private_prefix' => '', + ); + + $file_paths = AS3CF_Utils::get_attachment_file_paths( $attachment_id, false, $attachment_metadata ); + $file_paths = array_diff( $file_paths, array( $source_path ) ); + + foreach ( $file_paths as $size => $size_file_path ) { + $acl = apply_filters( 'as3cf_upload_acl_sizes', $as3cf->get_storage_provider()->get_default_acl(), $size, $attachment_id, $attachment_metadata ); + + if ( ! empty( $acl ) && $as3cf->get_storage_provider()->get_private_acl() === $acl ) { + $extra_info['private_sizes'][] = $size; + } + } + + if ( $as3cf->private_prefix_enabled() ) { + $extra_info['private_prefix'] = AS3CF_Utils::trailingslash_prefix( $as3cf->get_setting( 'signed-urls-object-prefix', '' ) ); + } + + return new self( + $provider, + $region, + $bucket, + $path, + $is_private, + $attachment_id, + $source_path, + $original_filename, + $extra_info, + null, + $originator, + $is_verified + ); } /** @@ -141,6 +279,28 @@ protected function source_paths( $id ) { return $paths; } + /** + * Getter for item's path value, optionally for a specific size + * + * @param null|string $size + * + * @return string + */ + public function path( $size = null ) { + $path = parent::path(); + + if ( empty( $size ) ) { + return $path; + } + + $meta = get_post_meta( $this->source_id(), '_wp_attachment_metadata', true ); + if ( ! empty( $meta['sizes'][ $size ]['file'] ) ) { + $path = str_replace( wp_basename( $path ), $meta['sizes'][ $size ]['file'], $path ); + } + + return $path; + } + /** * Get the array of thumbnail sizes that are private in the bucket. * @@ -165,6 +325,30 @@ public function private_sizes() { return array(); } + /** + * Set the private status for a specific size. + * + * @param $size + * @param $private + */ + public function set_private_size( $size, $private ) { + if ( empty( $size ) || AS3CF_Utils::is_full_size( $size ) ) { + return; + } + + $extra_info = $this->extra_info(); + $private_sizes = $this->private_sizes(); + if ( $private && ! in_array( $size, $private_sizes, true ) ) { + $private_sizes[] = $size; + } + if ( ! $private && in_array( $size, $private_sizes, true ) ) { + $private_sizes = array_diff( $private_sizes, array( $size ) ); + } + $extra_info['private_sizes'] = $private_sizes; + + $this->set_extra_info( $extra_info ); + } + /** * Get the private status for a specific size. * @@ -173,7 +357,7 @@ public function private_sizes() { * @return bool */ public function is_private_size( $size ) { - if ( empty( $size ) || in_array( $size, array( 'full', 'original' ) ) ) { + if ( AS3CF_Utils::is_full_size( $size ) ) { return $this->is_private(); } diff --git a/languages/amazon-s3-and-cloudfront-en.pot b/languages/amazon-s3-and-cloudfront-en.pot index 27a91e02..450a764d 100644 --- a/languages/amazon-s3-and-cloudfront-en.pot +++ b/languages/amazon-s3-and-cloudfront-en.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: amazon-s3-and-cloudfront\n" "Report-Msgid-Bugs-To: nom@deliciousbrains.com\n" -"POT-Creation-Date: 2020-09-08 12:17+0100\n" +"POT-Creation-Date: 2020-11-11 11:45+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,17 +17,17 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: classes/amazon-s3-and-cloudfront.php:165 #: classes/amazon-s3-and-cloudfront.php:166 +#: classes/amazon-s3-and-cloudfront.php:167 msgid "Offload Media Lite" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:400 -#: classes/amazon-s3-and-cloudfront.php:417 +#: classes/amazon-s3-and-cloudfront.php:402 +#: classes/amazon-s3-and-cloudfront.php:419 msgid "Unknown" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:489 +#: classes/amazon-s3-and-cloudfront.php:491 #: view/bucket-select.php:87 #: view/delivery-provider-select.php:129 #: view/delivery-provider-select.php:149 @@ -38,221 +38,223 @@ msgstr "" msgid "defined in wp-config.php" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1413 +#: classes/amazon-s3-and-cloudfront.php:1422 #, php-format msgid "Media Library item ID %d. Provided path is not a string" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1421 +#: classes/amazon-s3-and-cloudfront.php:1430 +#: classes/items/media-library-item.php:133 #, php-format msgid "Media Library item with ID %d has damaged meta data" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1432 +#: classes/amazon-s3-and-cloudfront.php:1441 +#: classes/items/media-library-item.php:144 #, php-format msgid "Media Library item with ID %d does not have a valid file path" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1483 -#: classes/amazon-s3-and-cloudfront.php:1708 +#: classes/amazon-s3-and-cloudfront.php:1492 +#: classes/amazon-s3-and-cloudfront.php:1717 #, php-format msgid "File %s does not exist" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1512 +#: classes/amazon-s3-and-cloudfront.php:1521 #, php-format msgid "Mime type %s is not allowed" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1523 +#: classes/amazon-s3-and-cloudfront.php:1532 msgid "Already offloaded to a different provider" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:1631 -#: classes/amazon-s3-and-cloudfront.php:1722 +#: classes/amazon-s3-and-cloudfront.php:1640 +#: classes/amazon-s3-and-cloudfront.php:1731 #, php-format msgid "Error offloading %s to provider: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2840 +#: classes/amazon-s3-and-cloudfront.php:2854 msgid "This action can only be performed through an admin screen." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2842 +#: classes/amazon-s3-and-cloudfront.php:2856 msgid "Cheatin’ eh?" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2844 +#: classes/amazon-s3-and-cloudfront.php:2858 msgid "You do not have sufficient permissions to access this page." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3168 +#: classes/amazon-s3-and-cloudfront.php:3182 msgid "Error Getting Bucket Region" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3169 +#: classes/amazon-s3-and-cloudfront.php:3183 #, php-format msgid "There was an error attempting to get the region of the bucket %s: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3272 +#: classes/amazon-s3-and-cloudfront.php:3286 msgid "" "This is a test file to check if the user has write permission to the bucket. " "Delete me if found." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3278 +#: classes/amazon-s3-and-cloudfront.php:3292 #, php-format msgid "" "There was an error attempting to check the permissions of the bucket %s: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3374 +#: classes/amazon-s3-and-cloudfront.php:3388 msgid "Error creating bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3375 +#: classes/amazon-s3-and-cloudfront.php:3389 msgid "Bucket name too short." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3376 +#: classes/amazon-s3-and-cloudfront.php:3390 msgid "Bucket name too long." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3377 +#: classes/amazon-s3-and-cloudfront.php:3391 msgid "" "Invalid character. Bucket names can contain lowercase letters, numbers, " "periods and hyphens." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3378 +#: classes/amazon-s3-and-cloudfront.php:3392 msgid "Error saving bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3379 +#: classes/amazon-s3-and-cloudfront.php:3393 msgid "Error fetching buckets" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3380 +#: classes/amazon-s3-and-cloudfront.php:3394 msgid "Error getting URL preview: " msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3381 +#: classes/amazon-s3-and-cloudfront.php:3395 msgid "The changes you made will be lost if you navigate away from this page" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3382 +#: classes/amazon-s3-and-cloudfront.php:3396 msgid "Getting diagnostic info..." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3383 +#: classes/amazon-s3-and-cloudfront.php:3397 msgid "Error getting diagnostic info: " msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3384 +#: classes/amazon-s3-and-cloudfront.php:3398 msgctxt "placeholder for hidden access key, 39 char max" msgid "-- not shown --" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3386 -#: classes/amazon-s3-and-cloudfront.php:5643 +#: classes/amazon-s3-and-cloudfront.php:3400 +#: classes/amazon-s3-and-cloudfront.php:5777 msgid "Settings saved." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3507 +#: classes/amazon-s3-and-cloudfront.php:3522 msgid "Cheatin' eh?" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3580 +#: classes/amazon-s3-and-cloudfront.php:3595 #, php-format msgid "Could not set new Delivery Provider: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3655 -#: classes/amazon-s3-and-cloudfront.php:3785 +#: classes/amazon-s3-and-cloudfront.php:3670 +#: classes/amazon-s3-and-cloudfront.php:3800 msgid "No bucket name provided." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3664 +#: classes/amazon-s3-and-cloudfront.php:3679 msgid "Bucket name not valid." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3677 +#: classes/amazon-s3-and-cloudfront.php:3692 msgid "No region provided." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3754 +#: classes/amazon-s3-and-cloudfront.php:3769 #, php-format msgctxt "Trying to change public access setting for given provider's bucket." msgid "Can't change Block All Public Access setting for %s buckets." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3763 +#: classes/amazon-s3-and-cloudfront.php:3778 msgid "No block public access setting provided." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3776 +#: classes/amazon-s3-and-cloudfront.php:3791 msgid "Storage Provider not configured with access credentials." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3803 +#: classes/amazon-s3-and-cloudfront.php:3818 msgid "Could not change Block All Public Access status for bucket." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3820 +#: classes/amazon-s3-and-cloudfront.php:3835 msgid "" "Failed to Enable Block All Public Access — We could " "not enable Block All Public Access. You will need to log in to the AWS " "Console and do it manually." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3822 +#: classes/amazon-s3-and-cloudfront.php:3837 msgid "" "Failed to Disable Block All Public Access — We could " "not disable Block All Public Access. You will need to log in to the AWS " "Console and do it manually." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3857 +#: classes/amazon-s3-and-cloudfront.php:3872 #: view/provider-select.php:329 msgctxt "placeholder for hidden secret access key, 39 char max" msgid "-- not shown --" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3880 +#: classes/amazon-s3-and-cloudfront.php:3895 msgid "Key File not valid JSON." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3892 -#: classes/amazon-s3-and-cloudfront.php:3906 -#: classes/amazon-s3-and-cloudfront.php:3915 +#: classes/amazon-s3-and-cloudfront.php:3907 +#: classes/amazon-s3-and-cloudfront.php:3921 +#: classes/amazon-s3-and-cloudfront.php:3930 msgctxt "missing form field" msgid " not provided." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3958 +#: classes/amazon-s3-and-cloudfront.php:3973 msgctxt "Show the media library tab" msgid "Media Library" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3959 +#: classes/amazon-s3-and-cloudfront.php:3974 msgctxt "Show the addons tab" msgid "Addons" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3960 +#: classes/amazon-s3-and-cloudfront.php:3975 msgctxt "Show the support tab" msgid "Support" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4135 +#: classes/amazon-s3-and-cloudfront.php:4151 #, php-format msgid "" "WP Offload Media — The file %s has been given %s " "permissions in the bucket." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4154 +#: classes/amazon-s3-and-cloudfront.php:4170 msgid "" "WP Offload Media Requirement Missing — Looks like you " "don't have an image manipulation library installed on this server and " @@ -260,18 +262,26 @@ msgid "" "Please setup GD or ImageMagick." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4936 +#: classes/amazon-s3-and-cloudfront.php:4194 +#, php-format +msgid "" +"Missing Table — One or more required database tables " +"are missing, please check the Diagnostic Info in the Support tab for " +"details. %s" +msgstr "" + +#: classes/amazon-s3-and-cloudfront.php:5016 #, php-format msgid "" "Define your access keys to enable write access to the " "bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4943 +#: classes/amazon-s3-and-cloudfront.php:5023 msgid "Quick Start Guide" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4945 +#: classes/amazon-s3-and-cloudfront.php:5025 #, php-format msgid "" "Looks like we don't have write access to this bucket. It's likely that the " @@ -280,7 +290,7 @@ msgid "" "correctly." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4947 +#: classes/amazon-s3-and-cloudfront.php:5027 #, php-format msgid "" "Looks like we don't have access to the buckets. It's likely that the user " @@ -288,39 +298,39 @@ msgid "" "Please see our %s for instructions on setting up permissions correctly." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5073 +#: classes/amazon-s3-and-cloudfront.php:5195 msgid "WP Offload Media Activation" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5074 +#: classes/amazon-s3-and-cloudfront.php:5196 msgid "" "WP Offload Media Lite and WP Offload Media cannot both be active. We've " "automatically deactivated WP Offload Media Lite." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5076 +#: classes/amazon-s3-and-cloudfront.php:5198 msgid "WP Offload Media Lite Activation" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5077 +#: classes/amazon-s3-and-cloudfront.php:5199 msgid "" "WP Offload Media Lite and WP Offload Media cannot both be active. We've " "automatically deactivated WP Offload Media." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5129 +#: classes/amazon-s3-and-cloudfront.php:5253 msgid "More info »" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5205 +#: classes/amazon-s3-and-cloudfront.php:5333 msgid "this doc" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5207 +#: classes/amazon-s3-and-cloudfront.php:5335 msgid "WP Offload Media Feature Removed" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5208 +#: classes/amazon-s3-and-cloudfront.php:5336 #, php-format msgid "" "You had the \"Always non-SSL\" option selected in your settings, but we've " @@ -331,59 +341,68 @@ msgid "" "to the old behavior." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5243 +#: classes/amazon-s3-and-cloudfront.php:5371 msgid "Offload" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5348 +#: classes/amazon-s3-and-cloudfront.php:5475 +msgid "No" +msgstr "" + +#: classes/amazon-s3-and-cloudfront.php:5480 msgctxt "Storage provider key name" msgid "Storage Provider" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5349 +#: classes/amazon-s3-and-cloudfront.php:5481 msgctxt "Storage provider name" msgid "Storage Provider" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5350 +#: classes/amazon-s3-and-cloudfront.php:5482 msgctxt "Bucket name" msgid "Bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5351 +#: classes/amazon-s3-and-cloudfront.php:5483 msgctxt "Path to file in bucket" msgid "Path" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5352 +#: classes/amazon-s3-and-cloudfront.php:5484 msgctxt "Location of bucket" msgid "Region" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5353 +#: classes/amazon-s3-and-cloudfront.php:5485 msgctxt "Access control list of the file in bucket" msgid "Access" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5354 +#: classes/amazon-s3-and-cloudfront.php:5486 msgid "URL" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5606 +#: classes/amazon-s3-and-cloudfront.php:5487 +msgctxt "Whether or not metadata has been verified" +msgid "Verified" +msgstr "" + +#: classes/amazon-s3-and-cloudfront.php:5740 msgid "Assets Pull" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5607 +#: classes/amazon-s3-and-cloudfront.php:5741 msgid "" "An addon for WP Offload Media to serve your site's JS, CSS, and other " "enqueued assets from Amazon CloudFront or another CDN." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5611 +#: classes/amazon-s3-and-cloudfront.php:5745 msgid "Feature" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5657 +#: classes/amazon-s3-and-cloudfront.php:5791 #, php-format msgid "" "Amazon Web Services Plugin No Longer Required — As of " @@ -394,7 +413,7 @@ msgid "" "plugin, it should be safe to deactivate and delete it. %2$s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:5689 +#: classes/amazon-s3-and-cloudfront.php:5823 #, php-format msgid "" "WP Offload Media Settings Moved — You now define your " @@ -527,26 +546,34 @@ msgstr "" msgid "Settings" msgstr "" -#: classes/as3cf-plugin-compatibility.php:595 +#: classes/as3cf-plugin-compatibility.php:639 #, php-format msgid "The local directory %s does not exist and could not be created." msgstr "" -#: classes/as3cf-plugin-compatibility.php:596 -#: classes/as3cf-plugin-compatibility.php:608 +#: classes/as3cf-plugin-compatibility.php:640 +#: classes/as3cf-plugin-compatibility.php:652 #: classes/upgrades/upgrade-meta-wp-error.php:81 #, php-format msgid "" "There was an error attempting to download the file %s from the bucket: %s" msgstr "" -#: classes/as3cf-plugin-compatibility.php:933 +#: classes/as3cf-plugin-compatibility.php:977 #, php-format msgid "" "Warning: This site is using PHP %1$s, in a future update WP " "Offload Media will require PHP %2$s or later. %3$s" msgstr "" +#: classes/items/media-library-item.php:97 +msgid "Empty Attachment ID passed to " +msgstr "" + +#: classes/items/media-library-item.php:106 +msgid "Invalid Originator passed to " +msgstr "" + #: classes/providers/delivery/another-cdn.php:47 #: classes/providers/delivery/digitalocean-spaces-cdn.php:83 msgid "Fast, No Private Media" @@ -764,11 +791,11 @@ msgstr "" msgid "This item has not been offloaded yet." msgstr "" -#: view/attachment-metabox.php:49 +#: view/attachment-metabox.php:56 msgid "File does not exist on server" msgstr "" -#: view/attachment-metabox.php:57 +#: view/attachment-metabox.php:64 msgid "File exists on server" msgstr "" diff --git a/readme.txt b/readme.txt index 3ef49ec8..2183dc5a 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: uploads, amazon, s3, amazon s3, digitalocean, digitalocean spaces, google Requires at least: 4.9 Tested up to: 5.5 Requires PHP: 5.5 -Stable tag: 2.4.4 +Stable tag: 2.5 License: GPLv3 Copies files to Amazon S3, DigitalOcean Spaces or Google Cloud Storage as they are uploaded to the Media Library. Optionally configure Amazon CloudFront or another CDN for faster delivery. @@ -81,6 +81,13 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin == Changelog == += WP Offload Media Lite 2.5 - 2020-11-11 = +* [Release Summary Blog Post](https://deliciousbrains.com/wp-offload-media-2-5-released/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting) +* Improvement: [Error notice shown](https://deliciousbrains.com/wp-offload-media/doc/missing-table-error-notice/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting) when plugin's required custom table(s) missing +* Improvement: [Diagnostic Info](https://deliciousbrains.com/wp-offload-media/doc/missing-table-error-notice/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting#diagnostic-info) shows status of plugin's required custom tables +* Bug fix: wp_get_original_image_path function does not return provider URL when local files removed +* Bug fix: File missing notices recorded in debug.log when regenerating thumbnails and Remove Files From Server turned on + = WP Offload Media Lite 2.4.4 - 2020-09-08 = * Improvement: Updated AWS PHP SDK to v3.151.6 * Bug fix: Files for duplicate thumbnail sizes not removed from server after initial offload diff --git a/view/attachment-metabox.php b/view/attachment-metabox.php index 040b7a56..bb92e32e 100644 --- a/view/attachment-metabox.php +++ b/view/attachment-metabox.php @@ -44,6 +44,13 @@ get_acl_value_string( $provider_object['acl'], $post->ID ); ?>
+ +
+
get_media_action_strings( 'is_verified' ); ?>:
+
get_media_action_strings( 'not_verified' ); ?>
+
+
diff --git a/wordpress-s3.php b/wordpress-s3.php index 4dad4200..0faad4a3 100644 --- a/wordpress-s3.php +++ b/wordpress-s3.php @@ -4,7 +4,7 @@ Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/ Description: Automatically copies media uploads to Amazon S3, DigitalOcean Spaces or Google Cloud Storage for storage and delivery. Optionally configure Amazon CloudFront or another CDN for even faster delivery. Author: Delicious Brains -Version: 2.4.4 +Version: 2.5 Author URI: https://deliciousbrains.com/ Network: True Text Domain: amazon-s3-and-cloudfront @@ -26,7 +26,7 @@ // Then completely rewritten. */ -$GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '2.4.4'; +$GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '2.5'; require_once dirname( __FILE__ ) . '/classes/as3cf-compatibility-check.php';